T-DHUF-entity-docs-migration-cleanup-ajv-removal
Status: closed/done · Impact: high · Complexity: large
Complete the entity-surface Zod migration: move the docs generator and every
remaining schema.json consumer onto the in-memory Zod schema, delete the 11
schema.json files + _common.json, drop the vestigial Python-style
error-parity goldens, amend the standards/decisions/conventions, and — the shared
final gate across both surfaces — remove ajv + ajv-formats from
package.json. This is child 3 of the three-way split of
T-QFTI-migrate-entity-schemas-to-zod-first (the epic) and lands last: it
depends on T-KESH-config-surface-sdlc-yaml-zod-safeparse (config off AJV, so
the shared package.json removal is safe) and
T-JO4I-entity-zod-schemas-validation-ops-swap (entity validation already on
Zod, so deleting schema.json and dropping the goldens leaves nothing broken).
After child 2 lands, entity validation and ops read Zod, but the docs generator,
entity discovery, and several JSON consumers still read the on-disk
schema.json / _common.json; the Python-style error-parity goldens still exist;
the standards/decisions/conventions still assert JSON-schema-of-record + AJV; and
ajv + ajv-formats are still in package.json (the config surface, child 1,
removed its AJV imports but the dependency line stays until both surfaces are
clean).
| Location | Role today |
|---|---|
plugin/lib/model/entities/<type>/schema.json (11 files) + plugin/lib/model/entities/_common.json | Still on disk after child 2 — read by the docs generator, entity discovery, and the JSON consumers below. To be deleted here |
plugin/lib/services/docs/data_model.ts | Reads each schema.json + _common.json directly (readJson) to derive entity type list, id-pattern prefix, and key-fields (props minus common) for the data-model page |
plugin/lib/services/docs/site.ts | Joins <type>/schema.json for per-entity reference pages (sourceRel, key-fields) |
plugin/lib/services/docs/site/projections.ts | Gates “is this an entity type” on existsSync(<type>/schema.json) |
plugin/lib/services/docs/site_data_model_template.eta, plugin/lib/services/docs/site_entity_template.eta, plugin/lib/services/docs/site_ref_index_template.eta | Prose/header references to schema.json as the frontmatter contract |
plugin/lib/model/entities/*/body-template.eta | Header-comment references to schema.json |
plugin/lib/configuration.ts | entity.schemaFile: "schema.json" — the surface filename constant every loader joins on |
plugin/lib/services/project/ops/setup.ts | globOneLevel(entitiesDir, "schema.json") to discover entity types during sdlc project setup |
plugin/cli/backlog_cli/create.ts, plugin/cli/backlog_cli/_common.ts | Join entities/backlog/schema.json to read the backlog contract |
.claude/skills/project-check/check_entities.ts | /project-check gate: loads each schema.json, asserts it’s valid Draft 2020-12, checks prose field/enum references against properties / status.enum |
tests/parity/validators/ (367 *.fixture.json) + tests/parity/validators.golden.test.ts + tests/parity/regen_validators.ts | Python-style error-parity goldens that lock the AJV 5-line failure-output format. The Python validators they mirrored are retired — vestigial |
plugin/lib/tests/validate_absorb_parity.test.ts + plugin/lib/tests/fixtures/validate-absorb/goldens/ | Validator-absorb goldens asserting the same Python-style failure format through sdlc entities validate |
docs/planning/standards/S-0005-entity-definition-contract.md | The standard. Rule table lists schema.json as “JSON-Schema frontmatter contract”; Deterministic-checks table names validate_frontmatter.ts (AJV) |
docs/planning/decisions/D-0004-entity-definition-architecture.md | ADR asserting schema.json as the validation+scaffolding source and JSON-Schema default keywords as the defaults home; describes the _common.json $ref architecture |
plugin/conventions/typescript-runtime.md | Lists ajv + ajv-formats as available runtime dependencies |
package.json | "ajv": "^8.17.1", "ajv-formats": "^3.0.1" dependencies. Removing them is gated on BOTH surfaces being off AJV (config via child 1, entity validation via child 2) |
Approach
Section titled “Approach”- Change
data_model.ts,site.ts, andprojections.tsto derive their entity inventory and per-entity facts fromzodToJsonSchema(EntitySchema)in memory (id-pattern prefix, key-fields = props minus common, status enum), instead of readingschema.json/_common.jsonoff disk. Update entity discovery (which today gates onschema.jsonpresence) to enumerate the schema registry from child 2. The existing rendering logic survives almost unchanged — it never hand-walks Zod’s_definternals. - Repoint the remaining
schema.jsonconsumers:configuration.ts(entity.schemaFile),backlog_cli/{create,_common}.ts,project/ops/setup.ts, and.claude/skills/project-check/check_entities.ts(validate field/enum prose references against the Zod schema instead of the JSON). - Update the
.etatemplates (site_data_model_template,site_entity_template,site_ref_index_template) and the per-entitybody-template.etaheader comments so theirschema.jsonreferences point at the Zod schema. - Delete the 11
schema.jsonfiles and_common.json. Optionally add an on-demandsdlc-verb / script that writes a generatedschema.jsonexport for autocomplete, off by default (not a checked-in artifact). - Drop the Python-style error-parity goldens (
tests/parity/validators/**,validators.golden.test.ts,regen_validators.ts, the validate-absorb goldensvalidate_absorb_parity.test.ts+ itsgoldens/). The Zod-error assertions added in child 2’sentity.test.tsalready cover the failure cases. - Amend S-0005 in place (Rule + Deterministic-checks tables read Zod-first);
update D-0004’s
schema.json/_common.json/AJV assertions to Zod-first; dropajv/ajv-formatsfromplugin/conventions/typescript-runtime.md. - Remove
ajv+ajv-formatsfrompackage.json. This is the shared gate — safe only because child 1 took config off AJV and child 2 took entity validation off AJV. - Regenerate the docs site (
sdlc docs generate site) so the data-model and per-entity reference pages reflect the in-memory Zod derivation, and confirmsdlc docs generate --checkexits clean.
Files to touch
Section titled “Files to touch”| Location | Kind | Change |
|---|---|---|
plugin/lib/services/docs/data_model.ts | modify | Derive from zodToJsonSchema(EntitySchema) in memory, not readJson |
plugin/lib/services/docs/site.ts | modify | Per-entity facts from the in-memory schema; drop <type>/schema.json joins |
plugin/lib/services/docs/site/projections.ts | modify | Entity-type gate off the schema registry, not schema.json presence |
plugin/lib/services/docs/site_data_model_template.eta | modify | Prose references to schema.json → Zod |
plugin/lib/services/docs/site_entity_template.eta | modify | Prose references to schema.json → Zod |
plugin/lib/services/docs/site_ref_index_template.eta | modify | Prose references to schema.json → Zod |
plugin/lib/model/entities/*/body-template.eta | modify | Header-comment references to schema.json → Zod |
plugin/lib/configuration.ts | modify | Retire/repoint entity.schemaFile: "schema.json" |
plugin/lib/services/project/ops/setup.ts | modify | Discover entity types via the registry, not globOneLevel(..., "schema.json") |
plugin/cli/backlog_cli/create.ts | modify | Resolve the backlog contract from the Zod schema |
plugin/cli/backlog_cli/_common.ts | modify | Resolve the backlog contract from the Zod schema |
.claude/skills/project-check/check_entities.ts | modify | Validate field/enum prose references against the Zod schema |
plugin/lib/model/entities/_common.json | delete | Replaced by the Zod base (child 2’s _common.ts) |
plugin/lib/model/entities/backlog/schema.json | delete | Replaced by the Zod schema |
plugin/lib/model/entities/capability/schema.json | delete | Replaced by the Zod schema |
plugin/lib/model/entities/decision/schema.json | delete | Replaced by the Zod schema |
plugin/lib/model/entities/driver/schema.json | delete | Replaced by the Zod schema |
plugin/lib/model/entities/milestone/schema.json | delete | Replaced by the Zod schema |
plugin/lib/model/entities/principle/schema.json | delete | Replaced by the Zod schema |
plugin/lib/model/entities/product/schema.json | delete | Replaced by the Zod schema |
plugin/lib/model/entities/reference/schema.json | delete | Replaced by the Zod schema |
plugin/lib/model/entities/standard/schema.json | delete | Replaced by the Zod schema |
plugin/lib/model/entities/task/schema.json | delete | Replaced by the Zod schema |
plugin/lib/model/entities/term/schema.json | delete | Replaced by the Zod schema |
tests/parity/validators/ | delete | Vestigial Python-style error-parity fixtures (367 files) |
tests/parity/validators.golden.test.ts | delete | Vestigial Python-style error-parity goldens |
tests/parity/regen_validators.ts | delete | Regenerator for the vestigial goldens |
plugin/lib/tests/validate_absorb_parity.test.ts | delete | Python-style format goldens through the op |
plugin/lib/tests/fixtures/validate-absorb/goldens/ | delete | Golden fixtures for the validate-absorb parity test |
docs/planning/standards/S-0005-entity-definition-contract.md | modify | Amend in place: every entity ships a Zod schema; validation + docs derive from it |
docs/planning/decisions/D-0004-entity-definition-architecture.md | modify | Update schema.json/_common.json/AJV assertions to Zod-first |
plugin/conventions/typescript-runtime.md | modify | Drop ajv/ajv-formats from the available-deps list |
package.json | modify | Drop ajv + ajv-formats (shared gate: both surfaces now off AJV) |
site/src/content/docs/planning/ | modify | Regenerated data-model + per-entity reference pages (via sdlc docs generate site) |
Acceptance criteria
Section titled “Acceptance criteria”- AC-1: No remaining
schema.json/_common.jsonfiles underplugin/lib/model/entities/, and noajv/Ajv/ajv-formatsimport anywhere in the runtime except, at most, the optional on-demand JSON-export path. - AC-2:
ajvandajv-formatsare removed frompackage.json— the shared gate, now satisfiable because both the config surface (T-KESH-config-surface-sdlc-yaml-zod-safeparse) and entity validation (T-JO4I-entity-zod-schemas-validation-ops-swap) are off AJV. - AC-3: The docs site still renders the data-model page and per-entity
reference pages, deriving all entity facts from
zodToJsonSchema(EntitySchema)in memory —sdlc docs generate site --checkexits clean. - AC-4:
S-0005-entity-definition-contract.mdreads Zod-first (Rule + Deterministic-checks tables name the Zod schema and.safeParsepath, notschema.json+ AJVvalidate_frontmatter.ts); D-0004, the entity conventions doc, and the.eta/template references no longer assert JSON-schema-of-record or AJV. - AC-5:
/project-checkcheck_entities.tspasses, validating prose field/enum references against the Zod schemas; and the Python-style error-parity goldens (tests/parity/validators/**, the validate-absorb goldens) are gone. - AC-6:
bun testis green andbunx tsc --noEmitis clean after the JSON deletes, the goldens drop, and theajvremoval.
Out of scope
Section titled “Out of scope”- The entity Zod schemas themselves, the
CommonFrontmatterbase, the registry, and theentity.ts/validate.ts/audit.ts/migrate.ts/authoring.ts/schema_patterns.tsvalidation+ops swap — all delivered by T-JO4I-entity-zod-schemas-validation-ops-swap (child 2), which this task depends on. - The config surface (
sdlc.yaml) AJV retirement — delivered by T-KESH-config-surface-sdlc-yaml-zod-safeparse (child 1), which this task depends on. This task only removes the sharedajv/ajv-formatsdependency line frompackage.jsononce both surfaces are clean. - Persisting any generated JSON schema by default. The optional on-demand export (editor/YAML-LS autocomplete) is a stub left for if/when wanted; not a checked-in artifact, no drift test.
- A separate ADR for the decision. D-0004 is amended in place (the epic’s settled rationale); no new ADR is minted.
- The
schema_versionnumbering and any instance migration — an/sdlc:entities-migrateconcern after the epic lands.
Dependencies
Section titled “Dependencies”- T-KESH-config-surface-sdlc-yaml-zod-safeparse — must land first. The shared
package.jsonremoval ofajv/ajv-formatsis only safe once the config surface has no AJV imports. - T-JO4I-entity-zod-schemas-validation-ops-swap — must land first. Entity
validation must already run on Zod before the 11
schema.json+_common.jsoncan be deleted and the Python-style error-parity goldens dropped; this task migrates the docs/discovery/JSON-consumer half that child 2 deliberately left on the JSON path.
Discovery context
Section titled “Discovery context”This task is child 3 of the three-way delivery split of
T-QFTI-migrate-entity-schemas-to-zod-first and is the shared final gate.
Scope maps to the epic’s Approach steps 7–11 (docs generator + remaining JSON
consumers onto the in-memory Zod schema, the schema.json / _common.json
deletes, dropping the Python-style error-parity goldens, the
standards/decisions/conventions amendments) plus the shared ajv /
ajv-formats removal from package.json. It is gated on both sibling children
because the dependency removal needs config off AJV (child 1) and the
JSON-deletion + goldens-drop needs entity validation already on Zod (child 2).
The full decision rationale — full-Zod cut, no bridge, JSON as optional export
only, D-0004 amended in place — lives in the epic.
Post-mortem
Section titled “Post-mortem”Captured by /sdlc:task-work on 2026-06-13. PR: pending.
Acceptance criteria coverage
Section titled “Acceptance criteria coverage”- AC-1: auto —
grep -rn 'ajv|Ajv' plugin/ --include='*.ts'returns onlyT-DHUF-…provenance comments documenting the removal; no live import remains, andbunx tsc --noEmitis clean against the Zod-only tree. - AC-2: auto —
grep -c ajv package.jsonreturns0; the shared dependency line is gone now that both sibling children are off AJV. - AC-3: auto —
sdlc docs generate site --check(worktree CLI) reportsclean (no drift); the data-model and per-entity reference pages derive fromzodToJsonSchema(EntitySchema)in memory. - AC-4: agent-manual — inspected
S-0005-entity-definition-contract.md,D-0004, the entity conventions doc, and the.eta/body-template.etaheaders; Rule + Deterministic-checks tables now name the Zod schema and the.safeParsepath, and no doc/template asserts JSON-schema-of-record or AJV. - AC-5: auto —
/project-checkcheck_entities.tspasses against the Zod schemas, andtests/parity/validators/no longer exists (the Python-style error-parity goldens are deleted). - AC-6: auto —
bun testis green (the lone failure is the pre-existingsite_roadmapidempotency case already failing onorigin/main, unrelated to this task) andbunx tsc --noEmitis clean after the JSON deletes, the goldens drop, and theajvremoval.
What worked
Section titled “What worked”- The three-way split landed the dependency removal cleanly: with config
(child 1) and entity validation (child 2) already off AJV, deleting the 11
schema.jsonfiles +_common.jsonand droppingajv/ajv-formatsleft nothing broken —tsc --noEmitandentities auditwere clean first try. - The worktree CLI’s regenerated site matched the deterministic
--checkgate exactly, so AC-3 verification was a single command.
Friction and automation gaps
Section titled “Friction and automation gaps”- The runner stalled on the watchdog at the Wave-3 finalize commit due to a
rumdl-reflow ↔ docs-drift race: editing the task file reflows the source
(markdown-fmt hook) while also staling the derived site page
(
project-check-docs-drift), and the two hooks fought across the commit boundary — the working sequence is reflow source → regenerate site (worktree CLI) → stage task file + regenerated page together → commit. Already tracked upstream by the auto-regen fix T-PA51/#443; no new follow-up spawned. - Debugging gotcha: running the main checkout’s
sdlc.ts docs generate siteagainst this worktree’s data falsely reports a mass page deletion (~471 pages) because the main generator is still the oldschema.json-based one and the worktree’sschema.jsonfiles are deleted — always run the worktree CLI for docs commands during this migration. A documentation/ergonomics gap, not a code bug; folded into the T-PA51 thread rather than spawned separately. - Baseline quality-check run is slow on this repo (full corpus audit + test suite), adding latency to each gate pass — already tracked as B-6AHH; no new follow-up spawned.
Depends on
Section titled “Depends on”T-KESH-config-surface-sdlc-yaml-zod-safeparse, T-JO4I-entity-zod-schemas-validation-ops-swap