From 4805ac0e363d3bf144334931fa63e6fecd9cf254 Mon Sep 17 00:00:00 2001 From: Nate Otto Date: Wed, 14 Oct 2026 16:54:34 -0700 Subject: [PATCH] docs: remove internal planning and downstream-app references Source comments ship in the published JavaScript and type declarations, so shorthand from a process readers cannot see showed up in consumers' editor hovers. Every such comment is rewritten as a statement of the behaviour and why it holds: - plan labels, numbered phases, question numbers, dated plan names and links into the untracked docs plans folder are gone, along with the ignore rule for that folder; - named downstream apps no longer justify library behaviour; the reason is stated directly instead; - the prompt-ready appendices in the API guides are removed, with them the mentions in the README, the architecture doc and the unreleased 2.0.0 changelog section; the verification-results appendix duplicated the UI rendering recipe it followed; - comments no longer narrate earlier states of the code; - broken file references are fixed: the deleted defaults module (now default-suites.ts), a non-existent constants module cited for the bitstring error strings, and a comment naming the result-ref check wrongly and describing a refactor the OpenBadges Zod module records as rejected. The envelope schema header now describes the schema as it is, composed top-to-bottom, rather than as a partial state. The rationale for the status suite owning status checking outright now sits in the architecture doc's hexagonal section as well as inline. SuitePhase and its lowercase values are untouched, as are the README migration section and released changelog entries. --- .gitignore | 3 - CHANGELOG.md | 3 +- README.md | 12 +- docs/api/timing.md | 80 +----- docs/api/verification-results.md | 230 +----------------- docs/architecture.md | 9 +- src/crypto-dispatch.ts | 6 +- src/fold-results.ts | 6 +- src/openbadges/achieved-level-check.ts | 2 +- src/openbadges/index.ts | 3 +- src/openbadges/openbadges-suite.ts | 6 +- src/openbadges/openbadges-zod.ts | 17 +- src/openbadges/problem-types.ts | 6 +- src/openbadges/recognizers.ts | 2 - src/openbadges/schemas/classes-v3p0.ts | 27 +- .../schemas/openbadge-credential-v3p0.ts | 17 +- src/problem-types.ts | 8 +- src/services/data-integrity-crypto.ts | 10 +- .../time-service/fake-time-service.ts | 12 +- src/suites/status/bitstring-status-check.ts | 4 +- src/types/check.ts | 2 +- src/types/crypto-service.ts | 2 +- src/types/recognition.ts | 2 - src/types/timing.ts | 7 +- src/util/did-web-driver-with-http-get.ts | 6 +- src/verifier.ts | 12 +- test/factories/services/build-test-context.ts | 11 +- .../obv3p0-openbadge-spec-conforming.ts | 7 +- test/openbadges/openbadges-submodule.spec.ts | 4 +- .../schema/achievement-subject-v3p0.spec.ts | 4 +- .../schema/achievement-v3p0.spec.ts | 10 +- test/openbadges/schema/envelope-v3p0.spec.ts | 12 +- test/openbadges/schema/image-v3p0.spec.ts | 8 +- test/openbadges/schema/profile-v3p0.spec.ts | 10 +- test/openbadges/schema/result-v3p0.spec.ts | 8 +- test/phase-filter.spec.ts | 3 +- test/signed-bytes-preserved.spec.ts | 10 +- test/verifier-cache-sharing.spec.ts | 11 +- test/verifier-recognition.spec.ts | 3 +- test/verify-credential.spec.ts | 14 +- 40 files changed, 142 insertions(+), 467 deletions(-) diff --git a/.gitignore b/.gitignore index 701ea31..5e6819f 100644 --- a/.gitignore +++ b/.gitignore @@ -112,9 +112,6 @@ jspm_packages/ /.vscode .cursor -# local development notes -docs/plans - # Output dist diff --git a/CHANGELOG.md b/CHANGELOG.md index 87bf234..576139d 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -32,8 +32,7 @@ not run after a fatal failure. - Pure `foldCheckResults` helper and `computeId` namespace builder, both exported from the package barrel. - New consumer doc at `docs/api/verification-results.md` covering the result - shape, the `id` namespace, a UI rendering recipe, and a prompt-ready appendix - for downstream UIs. + shape, the `id` namespace, and a UI rendering recipe. - `timing?: boolean` flag on `VerifierConfig`, `VerifyCredentialCall`, and `VerifyPresentationCall`. When true, every `CheckResult`, every `SuiteSummary`, and every top-level diff --git a/README.md b/README.md index 4f66268..e5bd08c 100644 --- a/README.md +++ b/README.md @@ -849,8 +849,8 @@ embeds its suite's phase, so re-tagging a built-in suite would rename every check in it: changing either is a breaking change. See [`docs/api/verification-results.md`](./docs/api/verification-results.md) for -the full reference (phase model, `SuiteSummary` fields, `id` namespace, UI -rendering recipe, and a prompt-ready appendix for downstream UIs). +the full reference (phase model, `SuiteSummary` fields, `id` namespace, and a UI +rendering recipe). ## Capturing timing data @@ -871,7 +871,7 @@ console.log(result.summary[0].timing!.durationMs); Per-check timings ride on every row of `results[]`, and the per-suite rollup lives on `summary[i].timing`. See [`docs/api/timing.md`](./docs/api/timing.md) for the full reference, granularity -table, recipes, and a prompt-ready appendix. +table, and recipes. ## Pluggable clock (`TimeService`) @@ -1044,9 +1044,9 @@ for (const credResult of presResult.credentialResults) { } ``` -This is the recommended advanced integration shape for services like -`dcc-transaction-service` that verify a presentation up front and then inspect -each embedded credential's profile to drive downstream business logic. If +This is the recommended advanced integration shape for a service that verifies +a presentation up front and then inspects each embedded credential's profile to +drive downstream business logic. If performance matters, you're verifying high volumes of credentials, you want to show partial results to a user for verification in progress, or you have specific requirements for the verification process, this advanced integration diff --git a/docs/api/timing.md b/docs/api/timing.md index 19a97b6..7842905 100644 --- a/docs/api/timing.md +++ b/docs/api/timing.md @@ -3,11 +3,8 @@ > Consumer reference for the optional `timing` flag and the > `TaskTiming` data carried on every result when it is enabled. > -> **Audience.** Performance / observability work in -> `dcc-transaction-service`, `skills-verifier`, and any future -> client building on `verifier-core`. The §9 LLM-prompt -> appendix is written to be copy-pasted as the seed of an LLM -> prompt for client-side instrumentation code-gen. +> **Audience.** Performance and observability work in any client +> building on `verifier-core`. ## Table of Contents @@ -19,7 +16,6 @@ 6. [Parse-failure behavior](#parse-failure-behavior) 7. [Recipes](#recipes) 8. [`TimeService` reference](#timeservice-reference) -9. [Prompt-ready appendix](#prompt-ready-appendix) ## Overview @@ -245,75 +241,3 @@ verifier built on a `FakeTimeService` decides both at the pinned moment. Signature clock-skew windows and key rotation belong there too. Read it from `VerificationContext.timeService` inside `execute()`. - -## Prompt-ready appendix - -> Drop this section verbatim into an LLM prompt for client-side -> instrumentation code-gen. - -`@digitalcredentials/verifier-core` v2 emits optional -`TaskTiming` data on every result when constructed with -`timing: true`: - -```ts -import { createVerifier } from '@digitalcredentials/verifier-core'; -const verifier = createVerifier({ timing: true }); -const result = await verifier.verifyPresentation({ presentation }); -``` - -Every result then carries a `timing: TaskTiming` field at three -levels: - -```ts -interface TaskTiming { - startedAt: string; // ISO 8601 wall-clock - endedAt: string; // ISO 8601 wall-clock - durationMs: number; // monotonic, fractional ms - events?: ReadonlyArray; // reserved -} - -// Per-check (every row in results[] when timing: true): -result.results[i].timing -// Per-suite (always present in summary[] when timing: true): -result.summary[i].timing -// Top-level inclusive wrapper: -result.timing -// In verifyPresentation results, each embedded VC also has: -result.credentialResults[i].timing -``` - -Invariants (always hold when `timing: true`): - -1. `endedAt >= startedAt` for every `TaskTiming`. -2. `durationMs >= 0` for every `TaskTiming`. -3. Top-level `result.timing.durationMs >= max(s.timing.durationMs for s in result.summary)`. -4. `verifyPresentation` top-level `result.timing.durationMs >= cr.timing.durationMs` for every `cr in result.credentialResults`. -5. Per-suite `timing.durationMs == sum(child timing.durationMs)`. -6. Per-suite `timing.startedAt == min(child startedAt)`, - per-suite `timing.endedAt == max(child endedAt)`. - -Use `durationMs` for any sort / sum / comparison (it comes -from a monotonic clock); use `startedAt` / `endedAt` only for -log correlation and human-readable timelines. - -A "verification time breakdown" UI from a presentation result: - -```ts -function buildBreakdown(result: PresentationVerificationResult) { - const totalMs = result.timing!.durationMs; - return { - totalMs, - presentationOnlyMs: result.summary - .reduce((a, s) => a + (s.timing?.durationMs ?? 0), 0), - credentials: result.credentialResults.map(cr => ({ - id: cr.verifiableCredential.id, - totalMs: cr.timing!.durationMs, - bySuite: cr.summary.map(s => ({ - suite: s.id, - durationMs: s.timing!.durationMs, - fatalFailureAt: s.fatalFailureAt, - })), - })), - }; -} -``` diff --git a/docs/api/verification-results.md b/docs/api/verification-results.md index 019f444..369c204 100644 --- a/docs/api/verification-results.md +++ b/docs/api/verification-results.md @@ -5,9 +5,8 @@ > `id` namespace, the `SuiteSummary` rollup (new in v2.0.0), and a UI > rendering recipe. > -> **Audience.** UI/integration teams building on top of `verifier-core`, -> primarily `dcc-transaction-service`. The §8 prompt-ready appendix is -> written to be copy-pasted as the seed of an LLM prompt. +> **Audience.** UI and integration teams building on top of +> `verifier-core`. > > **See also.** For per-check / per-suite / per-call timing data > on every result, see [`timing.md`](./timing.md). @@ -23,7 +22,6 @@ 5. [`id` namespace reference](#id-namespace-reference) 6. [UI rendering recipe](#ui-rendering-recipe) 7. [Backwards compatibility](#backwards-compatibility) -8. [Prompt-ready appendix](#prompt-ready-appendix) ## Overview @@ -548,227 +546,3 @@ Lazy-expand them — most users only want the verdict + per-phase status. against the summary rollup, narrowing by `outcome.status`. See §6 step 3 for the canonical pattern. - -## Prompt-ready appendix - -Copy the fenced block below as the seed of an LLM prompt for the -dcc-transaction-service UI implementation. - -> Given the JSON shape and `SuiteSummary` type below, produce a React -> component that renders a presentation verification result. Group -> per-credential summaries by phase -> (`cryptographic` → `trust` → `recognition` → `semantic` → `unknown`). -> Show failure detail rows (from `results[]` filtered by `id` prefix -> **and** `outcome.status === 'failure'`) under the failing phase row, -> lazy-expanded. Colour a suite red only when it has `fatalFailureAt`; -> a suite with failures but no `fatalFailureAt` is a warning (amber), -> because non-fatal failures leave `verified` true. -> -> The result is the return value of -> `verifier.verifyPresentation({ presentation })` from -> `@digitalcredentials/verifier-core` (v2.0.0+). `results[]` carries -> every check — passes, skips with a reason, and rows not run after a -> fatal failure — so a row's `outcome.status` is what distinguishes -> them. -> -> `verified` means "no fatal check failed" (and, for a presentation, -> that every embedded credential is itself verified). The advisory -> (non-fatal) checks are `trust.registry.issuer`, -> `recognition.profile`, `cryptographic.status.list-issuer`, the Open -> Badges checks (the semantic `schema.obv3.*` checks and the Open -> Badges JSON-schema check), and any caller-supplied check without -> `fatal`. A credential whose only failing check is advisory is -> `verified: true` with `status: 'failure'` (or `'mixed'`) and no -> `fatalFailureAt` on that suite — render the failure rows as warnings, -> but do not render the verdict as failed. - ---- - -**JSON example.** - -```jsonc -{ - "verified": false, - "verifiablePresentation": { /* the VP you passed in, unchanged */ }, - "presentationResults": [ - { "id": "cryptographic.core.vp-structure", /* success */ }, - { "id": "cryptographic.proof.signature", /* success */ } - ], - "summary": [ - { "id": "cryptographic.proof", "phase": "cryptographic", - "suite": "proof", "status": "success", - "message": "1 of 1 check passed", - "counts": { "passed": 1, "failed": 0, "skipped": 0 } } - ], - "credentialResults": [ - { - // Only advisory checks failed, so the verdict stays true. - "verified": true, - "verifiableCredential": { /* VC with an unregistered issuer */ }, - "recognizedProfile": "obv3p0.openbadge", - "results": [ - { "id": "cryptographic.core.context-exists", /* success */ }, - { "id": "cryptographic.core.vc-context", /* success */ }, - { "id": "cryptographic.core.vc-structure", /* success */ }, - { "id": "cryptographic.core.credential-id", /* success */ }, - { "id": "cryptographic.core.proof-exists", /* success */ }, - { "id": "recognition.profile", /* success */ }, - { "id": "cryptographic.proof.signature", /* success */ }, - { "id": "trust.registry.issuer", - "suite": "registry", - "outcome": { "status": "failure", "problems": [ - { "type": "https://www.w3.org/TR/vc-data-model#ISSUER_NOT_REGISTERED", - "title": "Issuer Not Registered", - "detail": "Issuer DID not in any registry." } - ], - "payload": { "matches": [], "uncheckedRegistries": [] }}, - "fatal": false } - ], - "summary": [ - { "id": "cryptographic.core", "phase": "cryptographic", - "suite": "core", "status": "success", - "message": "5 of 5 checks passed", - "counts": { "passed": 5, "failed": 0, "skipped": 0 } }, - { "id": "recognition", "phase": "recognition", - "suite": "recognition", "status": "success", - "message": "1 of 1 check passed", - "counts": { "passed": 1, "failed": 0, "skipped": 0 } }, - { "id": "cryptographic.proof", "phase": "cryptographic", - "suite": "proof", "status": "success", - "message": "1 of 1 check passed", - "counts": { "passed": 1, "failed": 0, "skipped": 0 } }, - // Non-fatal failure: status "failure", but no fatalFailureAt - // and the credential is still verified. Render as a warning. - { "id": "trust.registry", "phase": "trust", - "suite": "registry", "status": "failure", - "message": "1 of 1 check failed", - "counts": { "passed": 0, "failed": 1, "skipped": 0 } } - ] - }, - { - // A fatal failure: this is what makes a verdict false. - "verified": false, - "verifiableCredential": { /* VC with a broken signature */ }, - "recognizedProfile": "obv3p0.openbadge", - "results": [ - { "id": "cryptographic.core.context-exists", /* success */ }, - { "id": "cryptographic.core.vc-context", /* success */ }, - { "id": "cryptographic.core.vc-structure", /* success */ }, - { "id": "cryptographic.core.credential-id", /* success */ }, - { "id": "cryptographic.core.proof-exists", /* success */ }, - { "id": "recognition.profile", /* success */ }, - { "id": "cryptographic.proof.signature", - "suite": "proof", - "outcome": { "status": "failure", "problems": [ - { "type": "https://www.w3.org/TR/vc-data-model#INVALID_SIGNATURE", - "title": "Invalid Signature", - "detail": "The credential's signature did not verify." } - ]}, - "fatal": true }, - { "id": "cryptographic.status.bitstring", - "suite": "status", - "outcome": { "status": "skipped", - "reason": "Not run: proof.signature failed" } }, - { "id": "cryptographic.status.list-issuer", - "suite": "status", - "outcome": { "status": "skipped", - "reason": "Not run: proof.signature failed" } }, - { "id": "trust.registry.issuer", - "suite": "registry", - "outcome": { "status": "skipped", - "reason": "Not run: proof.signature failed" } } - ], - "summary": [ - { "id": "cryptographic.core", "phase": "cryptographic", - "suite": "core", "status": "success", - "message": "5 of 5 checks passed", - "counts": { "passed": 5, "failed": 0, "skipped": 0 } }, - { "id": "recognition", "phase": "recognition", - "suite": "recognition", "status": "success", - "message": "1 of 1 check passed", - "counts": { "passed": 1, "failed": 0, "skipped": 0 } }, - // The fatal failure: the one summary with fatalFailureAt. - { "id": "cryptographic.proof", "phase": "cryptographic", - "suite": "proof", "status": "failure", - "message": "1 of 1 check failed", - "counts": { "passed": 0, "failed": 1, "skipped": 0 }, - "fatalFailureAt": "cryptographic.proof.signature" }, - { "id": "cryptographic.status", "phase": "cryptographic", - "suite": "status", "status": "skipped", - "message": "status not run: proof.signature failed", - "counts": { "passed": 0, "failed": 0, "skipped": 2 } }, - { "id": "trust.registry", "phase": "trust", - "suite": "registry", "status": "skipped", - "message": "registry not run: proof.signature failed", - "counts": { "passed": 0, "failed": 0, "skipped": 1 } } - ] - } - ] -} -``` - -**`SuiteSummary` type.** - -```ts -type SuiteSummaryPhase = - | 'cryptographic' - | 'trust' - | 'recognition' - | 'semantic' - | 'unknown'; - -interface SuiteSummary { - id: string; - phase: SuiteSummaryPhase; - suite: string; - status: 'success' | 'failure' | 'skipped' | 'mixed'; - message: string; - counts: { passed: number; failed: number; skipped: number }; - fatalFailureAt?: string; -} -``` - -**Grouping snippet.** - -```ts -const PHASE_ORDER: SuiteSummary['phase'][] = [ - 'cryptographic', - 'trust', - 'recognition', - 'semantic', - 'unknown', -]; - -function groupByPhase(cr: CredentialVerificationResult) { - const groups = new Map(); - for (const phase of PHASE_ORDER) groups.set(phase, []); - for (const s of cr.summary) groups.get(s.phase)!.push(s); - return groups; -} - -function failureDetail( - cr: CredentialVerificationResult, - failing: SuiteSummary, -) { - return cr.results.filter( - r => r.id.startsWith(failing.id + '.') && r.outcome.status === 'failure', - ); -} -``` - -**Layout reference.** - -``` -Presentation: ✗ (1 of 2 credentials verified) -├─ VP signature: ✓ -├─ Credential 1 (obv3p0.openbadge): ✓ — verified, with advisory failures -│ ├─ cryptographic ✓ -│ ├─ trust ✗ (1 of 1 check failed — advisory) -│ │ └─ trust.registry.issuer — Issuer Not Registered -│ └─ recognition ✓ -└─ Credential 2 (obv3p0.openbadge): ✗ - ├─ cryptographic ✗ (1 of 1 check failed) - │ └─ cryptographic.proof.signature — Invalid Signature - ├─ trust — (not run after fatal) - └─ recognition ✓ -``` diff --git a/docs/architecture.md b/docs/architecture.md index f5e9afb..cb6ceca 100644 --- a/docs/architecture.md +++ b/docs/architecture.md @@ -628,7 +628,7 @@ by `summary[]`, a per-suite rollup. The rollup is computed by a pure helper — that append late results and want to recompute their summaries. See [`docs/api/verification-results.md`](api/verification-results.md) for the full reference, -including the `id` namespace, rendering recipes, and the LLM prompt appendix for downstream UIs. +including the `id` namespace and rendering recipes. Both shapes use the suite-based `CheckResult[]` model. Each `CheckResult` carries a discriminated `CheckOutcome` (`success | failure | skipped`) plus provenance (suite, check id, fatal flag) and, @@ -743,8 +743,8 @@ without network dependencies, and composable — consumers wire in exactly the b recursion guard. - **`RegistryHandler` is a port.** Callers can override or extend the per-type handler map by passing `registryHandlers` on `VerifierConfig`. -- **No dynamic imports in `src/`.** The proof suite is statically wired; recursion through the - verifier replaced the old dynamic-import seam from `vc-recognition-handler` into `verify-suite`. +- **No dynamic imports in `src/`.** The proof suite is statically wired, and `vc-recognition-handler` + reaches the verifier through an injected thunk rather than by importing `verify-suite` at runtime. ### What's not yet hexagonal @@ -752,7 +752,8 @@ without network dependencies, and composable — consumers wire in exactly the b implementation embeds `@digitalcredentials/vc` directly inside `DataIntegrityCryptoService`. Replacing the default crypto service requires understanding the LD-Proofs / Data Integrity internals. The default crypto service verifies signatures only — credential status checking - is the sole responsibility of the status suite (P-E, 2026-04-19). + is the sole responsibility of the status suite, so the status list is fetched once per + verification and one answer, not two, is reported for a credential's status. - **Status suite** still consumes `@digitalcredentials/vc-bitstring-status-list` for purpose matching, list type, bitstring decoding, and index reading. The list's validity dates are checked by `@digitalcredentials/vc` inside the crypto service, with its proof, and issuer diff --git a/src/crypto-dispatch.ts b/src/crypto-dispatch.ts index 0943783..361fc53 100644 --- a/src/crypto-dispatch.ts +++ b/src/crypto-dispatch.ts @@ -1,9 +1,9 @@ /** * Shared crypto-service dispatch. * - * Presentation proofs, credential proofs, and (after the status-check - * rewrite) status-list-credential proofs all resolve through this helper - * against the same `CryptoService[]`. Callers own their problem types: + * Presentation proofs, credential proofs, and status-list-credential + * proofs all resolve through this helper against the same + * `CryptoService[]`. Callers own their problem types: * the helper reports a neutral outcome so a bad signature on a credential * and a bad signature on its status list credential stay distinct * diagnostics. diff --git a/src/fold-results.ts b/src/fold-results.ts index 6e52a96..b6881e4 100644 --- a/src/fold-results.ts +++ b/src/fold-results.ts @@ -5,9 +5,9 @@ * Called from `verifier.ts` after `runSuites` returns and after * the recognition payload has been lifted onto the result. The * helper has no I/O, no async, and no service dependencies — it - * is safe to call from any context (incl. consumers recomputing - * summaries after appending late results, e.g. - * dcc-transaction-service's async OpenBadges merge). + * is safe to call from any context, including consumers + * recomputing summaries after appending results that arrived + * later (an asynchronous OpenBadges merge, say). * * `results[]` is the raw input, unchanged: every check that ran, * was skipped, or was not run after a fatal failure. diff --git a/src/openbadges/achieved-level-check.ts b/src/openbadges/achieved-level-check.ts index 46f4050..a731a2a 100644 --- a/src/openbadges/achieved-level-check.ts +++ b/src/openbadges/achieved-level-check.ts @@ -27,7 +27,7 @@ import { formatJsonPointer } from '../util/json-pointer.js'; * - The credential has no `achievement.resultDescription[]` (nothing * to validate against). * - * Out of scope (intentional non-flagging — see plan Phase 3): + * Out of scope (intentional non-flagging): * - A `Result` that carries `achievedLevel` but is missing * `resultDescription` entirely. This is a different rule * ("missing reference"); the `result-ref` check only flags diff --git a/src/openbadges/index.ts b/src/openbadges/index.ts index 1f9929b..28df291 100644 --- a/src/openbadges/index.ts +++ b/src/openbadges/index.ts @@ -7,8 +7,7 @@ * passed explicitly via a verify call's `additionalSuites` (or * composed into a custom suite). * - * Surface follows the design in - * `docs/plans/2026-04-18-openbadges-module/00-design.md`: + * The surface is: * * - **Suites** (most common entry point): * {@link openBadgesSuite}, {@link openBadgesSemanticSuite}, diff --git a/src/openbadges/openbadges-suite.ts b/src/openbadges/openbadges-suite.ts index 281ac2f..2428a56 100644 --- a/src/openbadges/openbadges-suite.ts +++ b/src/openbadges/openbadges-suite.ts @@ -16,9 +16,9 @@ * for consumers who want full OB coverage and accept the schema * suite's network cost. * - * None of these are part of the verifier's default suite set; the - * 2026-04-18 plan ([Q2](../../docs/plans/2026-04-18-openbadges-module/00-questions.md#q2-default-path-behavior)) - * makes OpenBadges verification fully opt-in. To enable: + * None of these are part of the verifier's default suite set: + * OpenBadges verification is fully opt-in, so a caller who does not + * ask for it pays none of its cost. To enable: * * @example * ```ts diff --git a/src/openbadges/openbadges-zod.ts b/src/openbadges/openbadges-zod.ts index fa0c9ff..c517034 100644 --- a/src/openbadges/openbadges-zod.ts +++ b/src/openbadges/openbadges-zod.ts @@ -14,13 +14,12 @@ * fail the whole credential and silently skip the check. Callers filter at * the point of use. * - * These shapes are **not** exported from the submodule barrel in this plan — - * they are an internal implementation detail of the checks. A future effort can - * promote them (or a curated subset) to the public surface if it would be - * useful for consumer business logic. + * These shapes are **not** exported from the submodule barrel — they are an + * internal implementation detail of the checks. A future effort can promote + * them (or a curated subset) to the public surface if it would be useful for + * consumer business logic. * - * Shape coverage tracks what every check planned in the - * 2026-04-18-openbadges-module plan needs to read: + * Shape coverage tracks what each check needs to read: * * - `Obv3ResultShape` — `result-ref`, `achieved-level`, * `missing-result-status` @@ -33,9 +32,9 @@ * * ## Why these are kept alongside the strict `Obv3p0OpenBadgeCredentialSchema` * - * The 2026-04-18 P-C plan introduced a strict envelope schema in - * `schemas/classes-v3p0.ts` and `schemas/openbadge-credential-v3p0.ts` that - * validates an OB 3.0 credential top-to-bottom. That schema powers the + * `schemas/classes-v3p0.ts` and `schemas/openbadge-credential-v3p0.ts` hold a + * strict envelope schema that validates an OB 3.0 credential + * top-to-bottom. That schema powers the * recognition pipeline (`obv3p0Recognizer`) and the public * `recognition.profile` check. * diff --git a/src/openbadges/problem-types.ts b/src/openbadges/problem-types.ts index 5119f49..3718ffc 100644 --- a/src/openbadges/problem-types.ts +++ b/src/openbadges/problem-types.ts @@ -10,9 +10,9 @@ * `OpenBadgesProblemTypes`. * * Wire URIs use the same `https://www.w3.org/TR/vc-data-model#…` - * placeholder host as the rest of the catalog. See - * `docs/plans/2026-04-17-architecture-readiness-review/draft-ob-spec-issue-problem-details-registry.md` - * for why we treat these as opaque keys until a real registry exists. + * placeholder host as the rest of the catalog. No registry of Open + * Badges problem types exists yet, so consumers should treat these + * as opaque keys and match on them exactly. */ export const Obv3ProblemTypes = { diff --git a/src/openbadges/recognizers.ts b/src/openbadges/recognizers.ts index 6602e02..ccfba70 100644 --- a/src/openbadges/recognizers.ts +++ b/src/openbadges/recognizers.ts @@ -17,8 +17,6 @@ * exclusive (an EndorsementCredential isn't an OpenBadgeCredential * and vice versa), so configuration order doesn't affect which one * fires for a given credential. - * - * @see `docs/plans/2026-04-18-openbadges-recognizer-and-subchecks/00-design.md` */ import type { RecognizerSpec } from '../types/recognition.js'; diff --git a/src/openbadges/schemas/classes-v3p0.ts b/src/openbadges/schemas/classes-v3p0.ts index 5ae72ee..4e92a00 100644 --- a/src/openbadges/schemas/classes-v3p0.ts +++ b/src/openbadges/schemas/classes-v3p0.ts @@ -79,13 +79,13 @@ export function ImageField() { * Required: `id` (IRI; DIDs accepted), `type` (must include * `'Profile'`). * - * In-scope optional fields per the design (Q4.c): `name`, `url`, - * `description`, `image`, `email`. Email validation is light - * (`@`-presence) — full RFC-5321 conformance is out of scope. + * Optional fields in scope: `name`, `url`, `description`, + * `image`, `email`. Email validation is light (`@`-presence) — + * full RFC-5321 conformance is out of scope. * * Recursive Profile-typed fields (e.g. `parentOrg`) stay * passthrough; supporting them needs `z.lazy()` and is out of - * scope per the parent plan's Q4.c. + * scope. * * `id` uses {@link IriString} rather than `z.string().url()` so * `did:`-prefixed identifiers (the most common issuer form) are @@ -219,10 +219,10 @@ export type Obv3p0ResultDescription = z.infer< * * **Note**: `Result.resultDescription` is a string IRI cross- * referencing an entry in `Achievement.resultDescription[]`. The - * existing `obv3-result-ref-check` (refactored in Phase 8 to use - * the normalized form) verifies that the reference resolves; - * this schema only validates that the field is a syntactically - * valid URL string. + * `schema.obv3.result-ref` check verifies that the reference + * resolves, parsing with the tolerant shapes in + * `openbadges-zod.ts` rather than this schema; this schema only + * validates that the field is a syntactically valid URL string. * * Backfilled into {@link Obv3p0AchievementSubjectSchema}.`result[]`. */ @@ -265,9 +265,9 @@ const Obv3p0CriteriaSchema = z * - `creator` — Profile (normalized via {@link ProfileRefField}). * - `image` — Image (normalized via {@link ImageField}). * - `resultDescription[]` — array of - * {@link Obv3p0ResultDescriptionSchema} (Phase 7). + * {@link Obv3p0ResultDescriptionSchema}. * - * Wired into `AchievementSubject.achievement` in Phase 6. + * Reached from `AchievementSubject.achievement`. */ export const Obv3p0AchievementSchema = z .object({ @@ -289,9 +289,8 @@ export type Obv3p0Achievement = z.infer; * Open Badges 3.0 §B.1.12 — IdentityObject (file-local). * * Used inside `AchievementSubject.identifier[]` entries. Kept - * light per Q4.c — passthrough with a required `type` - * discriminator. Full hashed-identity validation (§9.3) is out of - * scope. + * light — passthrough with a required `type` discriminator. Full + * hashed-identity validation (§9.3) is out of scope. */ const Obv3p0IdentityObjectSchema = z .object({ @@ -313,7 +312,7 @@ const Obv3p0IdentityObjectSchema = z * `/credentialSubject` once backfilled into the envelope. * * Optional in scope: `result[]` (array of - * {@link Obv3p0ResultSchema}, Phase 7), `image` (normalized via + * {@link Obv3p0ResultSchema}), `image` (normalized via * {@link ImageField}), `identifier[]` (entries kept light per the * IdentityObject note above). */ diff --git a/src/openbadges/schemas/openbadge-credential-v3p0.ts b/src/openbadges/schemas/openbadge-credential-v3p0.ts index f289fc4..f3aa35a 100644 --- a/src/openbadges/schemas/openbadge-credential-v3p0.ts +++ b/src/openbadges/schemas/openbadge-credential-v3p0.ts @@ -1,13 +1,13 @@ /** * Strict Zod schema for the OB 3.0 OpenBadgeCredential envelope. * - * Phase-2 scope: top-level envelope only. Inner classes - * (`AchievementSubject`, `Achievement`, `Profile`, `Image`, - * `Result`, etc.) stay `passthrough` and are filled in by Phases - * 3–7. The cross-field VCDM v1/v2 date discriminator - * (`issuanceDate` vs `validFrom`) lives here. - * - * @see `docs/plans/2026-04-18-openbadges-recognizer-and-subchecks/02-envelope-and-date-discriminator.md` + * This module owns the top-level envelope: the `@context` array, + * `id`, `type`, `issuer`, `image`, the date fields, and the + * cross-field VCDM v1/v2 date discriminator (`issuanceDate` vs + * `validFrom`). Inner classes (`AchievementSubject`, + * `Achievement`, `Profile`, `Image`, `Result`, etc.) are defined + * in `classes-v3p0.ts` and composed in here, so a credential + * validates top-to-bottom. */ import { z } from 'zod'; @@ -50,8 +50,7 @@ const TypeField = JsonLdTypeField(['VerifiableCredential']).superRefine( * this, `tsc --declaration` runs into TS7056 ("inferred type * exceeds the maximum length the compiler will serialize") * because the union/refine layers compound across every nested - * class schema (Phase 7 was the straw that broke the camel's - * back). Runtime behavior is unaffected; consumers narrow + * class schema. Runtime behavior is unaffected; consumers narrow * `Obv3p0OpenBadgeCredential` (typed as a permissive record * below) at use, typically via `recognizedProfile`. */ diff --git a/src/problem-types.ts b/src/problem-types.ts index aa92564..37fd398 100644 --- a/src/problem-types.ts +++ b/src/problem-types.ts @@ -14,13 +14,13 @@ * Of those four, this catalog currently emits only PARSING_ERROR. * The other three are listed in the JSDoc above for awareness; we * may align synthesized tokens like INVALID_SIGNATURE onto - * CRYPTOGRAPHIC_SECURITY_ERROR in a future deliberate refactor (out - * of scope for P-D — see the design doc's non-goals). + * CRYPTOGRAPHIC_SECURITY_ERROR in a future deliberate refactor; + * doing so would change values consumers match on, so it is not + * done casually. * * **Synthesized placeholders** — temporary URIs we use until the * W3C VC working group publishes a wider error registry or we adopt - * our own URN namespace (see the readiness review's Q9 for the - * decision history). They reuse the same + * our own URN namespace. They reuse the same * `https://www.w3.org/TR/vc-data-model#…` prefix as a stable string * but are NOT defined by the spec; treat them as opaque keys. * diff --git a/src/services/data-integrity-crypto.ts b/src/services/data-integrity-crypto.ts index ecac6a6..8329e63 100644 --- a/src/services/data-integrity-crypto.ts +++ b/src/services/data-integrity-crypto.ts @@ -25,11 +25,11 @@ import { isVerificationMethodMessage } from '../util/verification-method-message * for any VC with `credentialStatus`; this no-op satisfies that * required signature without performing any real status work. * - * Real status verification is owned by `statusSuite` - * (`src/suites/status/bitstring-status-check.ts`). See plan - * `docs/plans/2026-04-19-route-via-http-and-dedupe-status-check/` - * for the rationale (P-E: status is owned solely by the status - * suite, no double-check from the proof path). + * Real status verification is owned solely by `statusSuite` + * (`src/suites/status/bitstring-status-check.ts`), so the proof + * path must not check status a second time: a double check would + * fetch the status list twice and could report two different + * answers for one credential. */ const noopCheckStatus = async (): Promise<{ verified: true; results: [] }> => ({ verified: true, diff --git a/src/services/time-service/fake-time-service.ts b/src/services/time-service/fake-time-service.ts index 134d369..20192af 100644 --- a/src/services/time-service/fake-time-service.ts +++ b/src/services/time-service/fake-time-service.ts @@ -20,13 +20,11 @@ import type { TimeService } from './time-service.js'; * wall-clock and performance reads can interleave without * skewing each other's expected values in tests. * - * Diverges from the skills-verifier `FakeTimeService` (which - * passes `performanceNowMs()` through to real - * `performance.now()`) on purpose: verifier-core uses - * `performanceNowMs()` to populate `TaskTiming.durationMs`, - * and shape-only duration assertions are a poor substitute - * for the deterministic exact-value assertions consumers - * actually want when verifying timing rollup math. + * `performanceNowMs()` is counted rather than passed through + * to the real `performance.now()` on purpose: it populates + * `TaskTiming.durationMs`, and shape-only duration assertions + * are a poor substitute for the deterministic exact-value + * assertions that timing rollup math needs. */ export function FakeTimeService( opts: { diff --git a/src/suites/status/bitstring-status-check.ts b/src/suites/status/bitstring-status-check.ts index 2edeb7c..1d70efa 100644 --- a/src/suites/status/bitstring-status-check.ts +++ b/src/suites/status/bitstring-status-check.ts @@ -22,7 +22,9 @@ import { loadStatusList } from './status-lists.js'; -// Error patterns from constants/external.ts +// Error strings raised by `@digitalcredentials/vc-bitstring-status-list`, +// matched to classify its rejections. They are not part of that package's +// typed surface, so they are pinned here as literals. const NOT_FOUND_ERROR = 'NotFoundError'; const STATUS_TYPE_ERROR = 'Status list credential type must include "BitstringStatusListCredential".'; diff --git a/src/types/check.ts b/src/types/check.ts index 45995c7..f4b933c 100644 --- a/src/types/check.ts +++ b/src/types/check.ts @@ -7,7 +7,7 @@ * iterates suites and assembles a flat `CheckResult[]` report. * * @see {@link ../run-suites.ts} for orchestration - * @see {@link ../defaults.ts} for the default suite ordering + * @see {@link ../default-suites.ts} for the default suite ordering */ import type { ProblemDetail } from './problem-detail.js'; diff --git a/src/types/crypto-service.ts b/src/types/crypto-service.ts index 86c91c8..dae0feb 100644 --- a/src/types/crypto-service.ts +++ b/src/types/crypto-service.ts @@ -3,7 +3,7 @@ * * Implementations wrap concrete libraries (e.g. `@digitalcredentials/vc` for Data * Integrity). The proof suite dispatches to `CryptoService` instances from - * `VerificationContext` (phase 2). + * `VerificationContext`. */ import type { DocumentLoader } from './context.js'; diff --git a/src/types/recognition.ts b/src/types/recognition.ts index ab2f6d0..0492b78 100644 --- a/src/types/recognition.ts +++ b/src/types/recognition.ts @@ -8,8 +8,6 @@ * applies-true match's normalized form on * {@link CredentialVerificationResult.normalizedVerifiableCredential} * + {@link CredentialVerificationResult.recognizedProfile}. - * - * @see `docs/plans/2026-04-18-openbadges-recognizer-and-subchecks/00-design.md` */ import type { ProblemDetail } from './problem-detail.js'; diff --git a/src/types/timing.ts b/src/types/timing.ts index 02b5dd5..c227794 100644 --- a/src/types/timing.ts +++ b/src/types/timing.ts @@ -15,10 +15,9 @@ * - `events` — reserved for an ordered series of sub-events * captured from inside one task (e.g. `signature.verify`, * `documentLoader.load`, `statusList.fetch`). No built-in - * capture writes to this field today; a future plan may - * add a hook on `VerificationContext` for checks to push - * sub-events. Recursive so a sub-event can have its own - * sub-events. + * capture writes to this field today; a future hook on + * `VerificationContext` could let checks push sub-events. + * Recursive so a sub-event can have its own sub-events. * * @see ../../docs/api/timing.md for usage patterns. */ diff --git a/src/util/did-web-driver-with-http-get.ts b/src/util/did-web-driver-with-http-get.ts index 93efcc7..f0a16c7 100644 --- a/src/util/did-web-driver-with-http-get.ts +++ b/src/util/did-web-driver-with-http-get.ts @@ -1,8 +1,8 @@ /** * did:web resolution using caller-provided {@link HttpGetService} so DID document - * fetches share the same cache as JSON-LD context loads (e.g. Keyv in - * transaction-service). The stock {@link DidWebDriver} uses - * `@digitalcredentials/http-client` and bypasses that cache. + * fetches share the same cache as JSON-LD context loads (a Keyv-backed + * cache in a long-running service, say). The stock {@link DidWebDriver} + * uses `@digitalcredentials/http-client` and bypasses that cache. * * Fragment dereference logic matches `@digitalcredentials/did-method-web` * `getNode` (suite context map aligned with that package). diff --git a/src/verifier.ts b/src/verifier.ts index 8c00df9..a31fa6e 100644 --- a/src/verifier.ts +++ b/src/verifier.ts @@ -33,14 +33,14 @@ * not name, and a `.transform` can rewrite scalars into arrays. Either change * alters the canonicalized N-Quads, which makes a valid proof fail as * `INVALID_SIGNATURE` — a failure that looks like a bad credential and is - * actually a bug here. That is not hypothetical: a spec-legal - * `issuer.image.caption` was once dropped by a nested issuer image schema - * and rejected production Open Badges credentials. + * actually a bug here. A nested issuer image schema that drops a spec-legal + * `issuer.image.caption`, for instance, rejects Open Badges credentials that + * are perfectly valid. * * Returning the original object also matters downstream, because consumers - * re-verify `result.verifiableCredential` (the async Open Badges pass in - * dcc-transaction-service does exactly this). Handing them a rewritten copy - * makes the second pass fail where the first passed. + * re-verify `result.verifiableCredential` — an asynchronous second Open + * Badges pass, say. Handing them a rewritten copy makes that pass fail where + * the first passed. * * Suites must therefore tolerate raw JSON-LD shapes rather than assume * normalization: `@context` and `type` may each be a string or an array. diff --git a/test/factories/services/build-test-context.ts b/test/factories/services/build-test-context.ts index fe70c0c..ee78bc2 100644 --- a/test/factories/services/build-test-context.ts +++ b/test/factories/services/build-test-context.ts @@ -1,16 +1,15 @@ /** * Test-only helper that assembles a {@link VerificationContext} with - * sensible defaults — equivalent to the old `buildContext()` from - * `src/defaults.ts` (deleted in P-A phase 4). + * sensible defaults. * * Production code does not use this; the public path is * `createVerifier(...)`. Suite unit tests use it because they exercise * a single suite against a hand-built context, not the full pipeline. * - * Precedence matches the post-P-E production factory: explicit - * overrides win, otherwise the document loader and JSON fetcher are - * always derived from the effective `httpGetService` (default or - * override). No "first-time caller bypass" branch. + * Precedence matches `createVerifier`: explicit overrides win, + * otherwise the document loader and JSON fetcher are always derived + * from the effective `httpGetService` (default or override), so a + * test's injected HTTP service sees every fetch. */ import { Ed25519Signature2020 } from '@digitalcredentials/ed25519-signature-2020'; diff --git a/test/openbadges/fixtures/obv3p0-openbadge-spec-conforming.ts b/test/openbadges/fixtures/obv3p0-openbadge-spec-conforming.ts index 2ac0592..dd12d0d 100644 --- a/test/openbadges/fixtures/obv3p0-openbadge-spec-conforming.ts +++ b/test/openbadges/fixtures/obv3p0-openbadge-spec-conforming.ts @@ -1,11 +1,10 @@ /** * Spec-conforming VCDM v2 OB 3.0 OpenBadgeCredential fixture. * - * Designed to round-trip through every schema phase of the - * 2026-04-18-openbadges-recognizer-and-subchecks plan: every + * Designed to round-trip through every OB 3.0 class schema: every * required envelope, profile, achievement, subject, and result - * field is present so later phases can assert on the same fixture - * without amendment. + * field is present, so a spec for any one class can assert against + * this fixture without amending it. * * Authored as a `.ts` module (rather than `.json`) to match the * existing fixture convention in this repo diff --git a/test/openbadges/openbadges-submodule.spec.ts b/test/openbadges/openbadges-submodule.spec.ts index 1ab0c57..3378a06 100644 --- a/test/openbadges/openbadges-submodule.spec.ts +++ b/test/openbadges/openbadges-submodule.spec.ts @@ -7,7 +7,7 @@ * dynamic `import('@digitalcredentials/verifier-core/openbadges')` * catches typos in `exports` and missing re-exports in * `src/openbadges/index.ts`. - * 2. **Default-path invariant (Q2).** A vanilla + * 2. **Default-path invariant.** A vanilla * `createVerifier()` against a real OB credential emits zero * OB-related `CheckResult` entries. * 3. **Opt-in path emits OB findings.** Adding `openBadgesSuite` @@ -118,7 +118,7 @@ describe('@digitalcredentials/verifier-core/openbadges (integration)', () => { expect(ACHIEVEMENT_TYPE_EXT_PREFIX).toBe('ext:'); }); - it('runs no OB checks by default (Q2 invariant)', async () => { + it('runs no OB checks by default', async () => { const verifier = createVerifier(fakeVerified); const result = await verifier.verifyCredential({ credential: cloneCredential() diff --git a/test/openbadges/schema/achievement-subject-v3p0.spec.ts b/test/openbadges/schema/achievement-subject-v3p0.spec.ts index 01b75bb..0532b0c 100644 --- a/test/openbadges/schema/achievement-subject-v3p0.spec.ts +++ b/test/openbadges/schema/achievement-subject-v3p0.spec.ts @@ -1,5 +1,5 @@ /** - * Spec for the Phase-6 OB 3.0 `AchievementSubject` (§B.1.3) class + * Spec for the OB 3.0 `AchievementSubject` (§B.1.3) class * schema, its **id-or-identifier[]** at-least-one refinement, and * its backfill into the top-level envelope's `credentialSubject` * slot. @@ -119,7 +119,7 @@ describe('Obv3p0AchievementSubjectSchema (standalone)', () => { } }); - it('passes result[] entries through unchanged (Phase 7 backfills)', () => { + it('passes result[] entries through unchanged', () => { const opaqueResult = { type: ['Result'], value: '95', diff --git a/test/openbadges/schema/achievement-v3p0.spec.ts b/test/openbadges/schema/achievement-v3p0.spec.ts index 80b6df8..a4c6338 100644 --- a/test/openbadges/schema/achievement-v3p0.spec.ts +++ b/test/openbadges/schema/achievement-v3p0.spec.ts @@ -1,10 +1,10 @@ /** - * Spec for the Phase-5 OB 3.0 `Alignment` (§B.1.5) and - * `Achievement` (§B.1.1) class schemas. + * Spec for the OB 3.0 `Alignment` (§B.1.5) and `Achievement` + * (§B.1.1) class schemas. * - * Achievement is not yet wired into `AchievementSubject` (Phase 6 - * does that). These schemas are tested standalone via - * `safeParse`. Path-based `instance` assertions verify that + * These schemas are tested standalone via `safeParse`; the + * envelope spec covers them in composition. Path-based + * `instance` assertions verify that * malformed nested entries (e.g. `alignment[1]`) carry accurate * JSON Pointers when run through Zod. */ diff --git a/test/openbadges/schema/envelope-v3p0.spec.ts b/test/openbadges/schema/envelope-v3p0.spec.ts index f9ce68c..3fddb1b 100644 --- a/test/openbadges/schema/envelope-v3p0.spec.ts +++ b/test/openbadges/schema/envelope-v3p0.spec.ts @@ -1,8 +1,6 @@ /** - * Spec for the Phase-2 OB 3.0 envelope schema. - * - * Asserts the contract documented in - * `docs/plans/2026-04-18-openbadges-recognizer-and-subchecks/02-envelope-and-date-discriminator.md`. + * Spec for the OB 3.0 envelope schema, including the cross-field + * VCDM v1/v2 date discriminator (`issuanceDate` vs `validFrom`). */ import { describe, it, expect } from 'vitest'; @@ -219,9 +217,9 @@ describe('parseObv3p0OpenBadgeCredential (envelope)', () => { }); it('omits instance for whole-document problems (none expected at this layer)', () => { - // Sanity: every problem we currently raise has a path. Future - // changes that introduce root-level refines should leave - // `instance` undefined per the design's policy. + // Sanity: every problem we currently raise has a path. A + // root-level refine added later should leave `instance` + // undefined rather than point at the document root. const cred = clone(obv3p0OpenBadgeSpecConforming); cred.id = 'not-an-iri'; diff --git a/test/openbadges/schema/image-v3p0.spec.ts b/test/openbadges/schema/image-v3p0.spec.ts index 188bf79..1510bee 100644 --- a/test/openbadges/schema/image-v3p0.spec.ts +++ b/test/openbadges/schema/image-v3p0.spec.ts @@ -1,10 +1,6 @@ /** - * Spec for the Phase-3 OB 3.0 `Image` class schema and the - * `ImageField()` builder, plus its backfill into the top-level - * credential `image` slot. - * - * Asserts the contract documented in - * `docs/plans/2026-04-18-openbadges-recognizer-and-subchecks/03-image.md`. + * Spec for the OB 3.0 `Image` class schema and the `ImageField()` + * builder, plus its use in the top-level credential `image` slot. */ import { describe, it, expect } from 'vitest'; diff --git a/test/openbadges/schema/profile-v3p0.spec.ts b/test/openbadges/schema/profile-v3p0.spec.ts index b8eb322..6fba147 100644 --- a/test/openbadges/schema/profile-v3p0.spec.ts +++ b/test/openbadges/schema/profile-v3p0.spec.ts @@ -1,10 +1,7 @@ /** - * Spec for the Phase-4 OB 3.0 `Profile` class schema and the - * `ProfileRefField()` builder, plus its backfill into + * Spec for the OB 3.0 `Profile` class schema and the + * `ProfileRefField()` builder, plus its use in * `credential.issuer`. - * - * Asserts the contract documented in - * `docs/plans/2026-04-18-openbadges-recognizer-and-subchecks/04-profile-and-profileref.md`. */ import { describe, it, expect } from 'vitest'; @@ -154,7 +151,8 @@ describe('credential.issuer (backfilled ProfileRefField)', () => { // when neither branch advances past structural matching — the // string-IRI branch is rejected immediately (input is an object) // and the Profile branch fails at the missing required `type`. - // This matches the plan's "appropriate parent path" allowance. + // Attribution to the parent path is accurate enough here: the + // failure is "this issuer value fits neither form". assertMalformedAt(result, '/issuer'); }); diff --git a/test/openbadges/schema/result-v3p0.spec.ts b/test/openbadges/schema/result-v3p0.spec.ts index 00a0a0b..8bde445 100644 --- a/test/openbadges/schema/result-v3p0.spec.ts +++ b/test/openbadges/schema/result-v3p0.spec.ts @@ -1,5 +1,5 @@ /** - * Spec for the Phase-7 OB 3.0 result trio: + * Spec for the OB 3.0 result trio: * - RubricCriterionLevel (§B.1.18) * - ResultDescription (§B.1.17) * - Result (§B.1.16) @@ -10,9 +10,9 @@ * attribution for malformed nested entries when validated through * the envelope. * - * Phase 7 closes out the strict envelope; Phase 8 will refactor - * the existing OB semantic checks (achieved-level, result-ref) to - * consume this normalized form via `recognizedProfile`. + * The OB semantic checks (achieved-level, result-ref) parse with + * the tolerant shapes in `openbadges-zod.ts` instead of this + * normalized form, so nothing here constrains them. */ import { describe, it, expect } from 'vitest'; diff --git a/test/phase-filter.spec.ts b/test/phase-filter.spec.ts index ebbc2a3..260fdf1 100644 --- a/test/phase-filter.spec.ts +++ b/test/phase-filter.spec.ts @@ -1,6 +1,5 @@ /** - * Spec for the suite-phase filter (Phase 10 of the - * 2026-04-18-openbadges-recognizer-and-subchecks plan). + * Spec for the suite-phase filter. * * Coverage: * diff --git a/test/signed-bytes-preserved.spec.ts b/test/signed-bytes-preserved.spec.ts index c4a639a..363b2ac 100644 --- a/test/signed-bytes-preserved.spec.ts +++ b/test/signed-bytes-preserved.spec.ts @@ -32,8 +32,9 @@ const offlineHttpGetService = { const documentLoader = defaultDocumentLoaderFor(offlineHttpGetService as never); /** - * The issuer image `caption` is the load-bearing field: it is signed, and it is - * the key the old schema dropped. + * The issuer image `caption` is the load-bearing field: it is signed, and it + * is exactly the kind of key a nested object schema drops when it does not + * name it. */ const credentialTemplate = (issuerDid: string): Record => ({ '@context': [ @@ -128,9 +129,8 @@ describe('signed bytes survive verification', () => { }); it('returns the credential as issued, so a second pass can re-verify it', async () => { - // dcc-transaction-service re-verifies `verifiableCredential` in its - // asynchronous Open Badges pass. A rewritten copy fails there even though - // the synchronous pass succeeded. + // Consumers re-verify `verifiableCredential` in a later pass. A + // rewritten copy fails there even though the first pass succeeded. const result = await verifier().verifyCredential({ credential: signedCredential, phases: ['cryptographic'] diff --git a/test/verifier-cache-sharing.spec.ts b/test/verifier-cache-sharing.spec.ts index cfad642..5140198 100644 --- a/test/verifier-cache-sharing.spec.ts +++ b/test/verifier-cache-sharing.spec.ts @@ -1,5 +1,6 @@ /** - * Pins the headline performance claim of P-A: a single {@link createVerifier} + * Pins the verifier's headline performance claim: a single + * {@link createVerifier} * instance shares its long-lived caches across calls. In particular, an * issuer registry lookup that requires an HTTP fetch happens once per * registry URL — subsequent verifications for the same issuer hit the @@ -137,7 +138,7 @@ describe('Verifier cache sharing', () => { expect(http.callsTo(REGISTRY_URL)).toBe(2); }); - describe('status list URL routing through httpGetService (P-E)', () => { + describe('status list URL routing through httpGetService', () => { const STATUS_LIST_URL = 'https://example.test/status/list-pe'; /** Same-shape VC as `makeCredential`, but adds a BitstringStatusListEntry. */ @@ -173,13 +174,13 @@ describe('Verifier cache sharing', () => { credential: makeRevocableCredential('urn:uuid:status-once-1') }); - // Pre-P-E this was 2 (DataIntegrityCryptoService.checkStatus + statusSuite). - // Post-P-E only statusSuite fetches the list. + // Only statusSuite fetches the list: the proof path passes a no-op + // `checkStatus`, so there is no second fetch from there. expect(http.callsTo(STATUS_LIST_URL)).toBe(1); }); }); - describe('document loader memoization per HttpGetService (P-E)', () => { + describe('document loader memoization per HttpGetService', () => { it('returns the same DocumentLoader for the same HttpGetService instance', () => { const fake: HttpGetService = FakeHttpGetService({}); const a = defaultDocumentLoaderFor(fake); diff --git a/test/verifier-recognition.spec.ts b/test/verifier-recognition.spec.ts index f5714c1..86b4dd6 100644 --- a/test/verifier-recognition.spec.ts +++ b/test/verifier-recognition.spec.ts @@ -1,8 +1,7 @@ /** * End-to-end spec for the recognition pipeline through `createVerifier`. * - * Asserts the contract documented in - * `docs/plans/2026-04-18-openbadges-recognizer-and-subchecks/00-design.md`: + * Asserts the recognition contract: * * - With no `recognizers` configured, `recognition.profile` emits * skipped and the result has no `normalizedVerifiableCredential` diff --git a/test/verify-credential.spec.ts b/test/verify-credential.spec.ts index 266ee00..17393de 100644 --- a/test/verify-credential.spec.ts +++ b/test/verify-credential.spec.ts @@ -535,13 +535,13 @@ describe('verifyCredential', () => { }); }); - // P-E regression: revoked credentials must fail with the failure - // sourced from the status suite, not the proof suite. We exercise this - // through the same code path createVerifier uses (defaultSuites + - // runSuites + hasFatalFailures). FakeCryptoService verifies both the - // credential proof and the unsigned factory status-list credential so - // the contract being pinned is the aggregation, not real cryptography. - describe('revoked credential sourcing (P-E)', () => { + // Revoked credentials must fail with the failure sourced from the + // status suite, not the proof suite. We exercise this through the same + // code path createVerifier uses (defaultSuites + runSuites + + // hasFatalFailures). FakeCryptoService verifies both the credential + // proof and the unsigned factory status-list credential so the contract + // being pinned is the aggregation, not real cryptography. + describe('revoked credential sourcing', () => { it('flips verified to false via status.bitstring (not proof.signature) when status list marks the index revoked', async () => { const listUrl = 'https://factory.test/status/list-revoked-pe'; const slCred = await StatusListCredentialFactory({