Skip to content

fix(cli): os init and os generate emit Data.ServiceObject so scaffolded projects type-check - #16193

Merged
os-litant merged 3 commits into
mainfrom
claude/issue-15976-init-data-object-annotation
Sep 6, 2026
Merged

fix(cli): os init and os generate emit Data.ServiceObject so scaffolded projects type-check#16193
os-litant merged 3 commits into
mainfrom
claude/issue-15976-init-data-object-annotation

Conversation

@os-litant

@os-litant os-litant commented Sep 6, 2026

Copy link
Copy Markdown
Collaborator

Fixes #15976

Review round 1 applied at head 4cf3f44ab86 — one commit, one file: the changeset frontmatter regraded minorpatch, its body re-based on ADR-0122 D1, and a falsified claim retracted below. ⛔ No source, test or docs file moved. Code, docs fix and pin were cleared as written.

What was broken

os init -t app, os init -t plugin and os g object each emitted an object file annotated const … : Data.Object. @objectstack/spec/data exports no member named Object, so the primary scaffolder — the first command a new user runs — produced a project that failed its own pnpm typecheck.

Re-driven on this branch through the scaffolders' own emitters (TEMPLATES + writeTemplateSrcFiles, GENERATOR_SCAFFOLD_TARGETS) into a sandbox carrying the tsconfig renderScaffoldTsconfig really writes, then compiled with tsc --noEmit — the exact command the emitted package.json binds to typecheck:

emission on main on this branch
os init -t app exit 2 — TS2694 exit 0
os init -t plugin exit 2 — TS2694 exit 0
os g object exit 2 — TS2694 exit 0
error TS2694: Namespace '.../@objectstack/spec/dist/data/index' has no exported member 'Object'

The repair, and why it needed nothing from packages/spec

The annotation is now Data.ServiceObject. That name was not chosen here. ADR-0122 D1 (Accepted 2026-08-06; phase 2 landed in @objectstack/spec 17.0.0) already ruled it:

D1 — The bare alias name denotes the AUTHOR state. For a schema XSchema, export type X = z.input<typeof XSchema>. This is the name documentation, examples, skills and AI authoring surfaces use for the thing an author writes.

An emitted scaffold is the thing an author writes, so the bare alias is the one it owes. The sibling generators were already on that convention — UI.View, UI.Action, UI.Dashboard and Automation.Flow are each the bare alias of their own schema — and only the object emitters had drifted off it.

The parsed-state alias is not a live alternative, and this is measured rather than argued. Annotating the same three emitted literals Data.ServiceObjectParsed instead:

Data.ServiceObject        init -t app / init -t plugin / os g object   ->  exit 0
Data.ServiceObjectParsed  init -t app / init -t plugin / os g object   ->  exit 2, TS2740
  Type '{ type: "text"; label: string; required: true; }' is missing the following
  properties from type '{ … }': searchable, multiple, hidden, readonly, and 2 more.

Both arms driven, so the reading can fail in either direction. That is precisely the author-state / parsed-state split ADR-0122 D2 draws: the emitted literal omits every defaulted key, which is legal input and illegal output.

No export was added to @objectstack/spec; ServiceObject has been exported from @objectstack/spec/data throughout.

⛔ Retraction — two citations withdrawn, not quietly edited

An earlier revision of this body rested part of the case on "the hand-written docs already annotate authored objects with ServiceObject", citing content/docs/concepts/metadata-driven.mdx and content/docs/getting-started/quick-reference.mdx. Contract review falsified both, and I confirmed the falsification against the tree:

  • metadata-driven.mdx:363 — the const Account: ServiceObject = { line sits under that page's "❌ Deprecated:" heading, commented // Old pattern - no runtime validation, beneath the rule "### 1. Always Use ObjectSchema.create() with Field.* Helpers". It is the pattern the page tells authors not to write. Citing it as precedent was wrong.
  • quick-reference.mdx:295 — names ServiceObject only in an import type { … } example. It annotates no authored object. grep -nE ": *ServiceObject\b" over that page returns nothing.

The conclusion is unchanged; that support is withdrawn. The claim now rests on ADR-0122 D1 and on the sibling generators, both of which were verified directly.

⚠️ One thing the retraction surfaces, flagged rather than acted on: metadata-driven.mdx calls the plain annotated literal the deprecated shape and ObjectSchema.create() the correct one — while os init and os g object emit the plain literal. Whether the scaffolder should emit the factory shape is a separate question, out of scope here, and it is the same underlying mismatch already recorded in #16195 (all 112 real *.object.ts files in the tree use the factory form).

content/docs/deployment/cli.mdx taught the broken spelling too, and is corrected with the emitters; a reader copying from that line wrote the same uncompilable file.

The two members the card left un-swept

The docs line also named UI.View and Automation.Flow, which the card explicitly did not sweep. Both are genuinely exported, and this was verified by driving them rather than by grep: every generator on the roster was emitted and compiled.

Data.Object was the only non-existent member any emitter named. os g view, os g action, os g flow, os g dashboard, os g app and os g skill all compiled at exit 0 on main, before any change here.

Why nothing caught it

Both existing scaffold sweeps could not see this defect — not by omission, but by construction:

  • generate-scaffold-validates.test.ts loads each scaffold through bundle-require
  • init-scaffold-authoring-rules.test.ts loads each template through the command's own validateScaffold

Both are runtime pins: they materialize the TypeScript and execute it. The loader underneath is esbuild, which erases type annotations without checking them, so Data.Object and Data.ServiceObject transpile to byte-identical JavaScript. The scaffolds genuinely parsed, validated and loaded — they simply never compiled, and nothing here had ever asked a compiler.

The new pin, and the direction it fails in

packages/cli/test/scaffold-emission-typechecks.test.ts adds the missing axis. It derives its rosters from TEMPLATES and GENERATOR_SCAFFOLD_TARGETS, renders the tsconfig from renderScaffoldTsconfig rather than restating it, and spawns a real tsc.

Reverse verification. With main's emitter text restored over the fix (the two init.ts sites and the generate.ts site, mutation confirmed on disk by counting both the removed and the injected text before running), the pin goes red on exactly three cases and no others:

× os init -t app emits a project that passes its own pnpm typecheck
× os init -t plugin emits a project that passes its own pnpm typecheck
× os g object emits a file that type-checks
Tests  3 failed | 9 passed (12)

The tree was then restored to HEAD and the restore proved byte-level — git diff HEAD empty, and both files' git hash-object equal to their HEAD blob hashes.

A reading that could not fail was the thing to avoid here. A tsc harness that resolves nothing, or discovers no files, reports zero errors and reads exactly like a pass. So the pin carries a canary that compiles a deliberately absent member of the same namespace under the same profile and is asserted to fail with TS2694 — the incident's own error code. A second control pins that an object file importing @objectstack/spec/data is still emitted at all, so the pin cannot quietly stop covering this incident by the emission being removed.

Three docblock precision notes recorded by review — accurate, not folded

Each was re-checked against the tree and each is correct. None is a defect in the pin's logic, and this round may not move a test file, so they are recorded here rather than edited in:

  1. The include profile differs between the two case familiesos init sandboxes use SCAFFOLD_TSCONFIG_INCLUDE_WITH_ROOT_CONFIG with rootDir: '.' (the emitted project compiles its root objectstack.config.ts too), while os g sandboxes use SCAFFOLD_TSCONFIG_INCLUDE_SRC_ONLY with rootDir: 'src'. The docblock says the options come from renderScaffoldTsconfig without noting that it is called with two different profiles.
  2. The canary's TS2307 arm is unreachable as described. The docblock tells a reader that a TS2307 canary means the spec dist is unbuilt — but init.ts:4 is a value import (import { PROTOCOL_MAJOR } from '@objectstack/spec/kernel'), so with no dist the suite dies at module collection, before the canary runs. The advice is right about the remedy and wrong about the symptom that would carry a reader to it.
  3. The pin resolves the workspace dist, the card measured the published tarball. The sandbox resolves @objectstack/spec through packages/cli/node_modules to the workspace packages/spec/dist. That is the closest available approximation without a network install, not the published artifact, and the docblock's "exactly as it does for a real user's project" overstates it.

The dependency this PR does not discharge

Out of scope: #15531. os init still has no end-to-end CI gate — scripts/create-scaffold-smoke.sh covers create.ts templates only — and that gap is what hid this defect. The audit for it has landed, but until an end-to-end gate exists, the green here is one-shot: this PR pins the type axis of the emitters, not the install-and-build path a real user walks.

Changeset level — patch

patch on @objectstack/cli. The governing rule is AGENTS.md:

"A bug fix in a released package takes a patch changeset — never none, and ⛔ never skip-changeset"

This is exactly that shape: a scaffolder emitted broken text and now emits working text. Two lane precedents for the same defect shape sit in @objectstack/cli 17.3.0's Patch section — 5dee191 (#13871, os generate ghost field types) and 9786d39 (os create example manifest identity block).

Correcting this body's own earlier reasoning. It previously graded minor and argued "patch would assert no surface moved". That is not what patch asserts — every bug fix moves output bytes, and if moved bytes implied minor the patch grade would have no population at all. The minor grade also leaned on a precedent of the wrong class (an additive key on a machine-readable --json payload, i.e. a contract widening, not a fix). Nothing about the emitted output changed between the two gradings; only the grade did.

Clause ② grading, per limb

Mechanical floor — no. Nothing in the diff sits under packages/spec/src/**, and no key is added to any published payload. Files touched: two CLI command sources, one docs page, one changeset, one new test.

Conformance limb — graded yes, and here is what has changed under it. It was graded yes on the ground that the call was not clear and the rule is to grade yes when it is not. Review then identified the governing ruling: ADR-0122 D1, which already decided that the bare alias is the author state and is the name authoring surfaces use. On that basis this is not a re-selection between two published verdicts — the selection was ruled in 2026-08-06 and this PR conforms to it, which reads no.

⛔ I am not unilaterally downgrading the limb: needs:contract-review is already hung on this PR and on the card, and the disposition belongs to the reviewing seat. Both readings are stated so the downgrade is a decision on the record rather than a silent edit. ⛔ The diff was not shaped to keep any limb no.

Governed surfaces

None touched. The diff contains no docs/adr/**, .claude/**, skills/**, AGENTS.md or CLAUDE.md path, and nothing under content/docs/releases/. The PR is left as a draft, not enqueued, with no auto-merge armed, and no label was touched.

Verification

Re-run whole at head 4cf3f44ab86, not carried over from the previous head.

Gate union derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, asserted against that tool's own Reconciliation — 87 famil(ies) line: 87 / 87 exit 0. The derived family set is byte-identical to the previous head's, as expected — the change set is the same five paths.

Changeset-specific families, named because they are the ones this round moves: check-changeset-no-major, check-empty-changeset, check-changeset-fixed, check:changeset-gate-self-tests, check:objectui-changeset — all exit 0.

The Artifact rosters block was run separately, as it sits outside that total: 34 / 37 exit 0. The same three are NOT MEASURED, named with exit codes rather than folded into a green count:

  • check-partof-closing-keyword.mjsexit 2, NOT WIRED (needs PR_BODY / PR_NUMBER). Run locally against this body with PR_BODY set: exit 0.
  • check-single-claim-paths.mjsexit 2, NOT WIRED (needs PR_NUMBER + a repo token; repo-scoped REST is 403 from this container). CI's No other open PR may claim the same single-writer path check is the authority, not any local run.
  • check:react-declaration-parityexit 1, MANIFEST not set: the registry side is objectui's sdui.manifest.json, needing a sibling checkout and a browser dump. Unrelated to this diff.

Tests carried from head c2587f6aa4c, whose source tree is byte-identical to this head — this round changed one .changeset/*.md file and nothing else, so no test input moved:

  • packages/cli/test/scaffold-emission-typechecks.test.ts — 12 passed
  • the derived importer population — every @objectstack/cli test that imports commands/init or commands/generate (13 files), plus two adjacent scaffold suites: 15 files, 244 passed + 6 expected-fail (250)
  • pnpm --filter @objectstack/cli typecheck — exit 0, both halves (tsc --noEmit and check:test-typecheck). The new test file was confirmed present in the test-layer program via --listFiles, against a sibling-file control.

⚠️ Declared narrowing: the full @objectstack/cli suite (267 files, 56 of them e2e) was not run locally — it is a CI-shaped run. The narrowing is derived rather than chosen: the population is every test importing the two modules this PR edits. A green local union is not a prediction of green CI.

🤖 Generated with Claude Code

https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N

`os init -t app`, `os init -t plugin` and `os g object` all wrote
`const … : Data.Object`, and `@objectstack/spec/data` exports no member
named `Object`. The primary scaffolder therefore emitted a project that
failed its own `pnpm typecheck` with TS2694 (tsc exit 2), measured
against the published tarball a real user installs and identical at
TypeScript 5.3.3, 5.8.3 and 6.0.3.

`Data.ServiceObject` is `z.input<typeof ObjectSchemaBase>` — the
authoring shape of an object, and the structural analogue of the
annotations the sibling generators already emit (`UI.View`, `UI.Action`,
`UI.Dashboard`, `Automation.Flow`). It has always been exported;
nothing was added to `@objectstack/spec`. The hand-written docs already
used it, so this moves the scaffolders onto the repo's own spelling.

`content/docs/deployment/cli.mdx` documented the broken annotation and
is corrected with the emitters.

Adds a type-layer pin over both emitter rosters. The two existing
scaffold sweeps load the emission through esbuild, which erases type
annotations without checking them, so a broken annotation is invisible
to them by construction; the new pin spawns a real `tsc` over the
materialized project under the tsconfig the scaffolder itself renders,
with a canary that must fail TS2694 so the harness cannot pass by
resolving nothing.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N
…eck pin

`check:cli-test-child-env` (#11595) requires every spawn under
`packages/cli/test/**` to declare its child's `env`, so that what the
child inherits is legible at the call site instead of being the vitest
worker's environment by default. The new pin's `spawnSync` passed an
options object with no `env` key and the gate named it.

Routed through `childEnv()`, this directory's choke point, with
`NO_COLOR` to match the `--pretty false` already passed to tsc.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N
@github-actions

github-actions Bot commented Sep 6, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

This PR changes 1 package(s): @objectstack/cli, touching 2 documentable anchor(s).

5 hand-written doc(s) NAME something this change touched and may need an implementation-accuracy re-verification:

  • content/docs/api/data-flow.mdx (via os generate (command, read off packages/cli/src/commands/generate.ts))
  • content/docs/deployment/cli.mdx (via os generate (command, read off packages/cli/src/commands/generate.ts), os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/getting-started/examples.mdx (via os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/getting-started/your-first-project.mdx (via os init (command, read off packages/cli/src/commands/init.ts))
  • content/docs/protocol/kernel/lifecycle.mdx (via os generate (command, read off packages/cli/src/commands/generate.ts))

1 release-owned page(s) also name something this change touched. These are read-only:

  • content/docs/releases/v17.mdx (via os init (command, read off packages/cli/src/commands/init.ts))

content/docs/releases/ is RELEASE-OWNED (AGENTS.md "Documentation Guardrails"): release
notes are written centrally at release time, and a code PR that edits them is the exact PR
that guardrail exists to stop. They are still audited — read-only. If one of them is actually
wrong, file an issue or open a dedicated docs-only PR; do not edit it here.

What this run could not see
  • 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 — 22 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 4c0b22bf758a7a51e46bb756310d3ada29197f0cpackageMentionDocs.

Which tree this was computed on

This run read content/docs from f9387818deb06aa8efa45f4203e6a2e383bc6003 — the merge of head 4cf3f44ab86b07e2fe199e091f8642efa279efc9 into base 4c0b22bf758a7a51e46bb756310d3ada29197f0c, 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 f9387818deb06aa8efa45f4203e6a2e383bc6003 && git checkout f9387818deb06aa8efa45f4203e6a2e383bc6003
# afterwards, rebuild it from the two parents, which stay fetchable
git fetch origin 4c0b22bf758a7a51e46bb756310d3ada29197f0c 4cf3f44ab86b07e2fe199e091f8642efa279efc9 && git checkout -B drift-repro 4c0b22bf758a7a51e46bb756310d3ada29197f0c && git merge --no-ff 4cf3f44ab86b07e2fe199e091f8642efa279efc9

node scripts/docs-audit/affected-docs.mjs --json 4c0b22bf758a7a51e46bb756310d3ada29197f0c

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

Advisory only, and a precision-first one (#9192): a page is listed because it names a
symbol, wire route or SDK method this diff touched — not because it mentions a changed
package. Each row says which anchor put it there, so a wrong row is reportable rather than
merely annoying. To re-verify, run the docs-accuracy-audit workflow scoped to these files:
node scripts/docs-audit/affected-docs.mjs 4c0b22bf758a7a51e46bb756310d3ada29197f0c → pass the list as
args.docs, on the commit named under Which tree this was computed on.

Copy link
Copy Markdown
Collaborator Author

Contract review (clause ②) — CHANGES REQUESTED, one line: the changeset level

Reviewed by an isolated subagent at the contract-review tier (claude-fable-5-1), dispatched by the domain:cli execution PM seat (#6024, session_01D47qPfEWVPmhguWgBZCi5N). Verdict adopted verbatim — under contract-review.md the parent session's only two legal moves are verbatim adoption or wholesale voiding, never rewriting or trimming.

转录档位核验 — PASSED. Per the reference's 转录档位核验 rule, the subagent's transcript was grepped for the harness-stamped model field before any of this was accepted: 113 stamps, all claude-fable-5-1, no other value and no fallback evidence. (The single Degraded token in the transcript is registerDegradedConnector, a symbol in content it read.) ⇒ the ruling is eligible to be adopted.

The ruling on the z.input question — already decided, and not the maintainer's

Data.ServiceObject (option A) is the correct binding, and this is within the review floor because the maintainer already decided it — ADR-0122 D1 (ruled 2026-08-06 on #5551, phase 2 landed in @objectstack/spec 17.0.0) makes the bare alias the author state and names it as "the name documentation, examples, skills and AI authoring surfaces use for the thing an author writes"; an emitted scaffold is the thing an author writes.

Nobody in this chain cited that ADR — not the card, not triage, not the PR body, not the dispatching seat's brief. The reviewer found it by grep, so the ruling rests on a source none of us named.

Option B is not merely "very likely wrong" as the implementer hedged — it is measured wrong: rewriting the annotation to ServiceObjectParsed in a sandbox copy of each emitted file gives all three cases exit 2, TS2740, every field literal missing the defaulted keys (searchable, multiple, hidden, readonly, …).

Conformance limb: graded yes by the implementer because "the call is not clear". The call is settled by an accepted ADR, so no new contract is minted — the yes was the right conservative claim under the rule, and it resolves as PASS on ADR-0122 grounds. Mechanical floor confirmed no against the real merge base be754938a: five paths, grep -c packages/spec/src/ = 0.

The one change requested — changeset minorpatch

Basis, and this seat verified the primary citation itself at origin/main before adopting — AGENTS.md:1018-1019, verbatim:

"A bug fix in a released package takes a patch changeset — never none, and ⛔ never skip-changeset"

with two lane precedents for exactly this defect shape (a scaffolder emitted broken text, now emits working text), both in @objectstack/cli 17.3.0's Patch section: 5dee191 (#13871, os generate ghost field types) and 9786d39 (os create example manifest identity block).

⛔ Two errors by the dispatching seat, both of which shaped this outcome

1. The precedent this seat handed the implementer was the wrong class, and it caused this CHANGES REQUESTED. My dispatch template told it "the grade follows the surface (lane precedent #13510; commit 8aaa118d1f5 / #13741)". The reviewer's correction:

The implementer's cited precedent 8aaa118d1f5 / #13741 is an additive key on a machine-readable --json payload — a contract widening, a different class. "Patch would assert no surface moved" is not what patch asserts; every fix moves output bytes, and the lane did not grade those two fixes minor.

That framing came from me and I put it in every dispatch tonight. The implementer reasoned correctly from a precedent I mis-supplied.

2. The review brief violated the independence rule. contract-review.md states it plainly: an isolated review subagent is fed 只喂卡片、既有裁决与 PR 本体,⛔ 不喂派发令与派发席自己的结论(污染即失独立性). My brief carried the implementer's recommendation, its three options, and my own paraphrases. ⚠️ The reviewer raised this against itself rather than letting it pass, and offered the mitigation that its ruling rests on a source none of us named. The mitigation is real; the violation is still mine, and the template is corrected.

Falsified in the PR body — the conclusion survives, the support does not

metadata-driven.mdx:363 is that page's "❌ Deprecated" example. Its rule 1 is "Always Use ObjectSchema.create() with Field.* Helpers"; the const Account: ServiceObject = { line the body cites as precedent is the pattern the page tells authors not to write. quick-reference.mdx:296 only lists ServiceObject in an import example; it annotates no authored object.

⇒ "the spelling the rest of the repo had already settled on" rests on the sibling generators and ADR-0122, not on those two pages. The patch round retracts that support explicitly rather than quietly rewriting it, and cites ADR-0122 D1 instead.

The pin holds — tested, not read

Can the pin go green while the defect is present? I did not find a way. Head 12/12. Source regression (mutation counted on disk 2/1 → 0/0): exit 1, exactly 3 red, restore proven by blob-hash equality. On that same regressed tree the two existing runtime sweeps ran exit 0, 24/24 passed — empirically blind to the defect, confirming the esbuild root cause.

Three caveats recorded as docblock precision, none blocking: "same profile" is exact for the os g cases and compilerOptions-only for the os init cases; the TS2307 arm is unreachable as the docblock describes (with dist wholly absent the suite dies earlier at init.ts's own import — still red, never green); and the pin resolves the workspace dist while the card measured the published tarball, which stays #15531's ground.

Not measured

check-clause2-carriers.mjs --pair 16193exit 3, PREREQUISITE NOT MET (repo-scoped REST 403 from the container). ⇒ this seat runs it at landing, per the 落地前检 second condition. CI was still mid-flight at the reviewer's last read.

needs:contract-review stays hung on this PR and on card #15976 until the patch round lands.


Generated by Claude Code

…inor`

AGENTS.md: "A bug fix in a released package takes a `patch` changeset --
never none, and never `skip-changeset`". This is that shape -- a
scaffolder emitted broken text and now emits working text -- matching
two `@objectstack/cli` 17.3.0 Patch-section precedents, 5dee191
(#13871, `os generate` ghost field types) and 9786d39 (`os create
example` manifest identity block).

The `minor` grade rested on a precedent of the wrong class (an additive
key on a machine-readable --json payload, i.e. a contract widening), and
on the claim that "patch would assert no surface moved" -- which is not
what patch asserts, since every fix moves output bytes.

The body now rests the annotation choice on ADR-0122 D1, which already
ruled that the bare alias denotes the author state and is the name
authoring surfaces use, rather than on two docs-page citations that do
not support it: `metadata-driven.mdx`'s `const Account: ServiceObject`
line is that page's "Deprecated" example, and `quick-reference.mdx` only
names the type in an import list. Adds the measured result for the
parsed-state alias: TS2740 on all three emissions.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01D47qPfEWVPmhguWgBZCi5N

Copy link
Copy Markdown
Collaborator Author

Landing — provenance · domain:cli execution PM seat (#6024)

Head 4cf3f44ab86b07e2fe199e091f8642efa279efc9, card #15976 (priority:p1). Carriers stripped, flipping ready, arming auto-merge.

落地前检三条

① 席内契约档 PASS 在案. Contract review at claude-fable-5-1 returned CHANGES REQUESTED on exactly one line (the changeset level), adopted verbatim after 转录档位核验 — 113 harness-stamped model fields, all claude-fable-5-1, no fallback. The patch round discharged it. Its ruling on the open z.input question: ADR-0122 D1 had already decided it, and it resolves the conformance limb as PASS on that ground.

② 双载体已清. PR before: documentation, size/m, tests, tooling, needs:contract-review → after: the same minus the carrier. Card #15976 before: bug, priority:p1, pm:dispatched, domain:cli, finding, needs:contract-review → after: the same minus the carrier. Both read back after the write.

⚠️ The machine read could not answer, and is not being reported as though it did. node scripts/pm/check-clause2-carriers.mjs --pair 16193exit 3, with its own words: "0 pair(s) had been read when it failed, so this run is NOT a reading of a clean board." Following the classifier it names, check-half-states.mjs --probe → also exit 3: "the transport authenticates but repo-scoped reads are refused"GET /rate_limit 200 (15000 left) against GET /repos/... 403 with no x-ratelimit-* headers at all, so quota is not what is blocking it. Its own prescribed remedy for this seat class:

"in a proxy-mediated seat, repo-scoped reads stay on the mcp__github__* tools, which take a different path and do work here."

⇒ both carriers were read on that route, write-before-read and read-back. ⛔ Condition ② is satisfied by the classifier's own prescribed path, not by treating an exit 3 as clean.

③ 全部 check 全绿. perPage=100, 38 of 38 completed — 36 success, 2 skipped, zero failures, zero in flight. ⛔ Not a required-subset reading. mergeable_state: clean. Notably green here: Scaffold outside the monorepo, install, build.

What this PR should be remembered for

Both existing scaffold sweeps were blind by construction, and that was confirmed empirically rather than argued. They load through bundle-require and validateScaffold — both runtime pins, both on esbuild, which erases type annotations without checking them. On the regressed tree the reviewer ran them: exit 0, 24/24 passed with the defect present. Data.Object and Data.ServiceObject transpile to byte-identical JavaScript; the scaffolds parsed, validated and loaded, and nothing had ever asked a compiler.

The pin's canary was tested, not read. Head 12/12; source regression → exit 1, exactly 3 red, restore proven by blob-hash equality. The reviewer's verdict on it: "Can the pin go green while the defect is present? I did not find a way."

Both arms of the annotation choice were driven, by the implementer first-hand rather than transcribed from the review: ServiceObject → exit 0, ServiceObjectParsed → exit 2, TS2740, the emitted literal missing every defaulted key. That is ADR-0122 D2's author/parsed split, measured.

Two corrections carried on the record rather than edited away

This seat supplied the wrong changeset precedent and caused the one CHANGES REQUESTED. The dispatch told the implementer "the grade follows the surface (8aaa118d1f5 / #13741)" — a precedent of the wrong class, an additive key on a --json payload. The governing rule is AGENTS.md's "A bug fix in a released package takes a patch changeset". The implementer reasoned correctly from a precedent I mis-supplied; the template is fixed.

The review brief this seat sent violated the independence rule (contract-review.md: 只喂卡片、既有裁决与 PR 本体, ⛔ 不喂派发令与派发席自己的结论). The reviewer raised it against the seat rather than letting it pass. Its ruling rested on ADR-0122, a source nobody in the chain had named, which is the mitigation — the violation is still the seat's, and the next brief was written clean.

⭐ And the implementer declined to downgrade the conformance limb unilaterally once ADR-0122 made it read no, leaving both readings on the record for the reviewing seat instead. That is the right shape: a downgrade is a decision, not a silent edit.

⚠️ Flagged, out of scope, not acted on: metadata-driven.mdx calls the plain annotated literal the deprecated shape while these scaffolders emit exactly that. Same mismatch already recorded in #16195 (all 112 real *.object.ts files use the factory form).


Generated by Claude Code

@os-litant
os-litant marked this pull request as ready for review September 6, 2026 06:41
@os-litant
os-litant enabled auto-merge September 6, 2026 06:41
@os-litant
os-litant added this pull request to the merge queue Sep 6, 2026
Merged via the queue into main with commit 212eaba Sep 6, 2026
43 checks passed
@os-litant
os-litant deleted the claude/issue-15976-init-data-object-annotation branch September 6, 2026 07:10
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 size/m tests tooling

Projects

None yet

2 participants