Repository navigation
fix(spec): JobSchema's own @example no longer opens with the retired id key - #19296
Conversation
`JobSchema`'s TSDoc `@example` opened with `id`, removed in 17.0.0 and tombstoned a dozen lines below the block that wrote it, so the example the schema publishes was refused by that schema on a verbatim copy — measured: `safeParse` answers `success: false` with `unrecognized_keys: ["id"]` and the `JOB_ID_RETIRED` prescription as the message. Deleting the line is the whole fix: the same object with `id` removed parses. The pin reads the block out of the source instead of restating it, and carries a firing control (re-adding `id` must still be refused, with the prescription) so it cannot pass vacuously. Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 Co-authored-by: Claude <noreply@anthropic.com>
Measured rather than assumed: `packages/spec` publishes `src/**/*.zod.ts` in its `files`, and `npm pack --dry-run` lists `src/system/job.zod.ts` in the tarball, so the corrected block is shipped content. The generated artifacts do not move — `pnpm --filter @objectstack/spec build` leaves the tracked authorable-surface and JSON-Schema output byte-identical — so the accept set is unchanged and the declaration is `Clause-②: no`. Claude-Session: https://claude.ai/code/session_01AmH9bKvGoLjiY86Q4Z3og2 Co-authored-by: Claude <noreply@anthropic.com>
📓 Docs Drift Check1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to list — not a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run. What this run could not see
Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): Which tree this was computed onThis run read A worktree cut from an older # while this PR is open — GitHub drops the merge commit once it closes
git fetch origin d562f112e8d859af64081afc21be9c94a6b96a8d && git checkout d562f112e8d859af64081afc21be9c94a6b96a8d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4efb988a941056d6099b680924fa41807292115d cf3e8107b12b8e4a34a5c635546be756135d165a && git checkout -B drift-repro 4efb988a941056d6099b680924fa41807292115d && git merge --no-ff cf3e8107b12b8e4a34a5c635546be756135d165a
node scripts/docs-audit/affected-docs.mjs --json 4efb988a941056d6099b680924fa41807292115d |
Contract reviewServed-tier: 80/80 Isolated at-tier review of PR #19296 (card #19184), read off the PR body, diff, commits and check runs, the card body, the ① Derived judgmentsDiff vs merge-base
② Semver level
③ Boundary flags
Independence and landing inputs: the dev was a Implemented-by: VERDICT: PASS Generated by Claude Code |
Fixes #19184
Clause-②: no
JobSchema's TSDoc@exampleopened withid, the key removed in 17.0.0 (#4667, ADR-0049) and tombstoned a dozen lines below the block that wrote it. The repository's own example was therefore refused by the schema it documents on a verbatim copy. One line is deleted; the schema, the tombstone and its prescription are untouched.The two readings the card marked NOT measured by the filer
Both were attributed rather than adopted on the card, and both were re-run here, first-hand, on this branch.
1. Parse side — confirmed. The example was extracted from the source by anchor (not retyped) and parsed:
Control leg, same probe, same run: the identical object with
iddeleted answerssuccess: true. Soidwas the only defect in that block — the cron expression string,timezone: "UTC", the handler string and bothretryPolicykeys all parse — and deleting the line is the entire fix.2. Blast radius — confirmed, three ways. The block is source TSDoc and reaches no generated page:
content/docs/references/system/job.mdxcarries the.describe()text of this very file verbatim (positive control: thetimezonedescription, matched byte for byte) and carries no part of the@example. Grep forMetadata Sync Job (Cron)andsync_metadata_nightlyovercontent/docs/: zero hits, against a firing control (timeoutMsmatches five reference pages).pnpm --filter @objectstack/spec check:generatedreports all 16 generated artifacts up to date after the edit —check:docsamong them — so the change moves no generated byte.disteither: the distinctive phrase of the block has zero hits underpackages/spec/dist/after a full build, while the siblingdefineJobexample (a fencedtsblock) is present in the emitted.d.ts. The block travels to consumers through the tarball's source file, not the declarations.Why one block and not a family — re-derived here
The file's second
@example, ondefineJob, was extracted and tested by the same probe: it carries noid, notimeoutand noretryDelayMs. It is untouched, as isJOB_ID_RETIREDand every other retirement mechanism. The package-wide@examplequestion is a separate card and is deliberately not started here.Worth recording, because it is why this survived:
check:skill-examplestype-checks prose examples across three surfaces, including spec source TSDoc — but its population is fenced code blocks (ts / tsx / typescript), 10 of them in spec sources. This@exampleis a bare object literal with no fence, so no instrument in the repo reads it. The fix is the example; the gap is noted, not widened.The pin
JobSchema's own @example (#19184)inpackages/spec/src/system/job.test.tsreads the block out of the source rather than restating it — a restated copy is a second declaration and rots green. It carries an extraction assertion (an unmatched anchor fails loudly instead of parsing an empty string) and a firing control: re-addingidmust still be refused withunrecognized_keysnamingidand a message carrying the version and the replacement identity. Without that leg a pin that accepts anything would read as green.Changeset — measured, not assumed
patchon@objectstack/spec, andskip-changesetwas measured wrong rather than skipped by convention:packages/specis public at 17.4.0 and itsfileslist includessrc/**/*.zod.ts.npm pack --dry-run --jsonlistssrc/system/job.zod.tsamong the 2042 files of the tarball. The corrected block is shipped content, so the diff publishes.Clause-②: nois a measurement too: a fullpnpm --filter @objectstack/spec buildleaves every tracked generated artifact byte-identical (git statusclean), andcheck:authorable-surfaceis green — the accept set does not move, because only a comment changed.Verification
Run on
cf3e8107b1, the final commit of this branch.pnpm --filter @objectstack/spec exec vitest run --project local src/system/job.test.ts— 59 passed (55 before, 4 new).pnpm --filter @objectstack/spec typecheck— exit 0;check:test-typecheckOK with the debt ledger unmoved (54 files / 259 errors / 144 pinned signatures, shrink-only).pnpm --filter @objectstack/spec check:generated— 16 of 16 artifacts up to date.pnpm --filter @objectstack/spec check:skill-examples— 258 prose examples type-check across 3 surfaces (first run refused for an unbuiltclient-react/dist; re-run after building that package, which is what the refusal asked for).check:nul-bytes,check:cross-package-test-inputs,check:test-source-alias,check:spec-docblock-symbol-anchors,check:comment-mask-adoption,check:comment-mask-corpus,check:keyed-text-bounds,check:changeset-gate-self-tests,check-changeset-no-major --base origin/main,check-adr-0087-registration --base origin/main,check:published-files,check:doc-authoring,check:objectui-changeset,check:pm-changeset-deadline-census, and the spec source auditscheck:duration-unit-keys,check:yaml-examples,check:empty-state,check:exported-any,check:variant-docs,check:dual-source-exports,check:entry-nameability,check:browser-reachable-entries,check:llms-txt,check:objectui-pin-citations.parserOptions.project, stated in the config itself), so this diff cannot move the verdict on a file it does not touch; the repo-wide sweep stays CI's.Declared narrowings, so the reviewer reads coverage rather than infers it:
scripts/pm/dispatch-gates.mjs --ranreconciles 28 of 81 derived families as run here; the remaining 53 are repo-wide ratchets and whole-tree sweeps that CI owns. The clause-② level axis ofcheck-changeset-no-majorreports NOT APPLICABLE locally — it reads apull_requestpayload, so this PR's own run is its first real reading. The branch is two commits behindmainat the time of writing, anddispatch-gatesflags its own derivation stale forci.ymlon that account.Acceptance notes
Noted while measuring, not fixed here, not filed by this PR:
handler: "services/syncStatus.ts:syncAll"parses (the key is a plain string) but names something the runtime cannot resolve:AppPluginresolves a job handler as a straight key lookup intobundle.functions, and a miss logsjob handler not found in bundle.functions — skippingatwarnand schedules nothing. The working spelling is a bare function key —handler: 'sweepProjectHealth'inexamples/app-showcase, and the siblingdefineJobexample in this very file. Same block, different failure mode from the card's (accepted at parse, inert at boot), so it is reported rather than ridden in on this PR.handlerline also carries a trailing space. Style only; left alone.🤖 Generated with Claude Code
Generated by Claude Code