fix(spec): label every producer claim in the build-progress docblock — measured, ruled, or inferred - #18870
Conversation
The module docblock on `ai/build-progress.zod.ts` stated three producer claims as MEASUREMENTS. It ships inside the published `@objectstack/spec` tarball and is rendered verbatim into the generated reference page, and for a CLOSED vocabulary it is the audit trail the "re-measure before you move the array" discipline reads — so a reader deciding whether a fifth phase is warranted read all three as readings. One of them was false. Every producer claim now carries exactly one of three labels, defined at the top of the module: measured on a named reachable source / declared by ruling / inferred. - Membership is no longer described as uniformly measured. `structure`, `data` and `done` stay measured, cited with the tree they were read against; `verify` is declared by ruling (cloud#2172, objectui#7388), with the zero-occurrence reading and its bright control recorded beside it. - The cloud#1838 window (111 seconds, 9 tool calls, one of them `verify_build`) is inferred: that record is unreachable from this repo. What is measured is narrower and stated as such — `verify_build` is a registered platform tool. - The two skipped-phase illustrations are inferred; the consumer guidance around them is unchanged and does not rest on them. Adds a `## Liveness watch` section recording that `verify`, `hop` and `tool` are declared ahead of any code that uses them, who the named carriers are, and that no gate watches it — `BuildProgressFrame` is not a registered metadata type, so the ADR-0049 liveness ledger never sees it. No schema, export or parse behaviour moves. `content/docs/references/ai/ build-progress.mdx` is regenerated from the docblock by `gen:docs`. Claude-Session: https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3 Co-authored-by: Claude <noreply@anthropic.com>
…ild-progress-docblock-provenance
📓 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 32da22366f5ac3616aea45df34e585a0652d591d && git checkout 32da22366f5ac3616aea45df34e585a0652d591d
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 36583e989b0b598f7974a275445655bc2989d763 5d12a8885045c32b3dc26892ecaec9481b853d33 && git checkout -B drift-repro 36583e989b0b598f7974a275445655bc2989d763 && git merge --no-ff 5d12a8885045c32b3dc26892ecaec9481b853d33
node scripts/docs-audit/affected-docs.mjs --json 36583e989b0b598f7974a275445655bc2989d763 |
Fixes #18552
Clause-②: no
The module docblock on
packages/spec/src/ai/build-progress.zod.tsstated three producer claims as MEASUREMENTS. It ships inside the published@objectstack/spectarball and is rendered verbatim intocontent/docs/references/ai/build-progress.mdx, and for a CLOSED vocabulary it is the audit trail the "re-measure before you move the array" discipline reads. One of the three was false forverify. Every producer claim now carries exactly one of three labels, defined at the top of the module: measured on a named reachable source / declared by ruling / inferred.⛔ Nothing about the vocabulary or the schemas moves.
BUILD_PROGRESS_PHASES,BuildProgressPhaseSchemaandBuildProgressFrameSchemaaccept and refuse exactly what they did before — measured, below.Clause-② declaration
no, on the authority the dispatch cited:references/lanes/spec.md— only widening the accepted set or enlarging the public surface is clause ②. Neither happens here. Measured both halves rather than asserted:check:api-surfaceis green with the artifact untouched (no export added, removed or renamed), and the parse-behaviour matrix below is byte-identical before and after.Claim-by-claim
verifyat this head — the reading is belowstructure,data,done— the consumer's own declared union …"dda8f3815:ChatbotEnhanced.tsx:163andmapMessages.ts:743verify— the post-apply verification window objectui#7388 asks the panel to be able to name …"verify_buildtool thatservice-ai-studioactually registers"packages/spec/src/system/constants/platform-tool-names.ts:85apply_blueprintreturned"objectstack-ai/cloudis not reachable from this session's credential, so the figure cannot be re-measured from this repo. ⛔ Nothing was deletedverify_buildtool"verify_buildtool' is inferred as well"ctx.onProgresshandle dies when the tool returns, so a frame emitted in that window can only come from the loop"AIToolContext.onProgressin../contracts/ai-servicedeclares the emit as happening WHILE the tool executes, before it returns; that the handle is then gone is the deductiondata"apply_editturns need not reportstructure"hop: "the counter behind the '9 tool calls' cloud#1838 measured"The consumer guidance around the two skipped-phase illustrations is unchanged and does not rest on them: treat every phase as optional, compare by value.
The reading behind (a), re-taken here with a bright control
Taken in this session, in the local
objectstack-ai/objectuicheckout at commitdda8f3815df6eb92e944a33745070e361e8d6e40, one instrument:The control fires, so the zero is a real zero and not a dead instrument. Confirmed at the two symbols:
ChatbotEnhanced.tsx:163—phase: 'structure' | 'data' | 'done';mapMessages.ts:743—const phase = d.phase === 'data' || d.phase === 'done' ? d.phase : 'structure';Repo-wide in objectui,
'verify'occurs twice, both inapp-shell/src/console/RemediationOverlay.tsxas a password-remediation step — unrelated to build progress. On this side,data-build-progressoccurs in 4 files, every one of them this module's own declaration, its test, theai/index.tsline and thecontracts/ai-serviceexample (control:onProgressoccurs in 10 files) — zero emitters.mapMessages.ts:744; at the tree read here the line is 743. The citation now carries the tree it was read against, per the "a reading is a count plus the tree" rule.DARK — the parse behaviour reads 0 change
A 29-line probe over
BuildProgressPhaseSchemaandBuildProgressFrameSchema(14 phase inputs, 13 frame inputs, plus the exported array and frame-type literals), run against the source before the edit and again after:Covered on both sides: all four declared phases parse;
designing/rebuilding/VERIFY/''/3/1.5/null/undefined/{}/['verify']are refused withinvalid_valuecarrying the full accepted set; the frame refuseshop: -1(too_small),hop: 1.5(invalid_type),tool: ''(too_small), an undeclared phase located at['phase'], and a frame with no phase; the shipping panel fields (appLabel,items,done,total,seq) still survive the loose parse.packages/spectest suite: 487 files / 14058 tests, all passing — every pre-existing assertion inbuild-progress.test.tsuntouched.Generated-artifact reconciliation
gen:docswrites 224 files. After it, exactly one is modified:The
.mdxdiff is 27 insertions of the two module-docblock sections (## How to read the claims in this module,## Liveness watch) and the rewritten Producer paragraphs, and 8 deletions of the paragraph they replace. Nothing else in the page moves — the schema tables, the allowed-values list and the TypeScript-usage block are byte-identical. TheBUILD_PROGRESS_PHASESand field docblocks are not rendered bybuild-docs.ts, which is why (a) and (c) produce no.mdxchange at all.content/docs/references/**is on themerge=os-regenroster (verified here:git check-attr mergeanswersos-regenfor this path, andunspecifiedfor the hand-written.zod.tsbeside it — the instrument discriminates).origin/mainwas merged in throughbash scripts/pm/os-regen-merge.sh; it kept the branch's bytes because main did not touch the page, and the regeneration was re-run and re-checked afterwards.The card's second job — the liveness watch
verify,hopandtoolhave zero emitters and zero readers in every reachable repository. That is declared-ahead by ruling, not neglect, and it sits outside the ADR-0049 liveness ledger (registry-rooted;BuildProgressFrameis not a registered metadata type, so the liveness job is green). A new## Liveness watchsection in the docblock records all three: what they stand on, who is supposed to close it (cloud#2172 for the emitter, objectui#7388 block 2 for the consumer's strict parse), and that no gate watches it. ⛔ No enum member moves and no enforce-or-remove is attempted here.Tests
Three added, none changed.
describe('module docblock provenance')pins the narrow, load-bearing part and ⛔ not the wording: that the three labels are defined, that theverifybullet carries declared by ruling and the retired headline is gone, and that the liveness watch is still present with its carriers. It is the only leg in the file whose subject is text, and the docblock explains why that is legitimate here — the text ships and is rendered, and for a closed enum it is the audit trail.Gates
check:generated— all 15 green, includingcheck:docs,check:api-surface,check:authorable-surface. Tree clean after the run (no regeneration drift).check:api-surfacedid not move, which is the expected reading: no export surface was touched.pnpm --filter @objectstack/spec typecheck— green (check:scripts-typecheck+check:test-typecheck, ledger unchanged).pnpm --filter @objectstack/spec test— 487 files / 14058 tests green.pnpm lint(repo-wideeslint . --no-inline-config) — green, exit 0, at this head. Not narrowed.node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstackproduced 106 commands; all 106 accounted for, 103 run green, 0 unrun, plus the 6 artifact-roster families whose roster sits under a directory one of these paths is in. 3 are NOT MEASURED (exit 3, PREREQUISITE NOT MET:check:dual-build-cjs-loads,check:lean-entry-closure,check:type-check-debteach need the whole workspace's built output, which this container cannot build inside one foreground window). Those three read other packages' emitted bytes and cannot be moved by a docblock; CI owns them.Acceptance notes
.changeset/build-progress-phase-vocabulary.mdwas left untouched. Re-read at the start of this round: the file is still onorigin/main, and PR chore: version packages #17076 (chore: version packages) is still open and unmerged, and holds a deletion of it. Its "never reportsdata" sentence therefore still wants the same softening, and this branch is not the place to contend for that file. Re-measured with a bright control:git grep -c "never reports" -- '*CHANGELOG.md'returns zero hits across the 80 CHANGELOG files the instrument sees, and so does the whole-sentence controldata-build-progress, whilechangesethits many of them — so the sentence has NOT been compiled into any published CHANGELOG yet. Naming it for the dispatching seat to judge, as instructed.packages/spec/src/ai/build-progress.test.tscarries the same retired claim in its own words — "Each member was measured against a real end of the channel". It is the identical defect class, it predates this branch, and it does not ship (the tarball carries.zod.tssources, not tests) nor render into any page. It was left alone because the dispatch fenced that file and the seat's file-surface declaration cannot be extended from here. Carrier: whoever next edits that pin. Dedupe words:build-progress test docblock measured claim,verify phase zero emitters,closed enum audit trail re-measure.Generated by Claude Code