Skip to content

Commit 0b31d90

Browse files
os-billclaude
andauthored
fix(spec): label every producer claim in the build-progress docblock — measured, ruled, or inferred (#18870)
Fixes #18552 Clause-②: no The module docblock on `packages/spec/src/ai/build-progress.zod.ts` stated three producer claims as MEASUREMENTS. It ships inside the published `@objectstack/spec` tarball and is rendered verbatim into `content/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 for `verify`. 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`, `BuildProgressPhaseSchema` and `BuildProgressFrameSchema` accept 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-surface` is 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 | Original sentence | Now | Label | Basis | | :--- | :--- | :--- | :--- | | "Membership was MEASURED against the two ends of the channel, not designed — every member below is one a real producer emits or a real consumer already discriminates" | "Membership is MIXED-PROVENANCE, not uniformly measured. Each member carries its own label …" | (headline retired; each member labelled) | False for `verify` at this head — the reading is below | | "`structure`, `data`, `done` — the consumer's own declared union …" | same, prefixed **measured on a named reachable source**, cited with the tree it was read against | measured on a named reachable source | objectui `dda8f3815`: `ChatbotEnhanced.tsx:163` and `mapMessages.ts:743` | | "`verify` — the post-apply verification window objectui#7388 asks the panel to be able to name …" | same reason, now prefixed **declared by ruling**, ⛔ not measured, with the zero-occurrence reading and its bright control recorded beside it | declared by ruling | cloud#2172 ruling A; objectui#7388. ⛔ The member itself is untouched | | "Corroborated in this repo by the `verify_build` tool that `service-ai-studio` actually registers" | kept, and narrowed: it corroborates the TOOL's registration, not the phase | measured on a named reachable source | `packages/spec/src/system/constants/platform-tool-names.ts:85` | | "measured on cloud#1838: 111 seconds and 9 tool calls *after* `apply_blueprint` returned" | "The only record of that window is the cloud#1838 turn, reported there as 111 seconds and 9 tool calls … carried here, not measured" | inferred | `objectstack-ai/cloud` is not reachable from this session's credential, so the figure cannot be re-measured from this repo. ⛔ Nothing was deleted | | "one of them the registered `verify_build` tool" | "WHICH tools those 9 calls were is recorded nowhere reachable either … so 'one of them ran the registered `verify_build` tool' is **inferred** as well" | inferred | Neither #18451 nor objectui#7388 names the 9 calls | | "A tool's own `ctx.onProgress` handle dies when the tool returns, so a frame emitted in that window can only come from the loop" | same, now labelled and sourced | inferred (from a reachable contract) | `AIToolContext.onProgress` in `../contracts/ai-service` declares the emit as happening WHILE the tool executes, before it returns; that the handle is then gone is the deduction | | "The cloud AI-studio agent loop — deliberately not the tool it just ran" | same, now labelled | declared by ruling | cloud#2172 ruling A owns where the emitter sits | | "a turn that applies no seed data never reports `data`" | kept as an illustration, labelled **inferred** | inferred | No reachable repository records it; cloud#2172's emitter has not landed | | "`apply_edit` turns need not report `structure`" | kept as an illustration, labelled **inferred** | inferred | Same | | `hop`: "the counter behind the '9 tool calls' cloud#1838 measured" | "… the cloud#1838 turn record reports … **Inferred** that a producer counts hops at all" | inferred | Same unreachable record | 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/objectui` checkout at commit `dda8f3815df6eb92e944a33745070e361e8d6e40`, one instrument: ``` git grep -l "'verify'" -- packages/plugin-chatbot -> 0 files git grep -l "'structure'" -- packages/plugin-chatbot -> 4 files (bright control) packages/plugin-chatbot/src/ChatbotEnhanced.tsx packages/plugin-chatbot/src/__tests__/ChatbotEnhanced.test.tsx packages/plugin-chatbot/src/__tests__/buildPanelI18n-7388.test.tsx packages/plugin-chatbot/src/mapMessages.ts ``` 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 in `app-shell/src/console/RemediationOverlay.tsx` as a password-remediation step — unrelated to build progress. On this side, `data-build-progress` occurs in 4 files, every one of them this module's own declaration, its test, the `ai/index.ts` line and the `contracts/ai-service` example (control: `onProgress` occurs in 10 files) — zero emitters. ⚠️ The docblock previously cited `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 `BuildProgressPhaseSchema` and `BuildProgressFrameSchema` (14 phase inputs, 13 frame inputs, plus the exported array and frame-type literals), run against the source before the edit and again after: ``` diff dark-BEFORE.txt dark-AFTER.txt -> empty, exit 0 (29 lines each) ``` Covered on both sides: all four declared phases parse; `designing` / `rebuilding` / `VERIFY` / `''` / `3` / `1.5` / `null` / `undefined` / `{}` / `['verify']` are refused with `invalid_value` carrying the full accepted set; the frame refuses `hop: -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/spec` test suite: **487 files / 14058 tests, all passing** — every pre-existing assertion in `build-progress.test.ts` untouched. ## Generated-artifact reconciliation `gen:docs` writes 224 files. After it, exactly one is modified: ``` $ git status --porcelain M content/docs/references/ai/build-progress.mdx M packages/spec/src/ai/build-progress.zod.ts ``` The `.mdx` diff 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. The `BUILD_PROGRESS_PHASES` and field docblocks are not rendered by `build-docs.ts`, which is why (a) and (c) produce no `.mdx` change at all. `content/docs/references/**` is on the `merge=os-regen` roster (verified here: `git check-attr merge` answers `os-regen` for this path, and `unspecified` for the hand-written `.zod.ts` beside it — the instrument discriminates). `origin/main` was merged in through `bash 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`, `hop` and `tool` have 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; `BuildProgressFrame` is not a registered metadata type, so the liveness job is green). A new `## Liveness watch` section 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 the `verify` bullet 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**, including `check:docs`, `check:api-surface`, `check:authorable-surface`. Tree clean after the run (no regeneration drift). - `check:api-surface` did 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-wide `eslint . --no-inline-config`) — green, exit 0, at this head. Not narrowed. - Derived gate families: `node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack` produced 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-debt` each 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.md` was left untouched. Re-read at the start of this round: the file is still on `origin/main`, and PR #17076 (`chore: version packages`) is still open and unmerged, and holds a deletion of it. Its "never reports `data`" 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 control `data-build-progress`, while `changeset` hits 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. - noted, not filed: the docblock of `packages/spec/src/ai/build-progress.test.ts` carries 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.ts` sources, 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](https://claude.ai/code/session_01JbZnqu8bt6YqfJsr9vaFb3)_ Co-authored-by: Claude <noreply@anthropic.com>
1 parent 0b788da commit 0b31d90

4 files changed

Lines changed: 247 additions & 29 deletions

File tree

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
---
2+
'@objectstack/spec': patch
3+
---
4+
5+
fix(spec): label every producer claim in the `build-progress` docblock — measured, ruled, or inferred (#18552)
6+
7+
Clause-②: no
8+
9+
The module docblock on `ai/build-progress.zod.ts` stated three producer claims
10+
as MEASUREMENTS. It ships in this tarball (the published `files[]` carries the
11+
`.zod.ts` sources) and is rendered verbatim into the generated reference page,
12+
and for a CLOSED vocabulary it is the audit trail the "re-measure before you
13+
move the array" discipline reads. One of the three was false, and a reader
14+
deciding whether a fifth phase is warranted would have read all three as
15+
readings.
16+
17+
Each producer claim now carries exactly one of three labels, defined at the top
18+
of the module: **measured on a named reachable source**, **declared by ruling**,
19+
or **inferred**.
20+
21+
- Membership is no longer described as uniformly measured. `structure`, `data`
22+
and `done` stay **measured** — the objectui reader's own union and coercion
23+
default, cited with the tree they were read against. `verify` is **declared by
24+
ruling** (cloud#2172, objectui#7388): at the read tree the chat panel has zero
25+
occurrences of `'verify'` against a control of four files for `'structure'`,
26+
and this repository emits no frame at all. That is a good reason for the
27+
member; it is not an observation, and the docblock no longer says it is.
28+
- The cloud#1838 window — "111 seconds and 9 tool calls", "one of them
29+
`verify_build`" — is **inferred**: that record is not reachable from this
30+
repository, so the figure is carried, not measured, and which tools those
31+
calls were is recorded nowhere reachable. What is measured is narrower and
32+
stated as such: `verify_build` is a registered platform tool.
33+
- "A turn that seeds no sample data never reports `data`" and "`apply_edit`
34+
turns need not report `structure`" are **inferred**. The consumer guidance
35+
around them is unchanged and does not rest on them: treat every phase as
36+
optional and compare by value.
37+
38+
A new `## Liveness watch` section records that `verify`, `hop` and `tool` are
39+
declared ahead of any code that uses them, that cloud#2172 and objectui#7388
40+
block 2 are the named carriers meant to close that, and that no gate watches it
41+
— `BuildProgressFrame` is not a registered metadata type, so the ADR-0049
42+
liveness ledger never sees it.
43+
44+
No schema, export or parse behaviour moves: `BUILD_PROGRESS_PHASES`,
45+
`BuildProgressPhaseSchema` and `BuildProgressFrameSchema` accept and refuse
46+
exactly what they did before.

‎content/docs/references/ai/build-progress.mdx‎

Lines changed: 54 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -8,18 +8,45 @@ description: Build Progress protocol schemas
88
Build-progress PHASE vocabulary for the `data-build-progress` stream frame
99
(cloud#2172 ruling A).
1010

11+
## How to read the claims in this module
12+
13+
This vocabulary is CLOSED, so whoever next asks whether a fifth member is
14+
warranted has to re-measure before moving the array — and can only do that
15+
if they can tell a reading from a ruling. Every producer claim below
16+
therefore carries exactly one of three labels, and none is left bare:
17+
18+
- **measured on a named reachable source** — a repository and path the
19+
reader can open and re-measure, cited with the tree it was read against.
20+
- **declared by ruling** — a maintainer decision. The authority is the
21+
ruling; ⛔ it is not evidence the behaviour exists yet.
22+
- **inferred** — deduced from something reachable, or carried from a record
23+
this repository cannot open. ⛔ Never re-cite it as a measurement.
24+
1125
## Producer
1226

1327
The cloud AI-studio **agent loop** — deliberately not the tool it just ran.
28+
**Declared by ruling**: cloud#2172 ruling A owns WHERE in the loop a frame is
29+
emitted; this module declares only what such a frame may SAY.
30+
1431
A build turn applies its change through `apply_blueprint` / `apply_edit` and
1532
then keeps working: the loop spends a further POST-APPLY VERIFICATION window
16-
re-reading and re-seeding what it wrote (measured on cloud#1838: 111 seconds
17-
and 9 tool calls *after* `apply_blueprint` returned, one of them the
18-
registered `verify_build` tool — see `PLATFORM_TOOLS_BY_PACKAGE` in
19-
`../system/constants/platform-tool-names`). A tool's own `ctx.onProgress`
20-
handle dies when the tool returns, so a frame emitted in that window can only
21-
come from the loop. WHERE in the loop it is emitted is cloud#2172's decision,
22-
not this module's; what such a frame may SAY is declared here.
33+
re-reading and re-seeding what it wrote. **Inferred.** The only record of
34+
that window is the cloud#1838 turn, reported there as 111 seconds and 9 tool
35+
calls *after* `apply_blueprint` returned — and `objectstack-ai/cloud` is
36+
outside the set of repositories a reader of this file can open, so the
37+
figure is carried here, not measured, and cannot be re-measured from this
38+
repo. WHICH tools those 9 calls were is recorded nowhere reachable either:
39+
neither objectstack#18451 nor objectui#7388 names them, so "one of them ran
40+
the registered `verify_build` tool" is **inferred** as well. What is
41+
**measured on a named reachable source** is narrower than that — only that
42+
`verify_build` is a registered platform tool, listed in
43+
`PLATFORM_TOOLS_BY_PACKAGE` in `../system/constants/platform-tool-names`.
44+
45+
A tool's own `ctx.onProgress` handle dies when the tool returns, so a frame
46+
emitted in that window can only come from the loop. **Inferred** from a
47+
reachable contract: `AIToolContext.onProgress` in `../contracts/ai-service`
48+
declares the emit as happening WHILE the tool executes, before it returns;
49+
that the handle is gone afterwards is the deduction, not the declaration.
2350

2451
## Consumer
2552

@@ -40,6 +67,26 @@ Unchanged: the `data-`-prefixed custom part described on
4067
under a stable part id. This module adds the vocabulary that prose has always
4168
assumed and never declared; it moves no transport and renames nothing.
4269

70+
## Liveness watch — declared ahead, and ⛔ nothing gates it
71+
72+
Three surfaces here are declared ahead of any code that uses them: the
73+
`verify` phase, and the frame's `hop` and `tool` fields. Across every
74+
repository reachable from here they have zero emitters and zero readers
75+
(**measured on named reachable sources** — each reading is recorded in the
76+
source beside its surface: on `BUILD_PROGRESS_PHASES` for the phase, and on
77+
the `hop` / `tool` field declarations). They stand on a ruling instead
78+
(**declared by ruling**): cloud#2172 ruled the vocabulary in, and
79+
objectui#7388 asked the panel to be able to name the phase. That is a good
80+
reason, and it is not a measurement.
81+
82+
⛔ No gate watches this. The ADR-0049 liveness ledger is rooted in the
83+
metadata-type registry, and `BuildProgressFrame` is not a registered
84+
metadata type, so the liveness job stays green however long these three go
85+
unused. Two named carriers are meant to close it — cloud#2172 for the
86+
emitter, objectui#7388 block 2 for the consumer's strict parse. If neither
87+
lands, the three become enforce-or-remove candidates with nothing watching
88+
them, and the only thing that notices is a person reading this paragraph.
89+
4390
<Callout type="info">
4491
**Source:** `packages/spec/src/ai/build-progress.zod.ts`
4592
</Callout>

‎packages/spec/src/ai/build-progress.test.ts‎

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,10 @@
2323
* someone re-measured — not an edit to wave through.
2424
*/
2525

26+
import { readFileSync } from 'node:fs';
27+
import { dirname, resolve } from 'node:path';
28+
import { fileURLToPath } from 'node:url';
29+
2630
import { describe, it, expect } from 'vitest';
2731

2832
import {
@@ -180,3 +184,56 @@ describe('BuildProgressPhase (type)', () => {
180184
expect(everyPhase).toHaveLength(BUILD_PROGRESS_PHASES.length);
181185
});
182186
});
187+
188+
/**
189+
* The provenance leg — and the one leg whose subject is TEXT on purpose.
190+
*
191+
* This module's docblock is not incidental prose. The published
192+
* `@objectstack/spec` tarball ships the `.zod.ts` sources themselves, and the
193+
* same text is rendered verbatim into
194+
* `content/docs/references/ai/build-progress.mdx`. For a CLOSED vocabulary it
195+
* is the audit trail the "re-measure before you move the array" discipline
196+
* reads: a reader who cannot tell a MEASUREMENT from a RULING re-cites the
197+
* ruling as evidence, and the next member goes in on a claim nobody ever made.
198+
*
199+
* So what is pinned here is narrow and load-bearing: that the three labels are
200+
* defined, that `verify` is labelled as the ruling it is rather than as a
201+
* measurement, and that the liveness watch is still present — ⛔ not the
202+
* wording around any of them. Nothing else in this file asserts on text.
203+
*/
204+
describe('module docblock provenance', () => {
205+
const SOURCE = readFileSync(
206+
resolve(dirname(fileURLToPath(import.meta.url)), 'build-progress.zod.ts'),
207+
'utf-8',
208+
);
209+
210+
it('defines all three provenance labels', () => {
211+
for (const label of ['measured on a named reachable source', 'declared by ruling', 'inferred']) {
212+
expect(SOURCE).toContain(label);
213+
}
214+
});
215+
216+
it('labels `verify` as declared by ruling, and ⛔ never as measured membership', () => {
217+
// The bullet itself, not the surrounding paragraph: `verify` is in the
218+
// enum because cloud#2172 ruled it in, which is a good reason and is not
219+
// an observation of any producer or consumer.
220+
const bullet = SOURCE.slice(SOURCE.indexOf(' * - `verify`'));
221+
expect(bullet.slice(0, 200)).toContain('**declared by ruling**');
222+
223+
// The retired headline claimed every member was measured against a real
224+
// end of the channel. It was false for `verify` on the day it was written.
225+
expect(SOURCE).not.toContain('Membership was MEASURED');
226+
expect(SOURCE).not.toContain('every member below is one a real producer emits');
227+
});
228+
229+
it('keeps the liveness watch for the three declared-ahead surfaces', () => {
230+
expect(SOURCE).toContain('## Liveness watch');
231+
for (const surface of ['`verify` phase', '`hop`', '`tool`']) {
232+
expect(SOURCE).toContain(surface);
233+
}
234+
// The point of the watch: no gate notices, so the paragraph is the notice.
235+
expect(SOURCE).toContain('ADR-0049');
236+
expect(SOURCE).toContain('cloud#2172');
237+
expect(SOURCE).toContain('objectui#7388 block 2');
238+
});
239+
});

‎packages/spec/src/ai/build-progress.zod.ts‎

Lines changed: 90 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -7,18 +7,45 @@ import { lazySchema } from '../shared/lazy-schema';
77
* Build-progress PHASE vocabulary for the `data-build-progress` stream frame
88
* (cloud#2172 ruling A).
99
*
10+
* ## How to read the claims in this module
11+
*
12+
* This vocabulary is CLOSED, so whoever next asks whether a fifth member is
13+
* warranted has to re-measure before moving the array — and can only do that
14+
* if they can tell a reading from a ruling. Every producer claim below
15+
* therefore carries exactly one of three labels, and none is left bare:
16+
*
17+
* - **measured on a named reachable source** — a repository and path the
18+
* reader can open and re-measure, cited with the tree it was read against.
19+
* - **declared by ruling** — a maintainer decision. The authority is the
20+
* ruling; ⛔ it is not evidence the behaviour exists yet.
21+
* - **inferred** — deduced from something reachable, or carried from a record
22+
* this repository cannot open. ⛔ Never re-cite it as a measurement.
23+
*
1024
* ## Producer
1125
*
1226
* The cloud AI-studio **agent loop** — deliberately not the tool it just ran.
27+
* **Declared by ruling**: cloud#2172 ruling A owns WHERE in the loop a frame is
28+
* emitted; this module declares only what such a frame may SAY.
29+
*
1330
* A build turn applies its change through `apply_blueprint` / `apply_edit` and
1431
* then keeps working: the loop spends a further POST-APPLY VERIFICATION window
15-
* re-reading and re-seeding what it wrote (measured on cloud#1838: 111 seconds
16-
* and 9 tool calls *after* `apply_blueprint` returned, one of them the
17-
* registered `verify_build` tool — see `PLATFORM_TOOLS_BY_PACKAGE` in
18-
* `../system/constants/platform-tool-names`). A tool's own `ctx.onProgress`
19-
* handle dies when the tool returns, so a frame emitted in that window can only
20-
* come from the loop. WHERE in the loop it is emitted is cloud#2172's decision,
21-
* not this module's; what such a frame may SAY is declared here.
32+
* re-reading and re-seeding what it wrote. **Inferred.** The only record of
33+
* that window is the cloud#1838 turn, reported there as 111 seconds and 9 tool
34+
* calls *after* `apply_blueprint` returned — and `objectstack-ai/cloud` is
35+
* outside the set of repositories a reader of this file can open, so the
36+
* figure is carried here, not measured, and cannot be re-measured from this
37+
* repo. WHICH tools those 9 calls were is recorded nowhere reachable either:
38+
* neither objectstack#18451 nor objectui#7388 names them, so "one of them ran
39+
* the registered `verify_build` tool" is **inferred** as well. What is
40+
* **measured on a named reachable source** is narrower than that — only that
41+
* `verify_build` is a registered platform tool, listed in
42+
* `PLATFORM_TOOLS_BY_PACKAGE` in `../system/constants/platform-tool-names`.
43+
*
44+
* A tool's own `ctx.onProgress` handle dies when the tool returns, so a frame
45+
* emitted in that window can only come from the loop. **Inferred** from a
46+
* reachable contract: `AIToolContext.onProgress` in `../contracts/ai-service`
47+
* declares the emit as happening WHILE the tool executes, before it returns;
48+
* that the handle is gone afterwards is the deduction, not the declaration.
2249
*
2350
* ## Consumer
2451
*
@@ -38,31 +65,64 @@ import { lazySchema } from '../shared/lazy-schema';
3865
* `AIToolContext.onProgress` in `../contracts/ai-service`, reconciled in place
3966
* under a stable part id. This module adds the vocabulary that prose has always
4067
* assumed and never declared; it moves no transport and renames nothing.
68+
*
69+
* ## Liveness watch — declared ahead, and ⛔ nothing gates it
70+
*
71+
* Three surfaces here are declared ahead of any code that uses them: the
72+
* `verify` phase, and the frame's `hop` and `tool` fields. Across every
73+
* repository reachable from here they have zero emitters and zero readers
74+
* (**measured on named reachable sources** — each reading is recorded in the
75+
* source beside its surface: on `BUILD_PROGRESS_PHASES` for the phase, and on
76+
* the `hop` / `tool` field declarations). They stand on a ruling instead
77+
* (**declared by ruling**): cloud#2172 ruled the vocabulary in, and
78+
* objectui#7388 asked the panel to be able to name the phase. That is a good
79+
* reason, and it is not a measurement.
80+
*
81+
* ⛔ No gate watches this. The ADR-0049 liveness ledger is rooted in the
82+
* metadata-type registry, and `BuildProgressFrame` is not a registered
83+
* metadata type, so the liveness job stays green however long these three go
84+
* unused. Two named carriers are meant to close it — cloud#2172 for the
85+
* emitter, objectui#7388 block 2 for the consumer's strict parse. If neither
86+
* lands, the three become enforce-or-remove candidates with nothing watching
87+
* them, and the only thing that notices is a person reading this paragraph.
4188
*/
4289

4390
/**
4491
* The closed phase vocabulary, in emission-lifecycle order.
4592
*
46-
* Membership was MEASURED against the two ends of the channel, not designed —
47-
* every member below is one a real producer emits or a real consumer already
48-
* discriminates:
93+
* Membership is MIXED-PROVENANCE, not uniformly measured. Each member carries
94+
* its own label (the three are defined at the top of this module), because the
95+
* question a re-measure has to answer is which members a real end of the
96+
* channel already uses and which are standing on a ruling alone:
4997
*
50-
* - `structure`, `data`, `done` — the consumer's own declared union,
98+
* - `structure`, `data`, `done` — **measured on a named reachable source**:
99+
* the consumer's own declared union,
51100
* `ChatBuildProgress['phase']` at `packages/plugin-chatbot/src/ChatbotEnhanced.tsx:163`
52101
* in objectui, and the set its reader discriminates at
53-
* `packages/plugin-chatbot/src/mapMessages.ts:744`
102+
* `packages/plugin-chatbot/src/mapMessages.ts:743`
54103
* (`d.phase === 'data' || d.phase === 'done' ? d.phase : 'structure'`).
55-
* `structure` doubles as that reader's coercion default.
56-
* - `verify` — the post-apply verification window objectui#7388 asks the panel
57-
* to be able to name, and the reason cloud#2172 ruled this vocabulary into
58-
* the spec. Corroborated in this repo by the `verify_build` tool that
59-
* `service-ai-studio` actually registers
60-
* (`../system/constants/platform-tool-names`).
104+
* `structure` doubles as that reader's coercion default. Read at objectui
105+
* `dda8f3815`; line numbers drift, the two symbols are the anchor.
106+
* - `verify` — **declared by ruling**, ⛔ not measured: the post-apply
107+
* verification window objectui#7388 asks the panel to be able to name, and
108+
* the reason cloud#2172 ruled this vocabulary into the spec. At objectui
109+
* `dda8f3815` `packages/plugin-chatbot` has ZERO occurrences of `'verify'`
110+
* (bright control, same instrument: `'structure'` hits 4 files there), and
111+
* this repository emits no frame at all, so `verify` has no reachable
112+
* emitter and no reachable reader today. **Measured on a named reachable
113+
* source** alongside it, and narrower than a corroboration of the phase:
114+
* `service-ai-studio` registers a `verify_build` tool
115+
* (`../system/constants/platform-tool-names`). See the liveness watch at the
116+
* top of this module.
61117
*
62118
* Order is the order a build turn passes through these states; it is NOT a
63119
* scale. ⛔ Consumers compare phases by VALUE — never by index, and never by
64-
* assuming every phase occurs (a turn that applies no seed data never reports
65-
* `data`, and `apply_edit` turns need not report `structure`).
120+
* assuming every phase occurs. The two illustrations of a phase being skipped
121+
* — a turn that applies no seed data never reporting `data`, and `apply_edit`
122+
* turns not reporting `structure` — are **inferred**: no reachable repository
123+
* records either, and cloud#2172's emitter has not landed. ⛔ The guidance
124+
* does not rest on them; treat every phase as optional whatever a producer
125+
* turns out to do.
66126
*
67127
* Exported as an array as well as a schema so a consumer can build an
68128
* exhaustive per-phase label map without forcing the lazy schema to
@@ -107,13 +167,18 @@ export const BuildProgressFrameSchema = lazySchema(() => z.looseObject({
107167
phase: BuildProgressPhaseSchema.describe('Build-turn phase this frame reports'),
108168
/**
109169
* Which hop of the post-apply verification loop this frame is on — the
110-
* counter behind the "9 tool calls" cloud#1838 measured, so the panel can
111-
* show motion through a window that is otherwise a single flat phase.
170+
* counter behind the "9 tool calls" the cloud#1838 turn record reports, so
171+
* the panel can show motion through a window that is otherwise a single flat
172+
* phase. **Inferred** that a producer counts hops at all: that record is not
173+
* reachable from this repository (see the Producer section).
112174
*
113175
* Typed as a non-negative integer rather than pinned to a base: whether the
114176
* loop counts its first hop as 0 or 1 is part of the emitter's placement,
115177
* which cloud#2172 owns and this card does not decide. ⛔ Consumers render it
116178
* as progress, never as an index into anything here.
179+
*
180+
* ⛔ Zero emitters and zero readers in every reachable repository at this
181+
* tree — see the liveness watch at the top of this module.
117182
*/
118183
hop: z.number().int().nonnegative().optional().describe('Post-apply verification hop this frame reports'),
119184
/**
@@ -123,6 +188,9 @@ export const BuildProgressFrameSchema = lazySchema(() => z.looseObject({
123188
* registered at boot and legitimately includes plugin-contributed names that
124189
* `PLATFORM_PROVIDED_TOOL_NAMES` cannot know about
125190
* (`../system/constants/platform-tool-names`). Consumers treat it as a label.
191+
*
192+
* ⛔ Zero emitters and zero readers in every reachable repository at this
193+
* tree — see the liveness watch at the top of this module.
126194
*/
127195
tool: z.string().min(1).optional().describe('Name of the tool the current hop is running'),
128196
}));

0 commit comments

Comments
 (0)