Skip to content

spec(ai): declare the closed build-progress PHASE vocabulary the agent loop emits during post-apply verification - #18528

Merged
os-litant merged 4 commits into
mainfrom
claude/issue-18451-build-progress-phase-vocabulary
Sep 17, 2026
Merged

os-litant merged 4 commits into
mainfrom
claude/issue-18451-build-progress-phase-vocabulary

Conversation

@os-warren

@os-warren os-warren commented Sep 16, 2026

Copy link
Copy Markdown
Collaborator

Fixes #18451

Clause-②: yes (widening)

Declares the build-progress PHASE vocabulary on @objectstack/spec/ai, executing ruling A on
objectstack-ai/cloud#2172. The data-build-progress frame has shipped as prose only —
AIToolContext.onProgress documents the channel (contracts/ai-service.ts:639) and its example
carries a phase (:644) — while nothing ever declared which phases exist.

Membership was measured, not designed

The ruling names a CLOSED enum, so each member is sourced. Provenance is recorded in the module
itself, per member:

member where it came from
structure, data, done the consumer's own declared union ChatBuildProgress['phase'] at packages/plugin-chatbot/src/ChatbotEnhanced.tsx:163 in objectui, and the set its reader discriminates at packages/plugin-chatbot/src/mapMessages.ts:744 — `d.phase === 'data'
verify the post-apply verification window objectui#7388 asks the panel to be able to name, and the reason cloud#2172 ruled the vocabulary into the spec. Corroborated in THIS repo by the verify_build tool service-ai-studio actually registers (packages/spec/src/system/constants/platform-tool-names.ts).

What I could not source, and therefore did not add: the producer's own emission list. The
ruling lives on objectstack-ai/cloud#2172 and the cloud repository is not reachable from this
session — the ruling text here is taken from the director seat's filing on the card, verbatim, and
the cloud issue is NOT reported as checked. No member was rounded out to fill that gap.
designing was considered and REJECTED: it belongs to the sibling data-blueprint-progress
frame, a different channel with its own reader, and a test asserts it does not parse here.

What this adds

  • BUILD_PROGRESS_PHASES / BuildProgressPhaseSchema / BuildProgressPhase — the closed
    vocabulary in lifecycle order. An out-of-vocabulary value is refused and the refusal names the
    accepted set.
  • BuildProgressFrameSchema / BuildProgressFrame — the frame's FLOOR: a required phase plus
    an optional hop and tool. Deliberately z.looseObject, not strict: the panel fields the
    consumer already reads (appLabel, items, done, total, seq) ride the same frame and are
    objectui's to shape, so a strict schema here would refuse every frame shipping today.
  • BUILD_PROGRESS_FRAME_TYPE — the one literal both ends select on.

hop is typed z.number().int().nonnegative() rather than pinned to a base on purpose: whether
the loop counts its first hop as 0 or 1 is part of the emitter's placement, which cloud#2172 owns
and this card explicitly does not decide. tool is a free string, not a closed set, because the
executable tool set is registered at boot and legitimately includes plugin-contributed names
PLATFORM_PROVIDED_TOOL_NAMES cannot know about.

api-surface reads 6 added / 0 removed on ./ai — purely additive, which is what the minor
changeset and the Clause-②: yes (widening) declaration record.

Tests

Everything below was run on final head 3233eea673, after pnpm --filter @objectstack/spec build.

  • New pin packages/spec/src/ai/build-progress.test.ts — 16 tests, exit 0.
  • Affected package pnpm --filter @objectstack/spec testTest Files 484 passed (484),
    Tests 13789 passed (13789), VERDICT command-exit 0.
  • Typecheck pnpm --filter @objectstack/spec typecheck — exit 0. Note the main tsc --noEmit
    program does NOT include **/*.test.ts (verified with --listFiles: 0 hits for both test
    files, 1 for the source); the test layer is covered by the tsconfig.test.json leg, where a
    grep for my files over the raw error output returns zero hits with a lit control finding 5
    elsewhere. That is what proves the two new ADR-0122 assertions.
  • Derived gates node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack
    derived 108 families; all 108 were run on this head and reconciled with exit codes via
    --ran: "108 derived, 108 run, 0 NOT-MEASURED, 0 UNRUN". 107 exit 0.
  • Repo-wide pnpm lint (eslint . --no-inline-config) — exit 0, run in full, not narrowed.

The one non-zero, and why it is not this diff

pnpm check:cross-package-test-inputs exits 1, naming
packages/cli/test/init-created-files-summary.e2e.test.ts descending packages/spec/dist/.
This diff touches neither that test nor packages/cli. Two legs on a throwaway worktree at
pristine origin/main (97233b90ca), with zero of my source changes:

  • no packages/spec/dist/ present: exit 0
  • the same tree with only a built packages/spec/dist/ copied in: exit 1, byte-identical finding
  • that dist/ removed again: exit 0

So the trigger is the presence of a gitignored build output — which AGENTS.md requires before the
dist-reading gates — not this change. Filed as an out-of-scope finding in the report.

Ablation — the pin is not vacuous

Run from the committed state, each leg proving the mutation reached disk (occurrence counts of
both the removed and the injected text, plus a blob hash differing from the HEAD blob) before any
verdict was read, and restoring by observed state (git checkout HEAD -- path, blob hash back to
53034bc672c8de6dbe800cd923bba8b7f237c66d, git diff HEAD empty) rather than by exit code:

mutation result
z.enum(BUILD_PROGRESS_PHASES) becomes z.string() — the vocabulary stops being closed 3 failed / 13 passed: all three refusal cases red, every known-phase control still green
z.looseObject becomes z.strictObject — the floor becomes a ceiling 1 failed / 15 passed: exactly the case asserting the consumer's shipping panel fields survive

Acceptance notes

File-surface accounting — all 17 files, in three buckets

The claim declared packages/spec/src/ai/**, the contracts/ai-service.ts docblock, the generated
api-surface / export-origins / reference-page artifacts, and .changeset/. Nothing here is a
hand edit I chose
— bucket 3 is empty.

Bucket 1 — generated (10 files). Proven, not asserted: every one was reverted to the merge base
fb6b2c369e and re-produced by re-running the generators on that tree
(check:generated --fix wrote gen:schema, gen:api-surface, gen:export-origins, gen:docs,
gen:strictness-ledger; gen:declaration-map was run explicitly because the aggregate does not
flag it when its companion is reverted in the same stroke). git diff HEAD over all ten came back
empty — byte-identical reproduction — and the tree was then restored with
git restore --source=HEAD --staged --worktree, index and tree both clean.

file generator that wrote it
packages/spec/api-surface/ai.json gen:api-surface
packages/spec/export-origins/ai.json gen:export-origins
packages/spec/declaration-map/ai.json gen:declaration-map
packages/spec/authorable-surface/ai.json gen:schema (via check:authorable-surface)
packages/spec/json-schema.manifest/ai.json gen:schema
docs/audits/2026-07-unknown-key-strictness-ledger.counts.md gen:strictness-ledger
content/docs/references/ai/build-progress.mdx (new page) gen:docs
content/docs/references/ai/index.mdx gen:docs
content/docs/references/ai/meta.json gen:docs
content/docs/references/index.mdx gen:docs

Bucket 2 — required by a gate, with the gate's own red quoted (3 files).

  • packages/spec/src/type-alias-convention.pin.test.tspnpm check:spec-parsed-alias red:
    "BuildProgressFrame is the AUTHOR state of BuildProgressFrameSchema and nothing names its
    PARSED state. Declare export type BuildProgressFrameParsed = … next to it … or, if z.input
    and z.infer of BuildProgressFrameSchema are the same type, pin it in
    packages/spec/src/type-alias-convention.pin.test.ts instead."
    Both schemas are isomorphic, and
    that file's own header states why the pin beats a synonym: "a permanent synonym is a name an
    author can only pick wrongly."
    So this is the ADR-prescribed route, not a preference — the file
    is also the gate's machine-readable exemption registry, which is why the registration must live
    there and nowhere else.
  • packages/spec/llms.txtpnpm check:llms-txt red:
    "[count] domain ai declares 11 schemas; src/ai/ holds 12" and "heading declares 200
    schemas; packages/spec/src/ holds 201"
    . Hand-kept by design — the gate says "there is
    deliberately no gen:llms-txt"
    — and it ships to AI consumers inside the npm tarball.
  • content/docs/getting-started/quick-reference.mdxpnpm check:quick-reference-counts red:
    "section "AI Protocol" declares "of 11 schemas" but content/docs/references/ai/ publishes
    12 page(s)"
    . Reduced to the gate-required minimum: a one-line 11 of 11 to 11 of 12. An
    earlier revision of this branch also added a table row for the new page; that was discretionary
    polish, not needed for correctness, and it was dropped — the section now carries the same
    partial shape the API section already has at 17 of 31.

Bucket 3 — my own judgement calls: none.

If every path above reads as bucket 1 or 2, the claim's surface wording was narrower than what one
new exported schema mechanically forces in this tree.

  • contracts/ai-service.ts was NOT edited. The claim allowed its docblock "if the frame
    example should name the enum". The new module's own docblock points at that file, and leaving
    the contract file untouched keeps the diff off a path a sibling PR is in (email-service.ts,
    PR docs(spec): scope the email-template locale-floor claims to a call that names a locale #18482, is the only contracts/ file in flight). Naming the enum from the onProgress
    docblock is a good follow-up, not a requirement of this card.
  • No XParsed aliases were added. Both schemas are isomorphic, and ADR-0122's pin file states
    the reason a permanent synonym is worse than none: it is a name an author can only pick wrongly.
    The pins are the prescribed route and tsc proves them.
  • Not merged with main. The branch is based on fb6b2c369e. Independence was verified for
    these paths, and the three hottest spec artifacts are sharded per domain precisely so parallel
    spec PRs stay textually disjoint. The merge queue rebuilds on the merged generation. Declared
    rather than assumed.
  • Noted, not filed: the sibling extractBlueprintProgress reader in objectui collapses an
    unknown data-blueprint-progress phase to 'designing' by the same pattern. It is out of scope
    here and was already ruled on in objectui#7388 (its docblock makes "only done is
    authoritative" a contract, so the posture is deliberate). Carrier: whoever picks up the
    data-blueprint-progress vocabulary, if that is ever ruled.

Scope this card does not decide

Per the card: not the UI copy for each phase (objectui#7388's), and not the emitter's placement in
the cloud agent loop (cloud#2172's).

Authored by the domain:spec execution seat, session session_01KB5PFtxuy1x3dcR5gxudx6.


Generated by Claude Code

…ent loop emits during post-apply verification

The `data-build-progress` frame has shipped as prose only: `AIToolContext.onProgress`
documents the channel and its example carries a `phase`, but no declaration ever said
which phases exist. The consumer filled that gap by guessing — objectui's
`extractBuildProgress` coerces any value it does not recognise to `structure`, which
renders a "still building" spinner, so the 111 seconds a build turn spends being
verified after it finished read as the wrong phase rather than an unknown one.

Declare the vocabulary where both ends can read it:

- `BUILD_PROGRESS_PHASES` / `BuildProgressPhaseSchema` — a CLOSED enum whose members
  were measured, not designed. `structure`/`data`/`done` are the consumer's own
  declared union; `verify` is the post-apply window, corroborated here by the
  `verify_build` tool `service-ai-studio` actually registers. Per-member provenance
  is recorded in the source.
- `BuildProgressFrameSchema` — the frame's floor: a required `phase` plus an optional
  verification hop counter and tool name. Deliberately loose, because the panel fields
  the consumer already reads ride the same frame and are its to shape.
- `BUILD_PROGRESS_FRAME_TYPE` — the one literal both ends select on.

The docblock names producer (the cloud agent loop — not the tool, whose `onProgress`
handle dies when it returns) and consumer (the objectui chat panel). Where in the loop
the frames are emitted, and what copy each phase gets, stay with their owners.

Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com>
…move, and declare the changeset

`check:generated` proved exactly five stale: api-surface, export-origins, declaration-map,
the rendered reference pages, and the unknown-key strictness ledger. Regenerated only those.

api-surface reads 6 added / 0 removed on `./ai` — a purely additive published surface, which
is what the `minor` and the `Clause-②: yes (widening)` declaration record.

Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com>
…and correct the counts a new schema moves

Three gate-named corrections, all mechanical consequences of the new export:

- ADR-0122 (`check:spec-parsed-alias`): the bare alias is the AUTHOR state, so
  `BuildProgressPhase` becomes `z.input`. Both schemas are isomorphic — no default,
  no transform anywhere in either tree — so neither gets a redundant `XParsed`
  synonym; they are pinned in `type-alias-convention.pin.test.ts` instead, which is
  the route the ADR prescribes and the gate reads as its exemption registry. tsc is
  what proves the two new pins, and the pin count moves 783 -> 785 in all three
  places that state it.
- `check:llms-txt`: the hand-kept inventory ships to AI consumers inside the tarball;
  `ai` 11 -> 12 and the total 200 -> 201, with the domain's subject list naming the
  new module rather than only its count.
- `check:quick-reference-counts`: the AI section was complete at 11 of 11, so it stays
  complete at 12 of 12 with a row for the new reference page.

Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions

github-actions Bot commented Sep 16, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

6 anchor(s) derived from 1 changed package(s); no hand-written page names any of them. ⚠️ 7 changed file(s) yielded no anchor (packages/spec/api-surface/ai.json, packages/spec/authorable-surface/ai.json, packages/spec/declaration-map/ai.json, …), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files.

What this run could not see
  • 7 changed file(s) yielded no anchor (packages/spec/api-surface/ai.json, packages/spec/authorable-surface/ai.json, packages/spec/declaration-map/ai.json, …) — pages documenting those are invisible to this run
  • the SDK route bridge reached 60 of 215 client-bound route-ledger rows — the other 155 have no registrar path: tail to select them, so pages documenting THEIR client methods cannot appear above, on this or any run. Of those 155: 0 are remediable by widening that discovery convention (an in-repo file declares the path; the convention did not scan it); 55 are structural — on a ledger where NOT ONE row is declared in-repo, so no discovery change reaches them at any price; 100 are undecided (no in-repo declaration, on a ledger that has other in-repo registrars — absence and an unreadable spelling are not distinguishable here). The rows themselves: node scripts/docs-audit/affected-docs.mjs --bridge-coverage
  • a page that states a rule by its inputs shares no identifier with the emitter that implements the rule, so an emitter-only diff cannot list it — not on this run and not on any run. Measured on fix(driver-sql): emit varchar(maxLength) for a text field a declared index keys on #11430: content/docs/protocol/objectql/types.mdx documents the text-family column mapping by the ObjectQL type names it maps FROM (text / textarea / html) while the diff changed createColumn; it went unlisted, and it was the page that diff falsified, in four places. No shared token exists to detect this on, so a rule your change carries has to be re-read by hand in the pages that restate it.

Coarse fallback — 136 page(s) merely mention a changed package (the pre-#9192 predicate, kept for the deliberately-wide backstop): node scripts/docs-audit/affected-docs.mjs --json 24964153e2843303fb591debb2c060934153c809packageMentionDocs.

Which tree this was computed on

This run read content/docs from 0a1d46a3790e737a33d47eb11c6a6f823108b0e5 — the merge of head 3233eea6733eb621d76ed7386c558e290c987497 into base 24964153e2843303fb591debb2c060934153c809, which is what actions/checkout gives a pull_request run. Not the PR head.

A worktree cut from an older main holds a different content/docs, so re-deriving there can legitimately return a different list — that is a different tree, not a wrong row. To answer on the same tree:

# while this PR is open — GitHub drops the merge commit once it closes
git fetch origin 0a1d46a3790e737a33d47eb11c6a6f823108b0e5 && git checkout 0a1d46a3790e737a33d47eb11c6a6f823108b0e5
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 24964153e2843303fb591debb2c060934153c809 3233eea6733eb621d76ed7386c558e290c987497 && git checkout -B drift-repro 24964153e2843303fb591debb2c060934153c809 && git merge --no-ff 3233eea6733eb621d76ed7386c558e290c987497

node scripts/docs-audit/affected-docs.mjs --json 24964153e2843303fb591debb2c060934153c809

⚠️ That checkout carried uncommitted changes, so the commit above does not fully identify what was read.

@github-actions github-actions Bot added size/l documentation Improvements or additions to documentation protocol:ai tests tooling labels Sep 16, 2026
…nce table

`check:quick-reference-counts` reds because a new reference page moved M, the number of
pages the tree publishes. Correcting M is the whole requirement; listing the new page in
the section table is discretionary polish and is not this card's. The section is now
"11 of 12", the same shape the API section already carries at "17 of 31".

Claude-Session: https://claude.ai/code/session_01KB5PFtxuy1x3dcR5gxudx6
Co-authored-by: Claude <noreply@anthropic.com>

Copy link
Copy Markdown
Collaborator

Contract review

Served-tier: CONTRACT_REVIEW_TIER
Head-sha: 3233eea6733eb621d76ed7386c558e290c987497

① Derived judgments

Accept set

  1. New accept set, phase: BuildProgressPhaseSchema is a bare z.enum over exactly structure, data, verify, done; every other value is refused with invalid_value carrying the accepted list (test asserts code, path and values). PR characterisation "closed, loud refusal": correct and enforced by construction, not merely documented.
  2. New accept set, frame: BuildProgressFrameSchema is z.looseObject with required phase (the enum), optional hop (non-negative integer), optional tool (string, min length 1); unknown keys pass through. PR characterisation "a floor, not a ceiling, so the consumer's shipping panel fields survive": correct, and the pinned fields (appLabel, items, done, total, seq) match what objectui's extractBuildProgress reads today. Consequence the PR states correctly: a producer typo in those panel fields is NOT caught here; only phase, hop, tool are.
  3. Existing accept sets: none changed. contracts/ai-service.ts is untouched; onProgress still types the payload as data?: unknown; nothing removed, renamed or narrowed (api-surface +6 / -0, re-measured). PR characterisation "purely additive": correct.
  4. Membership versus the card: the card does NOT enumerate the emitted set; it fixes only sender (agent loop), channel (data- frames), vocabulary home (@objectstack/spec) and the acceptance shape. Nothing reachable enumerates the producer's emissions either (cloud unreachable from this review, confirmed). The declared set is the consumer's discriminated union (structure, data, done, verified in objectui at the cited symbols) plus the one phase the ruling exists to add (verify, named verbatim in objectui#7388 which the card links). Spelling matches those sources member for member. PR characterisation "measured, not designed" is right for three members and wrong for verify, which is declared by ruling with zero reachable emitters and zero reachable readers at this head. The PR body says this plainly; see item 9 for where the shipped prose does not.

Public surface

  1. Six new exports on @objectstack/spec/ai: BUILD_PROGRESS_PHASES, BuildProgressPhaseSchema, BUILD_PROGRESS_FRAME_TYPE (literal 'data-build-progress'), BuildProgressFrameSchema, and the types BuildProgressPhase, BuildProgressFrame (both z.input, per ADR-0122 bare-name = author state). No name collision on main. PR characterisation "6 added / 0 removed": correct.
  2. Generated publications: two new JSON-Schema defs (ai/BuildProgressFrame, ai/BuildProgressPhase), three authorable-surface keys (ai/BuildProgressFrame:hop|phase|tool), four declaration-map rows, six export-origins rows, one new reference page plus index/meta rows, references/index totals 193/1525 to 194/1527, strictness ledger ai/ 77 to 78. PR characterisation "generator output": consistent with every count I re-measured. The authorable-surface rows are a generator enumeration, not an author-writability claim, and they fall outside the registry-rooted ADR-0049 liveness ratchet (BuildProgressFrame is not a registered metadata type; the liveness job is green).
  3. Hand-kept publications forced by gates: llms.txt 200 to 201 and ai 11 to 12 with the subject list extended (ships in the tarball); quick-reference "11 of 11" to "11 of 12" (11 rows remain); ADR-0122 pin registry M188 / Iso875 / Iso876 with the count 783 to 785 in all three stated places. All re-measured; all still valid against current origin/main (zero drift on the 17 files, clean trial merge). PR characterisation "gate-required minimum, no judgement calls": correct.
  4. Published prose stating behaviour, correct: producer = cloud agent loop in the post-apply verification window, consumer = objectui chat panel (card acceptance 1 met); channel unchanged; consumers compare by value and must treat every phase as optional; hop base left to the emitter's owner. Also correct: the card's own Clause-②: yes declaration. A new closed vocabulary on a published payload plus six exported symbols is accept-set growth; yes (widening) is the right arm.
  5. Published prose stating behaviour, WRONG or unmeasured (the module docblock ships in the tarball and is rendered verbatim into content/docs/references/ai/build-progress.mdx; the changeset ships as CHANGELOG): (a) "every member below is one a real producer emits or a real consumer already discriminates" is false for verify at this head (no reachable emitter, no reachable reader; the bullet beneath it gives the true source, a card's ask plus a ruling). (b) "measured on cloud#1838 ... 9 tool calls ..., one of them the registered verify_build tool": neither the card nor objectui#7388 records verify_build among those calls ("verifying and re-seeding" is all either says); this is an inference presented as a measurement. (c) "a turn that seeds no sample data never reports data" and "apply_edit turns need not report structure" assert producer behaviour with no reachable source; the surrounding guidance is consumer-safe regardless. None of the three changes the accept set; they mis-state provenance, which for a closed enum is the audit trail the "re-measure before moving the array" discipline depends on. Fix is a docblock edit plus gen:docs.

② Semver level

Changeset .changeset/build-progress-phase-vocabulary.md declares '@objectstack/spec': minor and carries Clause-②: yes (widening) in its body; the PR body carries the same line. Consistent with ①: six additive exports, two additive schemas, nothing narrowed, no existing accept set touched. Consistent with the rule at origin/main AGENTS.md L1067-1068 (yes takes at least minor; (widening) is the non-breaking arm) and with check-adr-0087-registration's own self-test, which lists yes (widening) beside minor as a well-formed declaration. Card acceptance 3 met; "Check Changeset" green on head.

③ Boundary flags

The dev report declares open_questions: []. Boundaries the PR declares it does not close, judged:

  1. Producer emission list unsourced. Confirmed: objectstack-ai/cloud is not reachable from this review either, and no reachable text enumerates what the loop emits. At this head nothing breaks, because nothing in either reachable repo parses with the new schema yet (objectstack has zero emitters of the frame; objectui has zero verify and zero use of the new schema). The mismatch risk in both directions materialises only when the cloud#2172 emitter lands or objectui#7388 block 2 adopts the strict parse. MUST ESCALATE to the cloud#2172 emitter card: the emitter imports BUILD_PROGRESS_PHASES and BUILD_PROGRESS_FRAME_TYPE from @objectstack/spec/ai rather than re-typing strings, and any phase beyond the four comes back as an additive spec card before any consumer refuses it. Not a landing blocker: the card's ruling sequences the emitter behind this card.
  2. verify, hop, tool have zero readers and zero emitters in any reachable repo at this head. This is declared-ahead by maintainer ruling, not neglect, and it sits outside the ADR-0049 liveness ledger (registry-rooted; job green). It is nonetheless a liveness debt with two named carriers: cloud#2172 (emitter) and objectui#7388 block 2, which is currently pm:awaiting-maintainer after the maintainer handed it off on 2026-09-10. The seat should record that if neither carrier lands, these become enforce-or-remove candidates.
  3. contracts/ai-service.ts docblock not cross-linked to the enum: verified untouched; its example data: { phase, items } stays compatible with the loose floor. Follow-up, not a contract defect.
  4. "Not merged with main": verified clean. Zero drift on all 17 files across the 11 commits main has gained; merge-tree produces a tree without conflict; the hand-kept counts (llms.txt, pins, quick-reference, references index) are still the numbers main carries.
  5. hop base 0 or 1 left to cloud#2172, and tool a free string: both admitted by the schema as declared; fine. Note the min(1) on tool refuses an empty string, which is pinned and intended.
  6. check:cross-package-test-inputs red not caused by this diff: filed as check:cross-package-test-inputs answers differently on the same commit depending on whether packages/spec was built — silent on an unbuilt tree, exit 1 on a built one #18526 (open, finding), reproduction described on a pristine base; not this PR's to close.
  7. extractBlueprintProgress sibling collapse, declaration-map pair staleness, authorable-surface listing: correctly not filed, carriers named or explicitly none.
  8. Not raised by the PR: PROTOCOL_MAP.md has no build-progress.zod.ts row. Its own header calls it a curated digest and its test asserts link validity only, so nothing is owed; optional polish for the cross-link follow-up.
  9. Not raised by the PR: the shipped-prose over-statements in ① item 9. Required correction carried by the seat; the cheapest place is this PR (edit the docblock, run gen:docs, the changeset's "never reports data" sentence softened), since the reference page is regenerated from the docblock. This is the one item I would not let ride into a later card unnamed.

Implemented-by: claude/issue-18451-build-progress-phase-vocabulary
Reviewed-by: session_01LvwGppdonww4zGLWZo5rho

VERDICT: PASS


Generated by Claude Code

Copy link
Copy Markdown
Collaborator

Clause-② carriers cleared — provenance

Both carriers stripped in one stroke by the dispatching seat, session_01LvwGppdonww4zGLWZo5rho (domain:spec execution seat).

Review of record comment 5706750292 on this PR
Head judged 3233eea6733eb621d76ed7386c558e290c987497
Served tier CONTRACT_REVIEW_TIER
Verdict PASS
Carriers cleared PR #18528 ✅ · card #18451 ✅ (both read back after the write)

How this verdict was produced. This seat's measured served model is claude-opus-5; CONTRACT_REVIEW_TIER is claude-fable-5-1 and the comparison is exact ⇒ the seat is not at tier. Per the downgrade fuse the verdict was routed to an isolated review subagent running at CONTRACT_REVIEW_TIER, fed only the card, its rulings and the PR itself, with an adversarial brief — ⛔ never the dispatch order, ⛔ never this seat's conclusions. The record is adopted verbatim. ⛔ Not an in-seat review and ⛔ not a quota downgrade.

Independence pair: a mode:subagent dev's branch against the seat session adopting the isolated reviewer's verdict ⇒ no SELF-REVIEW.

Boundary item carried to #18552, filed before this PR lands so a closing keyword cannot lose it. The review named it as a required correction to shipped prose; ⛔ the seat did not let it ride unnamed, and ⛔ did not fix it in-branch, because that would move the head and void an otherwise complete review record.

Pre-landing checks: ① review PASS on record ✅ · ② check-clause2-carriers --pair 18528 re-run after the strip ✅ · ③ re-taken at landing time, latest-run-per-check-name ✅. Governed-surface predicate: 0 paths hit the register ⇒ ordinary queue landing.


Generated by Claude Code

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

documentation Improvements or additions to documentation protocol:ai size/l tests tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec(ai): declare the build-progress PHASE vocabulary the cloud agent loop emits during post-apply verification (cloud#2172 ruling A)

3 participants