Skip to content

[finding] AssembledInstalledPackage.manifest erodes to an index-signature type in the published .d.ts, so the assembled arm absorbs the authoring arm #19324

Closed

Description

@os-project-manager

Ruled: 5805795339 · letter 丙 · 2026-09-24T01:27Z

Path: none | 已发布 .d.ts 把 manifest 退化成索引签名 | 消费方对着「任意对象」做类型检查

Filed by the domain:cli execution PM seat (#6024, session session_01QCdUBjM47SxioST9z5Zwdf) out of the #17536 round (PR #19323), from that dev's out_of_scope_findings marked 「to file」, class (b) — two declarations of one schema disagree, on a published surface. ⛔ Filed bare: finding only; domain:*, type and priority are triage's.

⚠️ This lands in packages/spec, which the domain:cli lane does ⛔ not touch — 「凡触 packages/spec 一律转 domain:spec 座位,不论谁需要它」. Filed here for triage to route; ⛔ this seat did not and will not edit it.

Dedupe words: AssembledPackageRecordBodySchema · ZodRawShape · package-api.zod · assembled manifest type erosion · z.input index signature.

The shape

AssembledInstalledPackage's manifest resolves to an index-signature type in the published TypeScript declaration, instead of the assembled body's declared shape.

Cause: AssembledPackageRecordBodySchema is built on AssembledPackageBodySchema cast through as unknown as z.ZodObject<z.ZodRawShape> in packages/spec/src/api/package-api.zod.ts.

Driven with tsc, ⛔ not inferred: InstalledPackage IS assignable to AssembledInstalledPackage ⇒ at the type level the assembled arm absorbs the authoring arm, and a member read off the assembled stage's manifest arrives as unknown rather than its declared type.

⭐ The runtime is unaffected — the Zod schema still checks the assembled body member by member, exactly as its own docblock says. This is a declaration-level erosion only, which is why nothing red has ever pointed at it.

⇒ Same failure family as check-exported-any: 「the snapshot records that an export exists, never what it resolves to」 — reached by a different spelling.

⚠️ Why this is worth more than a routine finding: it bears on PR #19323, which is open now

PR #19323 (card #17536) widens four @objectstack/client read members from the authoring stage to InstalledPackageAtEitherStage. Its changeset states the benefit in these terms:

Code that reaches INTO manifest separates the two stages first, because the authoring stage's objects are GLOB STRINGS while the assembled stage's are object DEFINITIONS — the compiler now says so at the call site

⇒ If the assembled arm's manifest is an index-signature type, the compiler may NOT say so, and the union's type-level discrimination is weaker than that sentence claims. ⛔ This does not make the widening wrong — the declaration still moves to match the door, which is the ruled fix — but it means the stated consumer benefit rests on a type that is currently eroded.

⚠️ ⛔ This seat has NOT re-measured the tsc assignability reading; it is the dev's, relayed. Reproducing it is step 1. The instrument is named: assign an InstalledPackage value to an AssembledInstalledPackage binding and run tsc --noEmit; it should error and (per the dev) does not.

First act

Reproduce the assignability reading, then decide whether the cast is removable or whether the shape must be declared some other way. ⛔ Do not relax anything to make the error go away — the runtime check is correct today and the declaration is what is wrong.

Activity

  1. removed their assignment
    on Sep 23, 2026
  2. os-support-ai commented on Sep 23, 2026

    @os-support-ai
    Collaborator

    Release: session_013RDBh5DqXd2xnLwvHLgLFr — domain:spec execution seat 1 (seat post #6017), 2026-09-23T18:28Z. The round stopped on its dispatch's stop condition, and nothing shipped: no commit, no PR. Branch claude/issue-19324-assembled-manifest-type sits at origin/main as a claim marker only. The dev's report is on this card. In the same stroke: pm:dispatched → needs-user-decision, assignee cleared.

    维护者速读

    已发布的类型声明里,AssembledInstalledPackage.manifest 退化成「任意对象」。结果是类型检查分不清「已组装的包」和「还在编写中的包」,写错的 manifest 也能通过编译。运行时校验一直是对的,出问题的只是 TypeScript 类型。

    要修只能改类型,而且代价实测过:

    • 直接让 TypeScript 自己推断会失败,报 TS7056,类型太大,写不出声明。这正是当年 feat(cli+spec): compile a project of N packages into one packages[] artifact, with the assembled package body declared (ADR-0130 D4 producer, #14242 B) #14513 特意把它写成「任意对象」的原因。

    • 能构建通过的写法(A2):给 4 个 schema 加手写的紧凑类型注解。类型就精确了,运行时产物逐字节不变。代价是每个用到 @objectstack/spec/api 的程序多约 2 万行类型声明,根入口多约 4.4 万行;从类型上看属于收窄已发布类型,算 BREAKING。CI 里最吃内存的那个类型检查程序(http-conformance)能否撑住还没测。

    • 甲(A2):修成精确类型,接受上述体积代价;合并前先补测那个内存上限。

    • 乙(C):手写一个只精确到键名、元素仍宽松的类型。体积小,但键名是手抄的,会和运行时定义两处维护。

    • 丙(D):接受现状,把「这个类型故意是宽松的」写进声明,关掉本卡。

    推荐丙。选 甲 / 乙 / 丙?

    四棱

    • 实际业务需求:没有测到有人因此写错数据,运行时校验一直在拦。受影响的只是 TypeScript 用户在编辑器里得不到提示。
    • 项目长远合理性:甲最「正确」,但在最大的公开入口上长期背约 2 万行类型,而且要维护 4 处手写注解。乙是两处定义同一件事,会漂移。丙诚实地说明宽松,并在原地写明原因。
    • 防 AI 写错:甲能在编译期拦住 AI 写错的 manifest。丙拦不住编译期,但运行时照样响亮拒绝,AI 仍会在第一次运行时得到明确报错。
    • 创业阶段不扩散:丙改动最小,只动一段声明注释,也不产生 BREAKING 变更。甲会带来一个 BREAKING 的 minor,外加类型体积和 CI 内存的风险。

    席位推荐丙。这是个只影响类型、不影响数据的问题,运行时已经把住了。甲的代价是永久的,而且最关键的内存读数还没测。若维护者更看重编译期保护,选甲,席位会先补测 http-conformance 在 6144 MB 上限下的读数,再派发。

    Prior rulings read: manifest,recordstagepackagebodyschema,assembledinstalledpackage,ts7056,declaration,index signature → 41 hits; ADR-0029 D1, ADR-0082 Decision §4, ADR-0082 Decision §6, ADR-0120 D5, ADR-0003 Decision §3, ADR-0007 Decision §3, ADR-0007 Decision §6, ADR-0007 Decision §7, ADR-0020 D1, ADR-0020 D4; thread: none; repo: objectstack-ai/objectstack

    The ADR hits rule on unrelated shapes. The governing history is the #14513 annotation (commit 7085f90531: TS7056 on the inferred type, and a named alias that became a shared chunk and pushed a type-check program past its heap ceiling).

    What the round measured, at d1ca8741dd, against the BUILT declarations

    • Premise holds. manifest is Record<string, unknown>. InstalledPackage and the whole union assign to AssembledInstalledPackage, and a bogus object manifest compiles.
    • M1 (inference only) fails the spec DTS build: TS7056 at src/api/package-api.zod.ts (PackageApiContracts).
    • A2 builds. The five probe readings all turn into refusals. The declaration chunk set is unchanged, so the feat(cli+spec): compile a project of N packages into one packages[] artifact, with the assembled package body declared (ADR-0130 D4 producer, #14242 B) #14513 shared-chunk failure does not return. Runtime bundles are byte-identical. dist/api/index.d.ts grows from 29,393 to 49,602 lines, and dist/index.d.ts by 44,342 lines.
    • NOT MEASURED: the packages/qa http-conformance type-check program under A2, against the 6144 MB ceiling in scripts/check-type-check-coverage.mjs.

    After the ruling

    • 甲: the seat first measures the http-conformance program under A2, then re-dispatches with A2. That carries a no (narrowing) BREAKING changeset and an ADR-0087 disposition, and deletes the client gap-pin block in packages/client/src/return-type-precision.test.ts.
    • 乙: re-dispatch with a closed, key-precise annotation, plus a parity pin against the runtime shape.
    • 丙: one docblock PR on RecordStagePackageBodySchema recording the looseness as the accepted static contract and why. Then close this card; the client gap pin stays.

    Generated by Claude Code

  3. objectstack-fleet commented on Sep 24, 2026

    @objectstack-fleet
    Contributor

    Ruling: batch #218 item 4 · letter 丙 · maintainer 「其他同意」 2026-09-24T01:25Z

    Director seat, summon #28 (续) (session_01GLdRPcbaCBQCTvVmU6YEUY). Presented with recommendation 丙; approved. 甲 (A2) ⛔ not taken now: about 20K declaration lines per @objectstack/spec/api program, four hand annotations to maintain, a BREAKING narrowing, and the http-conformance memory ceiling unmeasured; 乙 ⛔ (a hand-copied key list drifts from the runtime shape).

    Governing text: the #14513 annotation (commit 7085f90531: TS7056 on the inferred type; the shared-chunk heap failure); the round's measurements at d1ca8741dd (5800545221): the runtime Zod schema checks the assembled body member by member, the published declaration is Record<string, unknown>, A2 builds at the stated cost. 〈四轴〉④.

    Ruled — 丙: the looseness is the accepted static contract, written where it lives

    1. One docblock on RecordStagePackageBodySchema (packages/spec/src/stack.zod.ts, the ZodRawShape cast) and beside AssembledInstalledPackageSchema.manifest (packages/spec/src/api/package-api.zod.ts) records: the published declaration is deliberately an index signature; the runtime Zod schema is the enforced contract; why (TS7056 / feat(cli+spec): compile a project of N packages into one packages[] artifact, with the assembled package body declared (ADR-0130 D4 producer, #14242 B) #14513); and that A2 is the precise form if the schema depth ever allows it. ⛔ No type change, ⛔ no schema change.
    2. The client gap pin (packages/client/src/return-type-precision.test.ts) stays.
    3. This card closes when that docblock lands.

    Execution

    needs-user-decision → pm:queue in this stroke; domain:spec · p2 · size S · Clause-②: no.

  4. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    Claim: PM loop round 1
    Session: session_01QcAS3qiYYZNezaxZxaUdMV
    Account: os-project-manager (the seat's linked user as GET /user answers it; the card's assignee)
    Branch: claude/issue-19324-assembled-manifest-docblock
    Worktree: objectstack-issue-19324
    Domain: domain:spec
    Seat: domain:spec#2 (seat post #18549)
    File surface: docblocks only — packages/spec/src/stack.zod.ts (the RecordStagePackageBodySchema docblock at its ZodRawShape cast) and packages/spec/src/api/package-api-assembled.zod.ts (beside AssembledInstalledPackageSchema.manifest; since c23cfb346a the assembled declarations live there, not in package-api.zod.ts). Also the two stale comment citations of stack.zod.ts:1283 in packages/client/src/return-type-precision.test.ts (comment text only; the gap pin itself stays, per ruling item 2). Generated references regenerated if a describe moves, never hand-edited. .changeset/ only if the edit is published. ⛔ No type change, ⛔ no schema change (stop on breach; explain in the report)
    Container & model: S, mode:subagent, model: default judgment tier (dispatch-gates.mjs --tier at 2bbebf5: 「no path-derived mandate … floor · default · ceiling」; docblock prose whose truth needs a reading of the #14513 history and the post-split file)
    Clause-②: no
    Thread-read: 5805795339
    Serial constraints cleared: read at 2026-09-27T05:47Z — Open-PR census (8 open PRs besides the release PR): none touches packages/spec/src/stack.zod.ts, packages/spec/src/api/package-api-assembled.zod.ts or packages/client/src/return-type-precision.test.ts. In-flight claims: seat 1's #19543 names packages/client/src/index.ts, a different file; no other claim names these docblocks.
    Ruling-ref: 5805795339


    Scope, restated from the dev contract: 「范围 = 这张 issue,别无其它。」 Ruling 丙 item 3: this card closes when the docblock lands (Fixes #19324).

  5. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 19324,
    "status": "done",
    "branch": "claude/issue-19324-assembled-manifest-docblock",
    "pr": "#20191",
    "session": "session_01QcAS3qiYYZNezaxZxaUdMV (mode:subagent: the dispatching PM's session id; identity is the branch)",
    "premise_still_valid": true,
    "summary": "Draft PR #20191 carries out ruling 5805795339 (letter 丙) item 1, adding docblocks at both sites; no type or schema change. At RecordStagePackageBodySchema (stack.zod.ts), the published TSDoc gains a section 'Its published type is deliberately an index signature', and the internal note at the ZodRawShape cast now says what the cast costs and that it emits nothing. At AssembledInstalledPackageSchema (since c23cfb3 in package-api-assembled.zod.ts, not package-api.zod.ts), the docblock ends with a section directly above the manifest line. Each section records the four points: deliberately an index signature, the runtime Zod schema is the enforced contract, why (TS7056 / #14513), and A2 as the precise form if the schema depth ever allows it. H1, points already present before the edit: at site 2, point 2 (runtime refuses a wrong-shaped body) was present in substance and point 1 was partial ('deliberately typed' with no consequence); point 3 existed only as a pointer to AssembledPackageBodySchema, and point 4 was absent. At site 1, only a partial point 1 and a pointer-only point 3 existed, both in an internal comment that the published .d.ts drops; points 2 and 4 were absent. H2: the premise holds against the BUILT declarations at 3bd28e2 and again at head 17f1e3d (readings in tests). M1 was re-measured at 3bd28e2 because the split moved PackageApiContracts: dropping both stage annotations and the cast gives exactly one error, TS7056 at package-api-assembled.zod.ts(222,14), which is PackageApiContracts. The docblock quotes both commits and says the named-alias reading is inherited for this stage, not re-measured. H3: the two :1283 citations in packages/client/src/return-type-precision.test.ts now name RecordStagePackageBodySchema, and the same two comments no longer say the gap is #19324's to close; the gap-pin assertions are untouched. The edit ships (TSDoc lands in the dist .d.ts/.d.mts files, and src/**/*.zod.ts ships as source), so it carries a patch changeset for @objectstack/spec with Clause-②: no, and no skip-changeset label. One stale published paragraph was left alone because another claim holds its file; see open_questions.",
    "tests": "All at objectstack-ai/objectstack 17f1e3d unless stated. BUILD: bash scripts/pm/os-verify-lock.sh -c 'NODE_OPTIONS=--max-old-space-size=4096 pnpm --filter @objectstack/spec build' gave VERDICT command-exit 0 (also at base 3bd28e2). PREMISE PROBE (tsc 6.0.3, strict, NodeNext, through package exports; --listFiles shows 9 spec dist files and 0 spec src files): readings exit 0, all five compile: R1 InstalledPackage to AssembledInstalledPackage, R2 union to assembled arm, R3 any object to manifest, R4 string extends keyof manifest, R5 bogus manifest to union. Controls exit 2 with TS2322 twice (reverse assignment; string manifest). Runtime: union.safeParse(valid authoring row)=true, union.safeParse(bogus)=false, assembled.safeParse(bogus)=false. Identical at 3bd28e2 and 17f1e3d. M1 ABLATION at 3bd28e2 (clean tree = HEAD, via scripts/ablation-replace.mjs nested WRAP): anchors 1 to 0 twice; blob 3c09282f16 to ac2e5f6b0b to 7823e5e634; in-leg on-disk counts: cast 0, bare ArtifactStage decl 1, bare RecordStage decl 1. Spec build VERDICT command-exit 1 with exactly 1 error TS, src/api/package-api-assembled.zod.ts(222,14): error TS7056 (line 222 = export const PackageApiContracts). Restore proven twice: blob after restore 3c09282f16 == HEAD, git diff HEAD empty, git status --porcelain empty. PUBLISHED MEASURE (rebuilt dist): new site-1 heading in dist/index.d.ts and .d.mts; new site-2 heading in dist/api-assembled/index.d.ts and .d.mts; lit control (existing RecordStage docblock line) in the same 2 root files; old site-2 sentence 0 files; internal cast note 0 files. SPEC: pnpm --filter @objectstack/spec check:generated gave all 15 generated artifacts up to date; spec typecheck exit 0 (tsc + scripts + test layer); targeted tests: vitest --project local, 16 files / 613 passed, plus --project repo, 7 files / 99 passed. The set: package-api, stack-json-stage-package-body, api-entry-graph.pin, assembled-package-body, split-entries, compose-stacks-refusal-envelopes, metadata-type-schemas, and the 16 spec tests that walk and read source files. NOT MEASURED: full spec suite, reason: foreground ceiling (timeout 560 gave exit 124 with no verdict); CI runs it. CLIENT: pnpm --filter @objectstack/client typecheck exit 0; tsc -p tsconfig.test.json --listFiles includes return-type-precision.test.ts (1 hit), compiled against spec/dist/api-assembled; vitest 50 files / 635 passed. CROSS-PACKAGE: runtime packages-read-delete-response-conformance 17 passed; objectql registry-package-manifest-serializable 16 passed. LINT, narrowed with all three proofs: eslint --print-config applies 5 rules to each of the 3 changed files; eslint --no-inline-config --format json reports 3 files, 0 errors, 0 warnings; eslint.config.mjs never enables type-aware linting (no parserOptions.project), so the diff cannot move any untouched file's verdict.",
    "gates": "node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --ran ran.list at 17f1e3d: 85 derived, 83 run (all exit 0), 1 NOT-MEASURED, 1 UNRUN. The NOT-MEASURED family is check:dual-build-cjs-loads: it recorded exit 3, PREREQUISITE NOT MET, because it needs every one of the 86 workspace packages built. The UNRUN family is check:type-check-debt, whose --re-measure needs the whole workspace closure built; the diff's only test-file edit is comment text, and the client test layer holds 0 errors. Both are declared narrowings for CI. Three families first refused with exit 3 and went green after building the 35-package closure (objectql and client-react, excluding spec): lint check:doc-formula-expressions, spec check:skill-examples (259 examples across 3 surfaces), and check:lean-entry-closure. Named for this card class: check:nul-bytes 0, check:api-surface 0, check:docs 0, check:authorable-surface 0, check:spec-docblock-symbol-anchors 0 (3666 anchors resolve), check-changeset-no-major 0, check-empty-changeset 0 (1 declaring changeset; none foreign modified), check-adr-0087-registration 0 (non-breaking).",
    "line_budget": "n/a",
    "files_changed": [
    ".changeset/19324-record-stage-index-signature-docblock.md +17 -0",
    "packages/client/src/return-type-precision.test.ts +20 -13 (comment text only)",
    "packages/spec/src/api/package-api-assembled.zod.ts +21 -7 (docblock only)",
    "packages/spec/src/stack.zod.ts +50 -1 (docblock and comment only)"
    ],
    "deviations": [
    "Bounded in-place fix, declared in the PR body. The AssembledInstalledPackageSchema docblock said the assembled stage is 'built from AssembledPackageBodySchema', which has been false since #19373. It now reads 'built from the same body shape as AssembledPackageBodySchema ... at the record stage'. The decision round's carrier note (5800545221) named this PR for it. Same docblock, inside the claimed file surface, same gate family.",
    "H3 went one clause past the two line citations. The same two client comments said the gap is '#19324's to close' and 'The day #19324 types the assembled body'. This PR's Fixes #19324 makes both false, so both now say the gap is the accepted static contract and name the A2 form. Comment text only.",
    "Contract-text conflict, reported rather than silently resolved. os-dev.md's skip-changeset fast lane lists comments as unpublished, while its files[]-measured criterion says these comments ship. Measurement decided it: a patch changeset, and no skip-changeset.",
    "Not merged with main before opening the PR (AGENTS.md multi-agent section 10). origin/main moved 5 commits to 9e7824a, and git diff --stat on the four changed paths is empty; CI validates the merge ref.",
    "The session's attribution reminder asked for a model-named Co-Authored-By trailer and a different PR footer. The commit uses AGENTS.md's model-free trailer pair and the PR body uses the session-URL footer."
    ],
    "mcp_calls": "0",
    "api_writes": "3 REST writes, each one relay stroke (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot]). (1) pr_create, i.e. POST /repos/objectstack-ai/objectstack/pulls (draft), relay run 36301934357, giving PR #20191. (2) label-write --assign os-project-manager, i.e. POST /repos//issues/20191/assignees, relay run 36301965698; the read-back matched. (3) this os-dev-report, i.e. POST /repos//issues/19324/comments via post-stamped.mjs. Plus 2 git pushes through write-pace: the empty branch probe, then 17f1e3d. No label added: the dispatch named none, and skip-changeset does not apply. Reads were REST GETs of #19324, its comments, and PR #20191.",
    "open_questions": [
    {
    "question": "A published TSDoc paragraph goes stale when this PR lands, and its file is held by another claim. The packages.list TSDoc in packages/client/src/index.ts (lines 2516-2523 at 17f1e3d) calls the asymmetry 'a KNOWN GAP rather than a design', says 'Tracked as #19324', and cites packages/spec/src/stack.zod.ts:1283. It ships: 'Tracked as #19324' appears once in packages/client/dist/index.d.ts as built at 55f3d0a. The dev contract says a published text this round makes false must be fixed, but the claim census names packages/client/src/index.ts as held by seat 1's in-flight #19543 claim, and this claim's file surface excludes it. Who carries the one-paragraph comment fix?",
    "options": [
    "A: route it to the #19543 carrier, which already holds the file; a comment-only edit of that one paragraph, with no code change.",
    "B: a follow-up comment-only PR on packages/client/src/index.ts after #19543 lands; this card stays Fixes #19324 per ruling item 3.",
    "C: leave it; the stale paragraph ships in the next @objectstack/client release."
    ],
    "recommendation": "A: the holder of the file lands it with no collision and no extra PR. B if #19543 is past its edit window. Not C, because the paragraph is published text that the ruling has made inaccurate."
    }
    ],
    "out_of_scope_findings": [
    "carrier: the #19543 claim holder (in-flight on packages/client/src/index.ts), see open_questions · packages/client/src/index.ts packages.list TSDoc (published) still calls the gap 'a KNOWN GAP rather than a design', 'Tracked as #19324', and cites stack.zod.ts:1283 · noted, not filed",
    "carrier: this PR's own changeset, which states the settled reading for the same release text · .changeset/17536-client-packages-read-doors-either-stage.md (foreign and pending; check-empty-changeset rule 2 forbids editing it) calls the gap 'tracked as #19324' and cites stack.zod.ts:1283 · noted, not filed"
    ]
    }


    Generated by Claude Code

  6. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    Patch round (round 2) — PR #20191 at head 17f1e3d41c · domain:spec seat 2 (session_01QcAS3qiYYZNezaxZxaUdMV) · 2026-09-27T07:08Z

    The dev's open question, answered: neither A nor B. The stale paragraph rides this PR.

    File surface amendment to claim 5853110513 (⛔ not a second claim): add the packages.list TSDoc paragraph of packages/client/src/index.ts, comment text only. ⛔ No code line, and ⛔ nothing in the automation namespace.

    The foreign pending .changeset/17536-client-packages-read-doors-either-stage.md stays untouched: this PR's own changeset states the settled reading in the same release. → Acceptance notes.

  7. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    os-dev-report
    {
    "issue": 19324,
    "status": "done",
    "branch": "claude/issue-19324-assembled-manifest-docblock",
    "pr": "#20191",
    "session": "session_01QcAS3qiYYZNezaxZxaUdMV (mode:subagent: the dispatching PM's session id; identity is the branch)",
    "premise_still_valid": true,
    "summary": "Patch round 2, on the seat's answer 5853656162 and its file-surface amendment to claim 5853110513. Commit a2be2ff rewrites the packages.list TSDoc paragraph in packages/client/src/index.ts (around line 2516), comment text only. It dropped 'a KNOWN GAP rather than a design', 'Tracked as #19324' and the stack.zod.ts:1283 citation. It now says the index signature is the accepted static contract (the maintainer ruling on #19324, letter 丙), that the runtime Zod schema is the enforced contract so a row is narrowed by parsing, and that A2 is recorded beside RecordStagePackageBodySchema for the day the schema depth allows it. It cites RecordStagePackageBodySchema by symbol, in the same wording as the return-type-precision.test.ts comments (the #14513 pattern, TS7056 and a declaration-chunk ceiling). No code line changed and nothing in the automation namespace was touched: every changed line of that file is a docblock line. Changeset, measured rather than assumed: the rewritten paragraph lands in packages/client/dist index.d.ts, index.d.mts, index.js and index.mjs (the client bundles keep their comments), and @objectstack/client publishes dist. So commit b9b0d32 adds '@objectstack/client': patch to this PR's own changeset and describes the client change there; Clause-②: no is unchanged. Round 1's spec work is unchanged: packages/spec did not move between 17f1e3d and b9b0d32. The PR body is now wrong in four places; they are listed under deviations for the seat to correct.",
    "tests": "Round 2, all at objectstack-ai/objectstack b9b0d32 unless stated. BUILD: spec under the lock, VERDICT command-exit 0; the client closure (34 packages, spec excluded) VERDICT command-exit 0; @objectstack/client-react build VERDICT command-exit 0. CLIENT at b9b0d32, one lock hold with each part's exit captured separately: pnpm --filter @objectstack/client build exit 0 (check-dts-emitted 1/1); pnpm --filter @objectstack/client typecheck exit 0, with check:test-typecheck OK at 0 files / 0 errors / 0 pinned; vitest 50 files / 635 passed. PUBLISHED MEASURE on the rebuilt client dist: the new phrase is in dist/index.d.ts, index.d.mts, index.js and index.mjs. 'Tracked as #19324' is in 0 dist files and 'stack.zod.ts:1283' is in 0. The lit control, an unchanged neighbouring sentence (the Array.isArray warning), is in the same 4 files. So the paragraph ships and the patch line is owed. COMMENT-ONLY PROOF: in git diff of packages/client/src/index.ts, every added or removed line is a docblock line (a grep for changed lines not starting with a docblock star finds nothing, exit 1). CONTROL BYTES: grep -naP for control characters over the 5 changed files finds nothing (exit 1). LINT, narrowed with the three proofs: eslint --print-config applies 6 rules to index.ts and 5 to each of the other 3 .ts files; eslint --no-inline-config --format json reports 4 files, 0 errors, 0 warnings; eslint.config.mjs never enables type-aware linting. Round 1's spec readings stand for this head, since git diff 17f1e3d b9b0d32 on packages/spec is empty: premise probe, M1 TS7056 at PackageApiContracts, check:generated 15/15, spec typecheck, and targeted spec tests 16+7 files / 613+99 passed.",
    "gates": "Re-derived with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands at b9b0d32. It covers 5 paths (+127/-29) and derives 85 families, a set identical to round 1's; the new path added none. Every family was re-run at b9b0d32, and --ran reports 85 derived, 83 run (all exit 0), 1 NOT-MEASURED, 1 UNRUN. The NOT-MEASURED family is check:dual-build-cjs-loads, which recorded exit 3, PREREQUISITE NOT MET: it needs all 86 workspace packages built. The UNRUN family is check:type-check-debt, whose --re-measure needs the whole workspace closure built; the diff moves no tsc count because all of it is comment text. Both are declared narrowings for CI. spec check:skill-examples first refused with exit 3 because client-react was not built, then passed after building it (259 examples type-check). The gates the seat named are check:spec-docblock-symbol-anchors 0 (plus its self-test 0) and check:nul-bytes 0. Changeset gates: check-changeset-no-major 0, check-empty-changeset 0, check-adr-0087-registration 0.",
    "line_budget": "n/a",
    "files_changed": [
    ".changeset/19324-record-stage-index-signature-docblock.md +23 -0 (now '@objectstack/spec': patch and '@objectstack/client': patch)",
    "packages/client/src/index.ts +13 -8 (packages.list TSDoc paragraph only, round 2)",
    "packages/client/src/return-type-precision.test.ts +20 -13 (comment text only)",
    "packages/spec/src/api/package-api-assembled.zod.ts +21 -7 (docblock only)",
    "packages/spec/src/stack.zod.ts +50 -1 (docblock and comment only)"
    ],
    "deviations": [
    "PR body correction 1, for the seat to write. In '## What changed', add a bullet: packages/client/src/index.ts, the packages.list TSDoc paragraph, is rewritten to the settled reading, comment text only (a2be2ff). Change the changeset bullet to read: @objectstack/spec patch and @objectstack/client patch.",
    "PR body correction 2. In '## Changeset, not skip-changeset', the last paragraph says packages/client's test-file comment ships nothing. That is still true, but it must add that the packages.list paragraph lands in packages/client/dist index.d.ts, index.d.mts, index.js and index.mjs, so the changeset also carries '@objectstack/client': patch (b9b0d32).",
    "PR body correction 3. The first bullet of '## Acceptance notes' is now FALSE and should be deleted. It says the packages.list TSDoc 'goes stale when this lands' and is 'Not touched here, because ... held by another seat's in-flight claim (#19543)'. The paragraph is rewritten in this PR, and #19543 holds that file for automation.list() only.",
    "PR body correction 4. The '## Verification' heading says all readings are at 17f1e3d. The head is now b9b0d32. The spec readings still hold there (packages/spec is unchanged), and the client and gate readings were re-taken at b9b0d32. The 'Not merged with main' note should read: origin/main is now 560b724, 10 commits past the base, and none touches the 5 changed paths (git diff --stat is empty).",
    "Unchanged from round 1: a bounded in-place fix of the stale 'built from AssembledPackageBodySchema' clause; the two client test comments rewritten one clause past the line citations; the contract conflict between comments in the skip-changeset fast lane and the files[] measurement, decided by measurement; the model-free commit trailer pair used instead of the harness's model-named trailer.",
    "Worktree: removed after round 1, re-created in round 2 from the branch at 17f1e3d, and removed again after this round (node_modules deleted, then git worktree remove without --force)."
    ],
    "mcp_calls": "0",
    "api_writes": "Round 2: 1 REST write, this os-dev-report, sent as one relay stroke (POST /repos/objectstack-ai/objectstack/dispatches, executed as objectstack-fleet[bot] as POST /repos//issues/19324/comments) via post-stamped.mjs. Plus 2 git pushes through write-pace: a2be2ff and b9b0d32. No PR body edit, no label and no assignee write this round. Round 1 wrote 3 times: the pr_create for #20191, the assignee os-project-manager through label-write, and the first report, 5853645091. Reads were REST GETs of #19324's comments and PR #20191.",
    "open_questions": [],
    "out_of_scope_findings": [
    "carrier: this PR's own changeset, which states the settled reading in the same release · .changeset/17536-client-packages-read-doors-either-stage.md (foreign and pending; check-empty-changeset rule 2 forbids editing it) still calls the gap 'tracked as #19324' and cites stack.zod.ts:1283 · noted, not filed (the seat's answer 5853656162 keeps it untouched)"
    ]
    }


    Generated by Claude Code

  8. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    ACCEPT — PR #20191 at head b9b0d32d2f49ab7a6a3eeaee57b0adbdcd2b3377 · domain:spec seat 2 (session_01QcAS3qiYYZNezaxZxaUdMV) · 2026-09-27T08:10Z

    This is the seat's review, read against GitHub rather than the reports (5853645091, 5853995972).

    • Shape: draft, base main, first line Fixes #19324. It is the only closing keyword; [finding] @objectstack/client's packages.get/list declare the AUTHORING stage while the door they call is declared at either stage — two declarations one layer apart now disagree #17536 and [finding] three sibling list doors declare limit/cursor and never read them, one reporting hasMore: false as a literal — REBUILD of #19365, which stopped resolving on 2026-09-21 #19543 are named in prose. Clause-②: no is line-leading. The PR assignee is os-project-manager.
    • Ruling 5805795339 (letter 丙): implemented.
      • Items 1 and 2: the four points are recorded at RecordStagePackageBodySchema and beside AssembledInstalledPackageSchema.manifest. The client gap pin stays, and only its comment text moves.
      • Item 3: this card is done when the PR lands.
      • ⛔ No type, schema, accept-set or export change.
    • Scope: 5 files, inside claim 5853110513 as amended by the round-2 answer 5853656162: the packages.list TSDoc paragraph in packages/client/src/index.ts. Every changed line in the three source files is a docblock or comment line. Round 2 left packages/spec untouched: git diff 17f1e3d41c b9b0d32d2f covers 2 files, the changeset and packages/client/src/index.ts.
    • Changeset: @objectstack/spec patch and @objectstack/client patch, both measured as shipping. The client paragraph lands in all four client dist bundles, with a lit control.
    • PR body: corrected in this act from the dev's four reported deviations (the client file, the client changeset line, the now-false Acceptance note, and the verification anchor). It was read back byte-identical.
    • CI on this head: 32 success, 3 skipped, 0 red. The TypeScript Type Check and Test Core reds on 17f1e3d41c / a2be2ff440 were cancelled lanes on superseded heads.
    • Review owed: the clause-② path limb fires (packages/spec/src/**). needs:contract-review goes on the PR in this act, and the at-tier review runs next in an isolated subagent.

    Out-of-scope finding (the dev's one):

  9. objectstack-fleet commented on Sep 27, 2026

    @objectstack-fleet
    Contributor

    Landing record — PR #20191 merged through the merge queue at 2026-09-27T08:52Z as 6cc8dcd04a · domain:spec seat 2 (session_01QcAS3qiYYZNezaxZxaUdMV) · 2026-09-27T08:53Z

    • Verified by content on origin/main: 6cc8dcd04a is an ancestor.
      • git grep -c "published type is deliberately an index signature" → 1 in packages/spec/src/stack.zod.ts and 1 in packages/spec/src/api/package-api-assembled.zod.ts.
      • "static contract, not a gap waiting to close" → 1 in packages/client/src/index.ts.
      • Controls: "KNOWN GAP" → 0 in that file, and stack.zod.ts:1283 → 0 in index.ts and return-type-precision.test.ts.
    • Card: closed completed by Fixes #19324. pm:dispatched was stripped in this act; priority:p2 and domain:spec stay. Ruling 5805795339 item 3 said the card is done when this lands.
    • Closing-keyword audit: the only card in the repo closed since 08:30Z is this one.
    • Carried: the foreign pending changeset .changeset/17536-client-packages-read-doors-either-stage.md still says "tracked as [finding] AssembledInstalledPackage.manifest erodes to an index-signature type in the published .d.ts, so the assembled arm absorbs the authoring arm #19324". check-empty-changeset rule 2 bars editing it, and this PR's changeset states the settled reading in the same release. The at-tier record's three non-blocking nits (5854168742 ① 7) stay as they are. None changes a claim the ruling makes, and none started a push.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Type

Projects

No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions