13 KiB
13 KiB
Tasks — Schema-registry completion
Every checkbox MUST reflect work actually done in this change. Do not retroactively tick boxes for work that was already in main when this change opened.
0. Pre-flight (non-negotiable)
- 0.1 Confirm clean working tree (
git statusreports nothing) and no in-flight worktrees with uncommitted work (git worktree listthengit statusin each) - 0.2 If any worktrees exist under
.claude/worktrees/, account for each one: still-active agent, work-pending-merge, or stale-and-prune. Stale worktrees MUST be removed (git worktree remove) before opening a new one — code that has been lost twice on main is the failure mode this guards against. - 0.3 Confirm the package set in
MANIFEST.mdmatchesfind packages -maxdepth 3 -name package.jsoncount. Drift here means a prior change archived without regenerating the manifest.
1. Spec authoring
- 1.1 Read legacy
packages/universal/schema-registry/srcend-to-end at../suaveplan/ - 1.2 Read genesis
packages/core/schema-registry/srcend-to-end andopenspec/specs/(no canonical schema-registry spec exists yet — this proposal seeds it) - 1.3 Read
openspec/AGENTS.mdSections 13 (spec-conformance audit), 14 (source mapping for migrations); §14a (proposal must list affected dependents); §0a (parity checklist) - 1.4 Draft
proposal.mdfromopenspec/templates/proposal.md - 1.5 Fill
## Source mappinginproposal.md— every requirement cites a line range OR appears in the "no source line" table with justification - 1.6 Draft
specs/schema-registry/spec.mdfromopenspec/templates/spec-delta.md— seven ADDED requirements (transitive, diff, store interface, memory store, database store, canonical utilities, subject config) - 1.7 Every requirement in the spec delta carries a
**Verified by:**line naming concreteit(...)titles - 1.8 Draft
## Migrationsection listing theCompatibilityLevelunion widening and the new async constructor option - 1.9
bunx openspec change validate schema-registry-completionexits with no errors
2. Implementation — types
- 2.1 Extend
CompatibilityLevelinsrc/types/schema-registry.types.tsto includeBACKWARD_TRANSITIVE,FORWARD_TRANSITIVE,FULL_TRANSITIVE - 2.2 Add
SchemaFieldinterface insrc/types/schema-registry.types.ts(legacy L15-L26); used by diff and canonical utilities - 2.3 Add
DiffChangeTypeunion ("added" | "removed" | "modified") - 2.4 Add
SchemaDiffChangeandSchemaDiffResultinterfaces - 2.5 Add
TransitiveReportandTransitiveVersionResultinterfaces - 2.6 Add
SubjectConfiginterface (compatibility,maxVersions?,metadata?) - 2.7 Add
SchemaStoreAdapterinterface (8 async methods per legacy L82-L105) - 2.8 Add optional
fingerprint?: stringtoSchemaDefinition
3. Implementation — utilities
- 3.1 Create
src/utils/canonical/canonical.tsexportingcanonicalizeSchema,computeFingerprint,djb2Hash,flattenFields,isOptionalOrHasDefault(legacysrc/utils/utils.tsL1-L70 verbatim) - 3.2 Sibling
canonical.md≥200 words with basic + advanced examples
4. Implementation — transitive compatibility
- 4.1 Create
src/core/transitive/transitive.tsexportingcheckTransitiveCompatibility(history, candidate, mode),buildTransitiveReport(history, candidate, mode),isTransitiveMode(mode),stripTransitive(mode) - 4.2 Use the genesis
checkCompatibilitydirectly (do not duplicate the rules); transitive entry points iterate history and aggregate errors with[v<n>]prefixes (legacy convention, L31) - 4.3 Sibling
transitive.md≥200 words
5. Implementation — diff
- 5.1 Create
src/core/diff/diff.tsexportingdiffSchemas(oldSchema, newSchema)andformatDiffReport(result)(legacysrc/diff/schema-differ.tsL1-L175 verbatim) - 5.2 Use
flattenFieldsfrom canonical utilities; never duplicate the field-walking logic - 5.3 Sibling
diff.md≥200 words
6. Implementation — stores
- 6.1 Create
src/store/memory/memory.tsexportingMemorySchemaStoreimplementingSchemaStoreAdapteragainst an in-memoryMap<string, SchemaDefinition[]>for versions andMap<string, SubjectConfig>for configs - 6.2 Create
src/store/database/database.tsexportingDatabaseSchemaStore<TAdapter extends DatabaseAdapter>(legacysrc/store/database-store.tsL1-L147) - 6.3 Add
@suaveplan/dbtopackages/core/schema-registry/package.jsondependencies(workspace:^); verifySTATUS.mdlists@suaveplan/dbas completed (it is —db-sqliteis shipped) - 6.4 Re-target the
DatabaseAdapterimport in the database store to@suaveplan/db(legacy used an inline interface) - 6.5 Sibling
memory.mdanddatabase.md≥200 words each
7. Implementation — registry async surface
- 7.1 Extend
SchemaRegistryconstructor to acceptstore?: SchemaStoreAdapterinRegistryOptions - 7.2 When
storeis supplied, route persistence through the store; gate sync method calls withSchemaRegistryError(SCHEMA_REGISTRY.STORE_FAILURE, ..., { context: { reason: "sync_call_on_async_store" } }) - 7.3 Add async siblings:
registerAsync,getLatestAsync,getVersionAsync,getAllAsync,listSubjectsAsync,deleteSubjectAsync,checkCompatibilityAsync - 7.4 In
register/registerAsync, computefingerprint = computeFingerprint(schema as SchemaDefinition)whenformat === "json-schema"and the schema matchesSchemaDefinitionshape; stamp it onto the resultingSchemaDefinition - 7.5 De-dup: if the latest version's fingerprint equals the candidate's fingerprint, return the existing version (no new version assigned)
- 7.6 Wire the new
*_TRANSITIVEmodes into the existingregisterflow: whenstate.compatibilityends with_TRANSITIVE, callcheckTransitiveCompatibility(state.versions, schema, state.compatibility)instead ofcheckCompatibility(schema, latest, ...) - 7.7 Validate
setCompatibility(subject, level)against the seven-mode union; throwSchemaRegistryError(SCHEMA_REGISTRY.UNKNOWN_COMPATIBILITY_MODE, ...)on invalid input
8. Errors
- 8.1 Add
STORE_FAILUREandUNKNOWN_COMPATIBILITY_MODEtoSCHEMA_REGISTRY_CODESinsrc/errors/schema-registry-error.ts - 8.2 Update sibling
schema-registry-error.mdto document the two new codes
9. Wiring
- 9.1 Update
packages/core/schema-registry/src/index.tsto re-export every new symbol (transitive helpers, diff, stores, canonical utilities, types) - 9.2 Update
packages/core/schema-registry/package.jsonexportsmap: add./transitive,./diff,./store/memory,./store/database,./utils/canonicalsubpath entries - 9.3 Update root
README.md## Modulessection to link the new co-located.mdfiles - 9.4 Update the package description in
package.jsonto mention transitive compatibility, diff, and pluggable stores
10. Tests
- 10.1
src/core/transitive/transitive.test.ts— multi-version history;BACKWARD_TRANSITIVErejects when ANY old version disagrees;FORWARD_TRANSITIVEandFULL_TRANSITIVEanalogous; per-version error prefixes[v1],[v2], ... - 10.2
src/core/transitive/transitive.spec-conformance.test.ts— exhaustiveisTransitiveMode/stripTransitiveover all sevenCompatibilityLevelvalues - 10.3
src/core/diff/diff.test.ts— added / removed / modified / type-changed; nested-field diff;formatDiffReportrendering with[BREAKING]annotation - 10.4
src/core/diff/diff.spec-conformance.test.ts—fast-checkpropertydiffSchemas(a, a).changes === [];formatDiffReportempty result text - 10.5
src/store/memory/memory.test.ts— round-trip every method ofSchemaStoreAdapter;deleteSubjectreturnsfalsewhen subject absent - 10.6
src/store/memory/memory.spec-conformance.test.ts— pins the contract (every method present, every method async) - 10.7
src/store/database/database.test.ts— against a fakeDatabaseAdapterthat records SQL; verifiesINSERT/SELECT/DELETEissued correctly with positional params - 10.8
src/store/database/database.spec-conformance.test.ts— error path:db.queryrejects →STORE_FAILUREraised with cause-chain preserving the original error - 10.9
src/utils/canonical/canonical.test.ts—canonicalizeSchemasorts at every level;computeFingerprintdeterministic;djb2Hashmatches known vectors;flattenFieldsproduces dot-paths;isOptionalOrHasDefaulttruthy/falsy - 10.10
src/utils/canonical/canonical.spec-conformance.test.ts—fast-checkpropertycanonicalizeSchema(a) === canonicalizeSchema(shuffleFields(a))(canonicalisation is field-order independent) - 10.11 Extend
src/core/registry/schema-registry.test.ts— async surface end-to-end withMemorySchemaStore; verifyregisterde-dup based on fingerprint; verify transitive mode triggers transitive checker - 10.12 Extend
src/core/registry/schema-registry.spec-conformance.test.ts—UNKNOWN_COMPATIBILITY_MODEthrown for invalidsetCompatibility;STORE_FAILUREthrown when sync method called on store-backed registry - 10.13 Every
it(...)in 10.1-10.12 carries{ timeout: 5000 }
11. Documentation
- 11.1 Sibling
.mdfor every implementation.ts(≥200 words, basic + advanced examples, full API reference) - 11.2 README links every co-located
.mdunder## Modules - 11.3 README
## Featuresenumerates the seven new capabilities (transitive, diff, store interface, memory store, database store, canonical utilities, subject config) - 11.4 Update
CLAUDE.mdpackage listing for@suaveplan/schema-registryto include the new exports
12. Pre-archive verification (the 5-gate triple)
- 12.1
bun run check— Biome 0 errors / 0 warnings / 0 info across the whole monorepo - 12.2
bunx turbo lint typecheck test --filter=@suaveplan/schema-registry— green - 12.3
bunx turbo test:coverage --filter=@suaveplan/schema-registry— 100/100/100/100 - 12.4
bun scripts/openspec-conformance.ts @suaveplan/schema-registry— exit 0, noerrorfindings - 12.5
bunx turbo build --filter=@suaveplan/schema-registry— green
13. Pre-archive 7-row checklist (AGENTS.md Section 13)
- 13.1 Generics threaded;
expectTypeOfassertions on every literal-type claim (CompatibilityLevelwidened,SCHEMA_REGISTRY_CODESkeys,DiffChangeTypeliterals) - 13.2 Zod schemas wired at every declared boundary (
SchemaDefinition,SubjectConfig,SchemaField,RegistryOptionsextended) - 13.3 Every error code in
SCHEMA_REGISTRY_CODESthrown by at least one production path (STORE_FAILUREandUNKNOWN_COMPATIBILITY_MODEcovered above; existing four codes remain in place) - 13.4 Strict-mode behaviors tested on and off (transitive vs. non-transitive, store-backed vs. in-memory)
- 13.5 OTel instruments emitted from production paths and asserted with
InMemorySpanExporterfrom@suaveplan/opentelemetry(theregister.totalcounter andcheck.duration_mshistogram are already in place; new transitive/store paths should record to the same instruments) - 13.6 Lifecycle dedup tested where claimed (re-register identical schema → fingerprint match → same
SchemaDefinitionreturned, no version increment) - 13.7 No retroactive tick — every box above reflects work done in this change
14. Dependent sweep
- 14.1
bunx turbo test --filter=...@suaveplan/schema-registry— green;@suaveplan/events-schemacontinues to pass - 14.2 Search dependents for exhaustive
switchoverCompatibilityLevel:grep -rln "CompatibilityLevel" packages/ src/. For each hit, verify the switch handles the three new transitive arms (or falls through to the corresponding non-transitive arm) - 14.3 Search dependents for
SchemaRegistryconstructor:grep -rln "new SchemaRegistry\|createRegistry" packages/ src/. None should require code changes — the new option is additive
15. Archive
- 15.1 Fold
specs/schema-registry/spec.mdintoopenspec/specs/schema-registry/spec.md(this seeds the canonical capability spec, since one does not yet exist) - 15.2 Move
openspec/changes/schema-registry-completion/→openspec/archive/<yyyy-mm-dd>-schema-registry-completion/ - 15.3 Refresh
MANIFEST.md(this change adds@suaveplan/dbas a dep)
Audit Note (2026-05-02)
Audit verdict: DEFERRED — 0/78 tasks evidenced in code.
Evidence reviewed:
packages/core/schema-registry/src/has onlycore/{registry,compatibility},errors/,types/,constants/. Nocore/transitive/, nocore/diff/, nostore/, noutils/canonical/.types/schema-registry.types.tsCompatibilityLeveldoes not include*_TRANSITIVEarms (verified by absence of any matching directory or test).- No
@suaveplan/dbdependency inpackage.json(DatabaseSchemaStore depends on it).
Missing pieces:
- Transitive compatibility (
core/transitive/) and the seven-modeCompatibilityLevelwidening. - Schema diff engine (
core/diff/) +formatDiffReport. - Canonical schema utilities (
utils/canonical/),SchemaStoreAdapterinterface,MemorySchemaStore,DatabaseSchemaStore<TAdapter>(requires adding@suaveplan/dbdep), the async surface onSchemaRegistry, and the two new error codes.
Decision: leave change open. New canonical spec file + dep graph change + async API surface require focused implementation work.