Skip to content

docs(spec): split the install door's residual clause 1 into its closed version half and open type half - #19935

Merged
objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-19327-install-door-residual-split
Sep 24, 2026
Merged

objectstack-fleet[bot] merged 3 commits into
mainfrom
claude/issue-19327-install-door-residual-split

Conversation

@objectstack-fleet

@objectstack-fleet objectstack-fleet Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

Fixes #19327

Clause-②: no

PackageInstallBodySchema's docblock in packages/spec/src/api/package-api.zod.ts lists the bodies POST /api/v1/packages answers 201 to while the declaration refuses them. Its clause 1 recorded "a manifest missing type and/or version" as ONE class. PR #19326 made the door parse ManifestSchema.shape.version by reference, so only the type half of that clause is still true. This PR splits the clause.

The diff is the docblock, one spec test file and one changeset. No schema, accept set, export or runtime code moves, and residual classes 2 to 5 are untouched.

Both halves re-measured at origin/main fdeeea0cc9, before any edit

Both halves read exactly as the card says.

half body sent to the door status error.code installed
control wrapped, well-formed 201 none yes
version wrapped, no version 400 VALIDATION_ERROR no
version bare, no version 400 VALIDATION_ERROR no
type wrapped, no type 201 none yes
type bare, no type 201 none yes

How it was driven. I used a one-shot probe, deleted right after the reading and never committed. It was a vitest file at packages/runtime/src/domains/probe-19327-oneshot.test.ts with the harness of packages-install-manifest-version.test.ts: a real HttpDispatcher, spied protocol and registry install writers, and OS_HOME redirected. It called handlePackages('', 'POST', body, {}, admin) once per row and printed status, code and whether either writer was called. It ran in one invocation with the existing drives that the docblock's parenthesis «both door drives above» points at:

bash scripts/pm/os-verify-lock.sh -c "pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 src/domains/probe-19327-oneshot.test.ts src/domains/packages-install-manifest-version.test.ts src/domain-handler-registry.test.ts src/package-door-namespace-conflict-code.test.ts"
  Test Files  4 passed (4)
       Tests  82 passed (82)
  os-verify-lock: VERDICT command-exit 0
  • The type half, from both existing drives. Each drive posts a manifest with no type, and each asserts that the door answers 201:
    • package-door-namespace-conflict-code.test.ts posts { id, name: id, namespace, version: '1.0.0' }.
    • The duplicate-id case in domain-handler-registry.test.ts posts { id: 'com.example.pkg-a', name: 'A', version: '1.0.0' } (:600): 409 first, then 201 on ?overwrite=true. The body { id: 'pkg-a', name: 'A', version: '1.0.0' } has been that file's REVERSED pin since PR fix(runtime): POST /api/v1/packages parses the manifest's id leg #19473 and is answered 400 (:622-623), so it is not a residual.
  • The version half, from fix(runtime): POST /api/v1/packages parses the manifest version leg instead of installing anything it is handed #19326's door test. §1 of packages/runtime/src/domains/packages-install-manifest-version.test.ts pins 400 + VALIDATION_ERROR + neither writer called, on both body forms. It is cited, not duplicated.

What changed

packages/spec/src/api/package-api.zod.ts (docblock only)

packages/spec/src/api/package-api.test.ts (the pin of the clause)

  • DOOR_DRIVE_REGISTRY now transcribes the body the drive posts: { id: 'com.example.pkg-a', name: 'A', version: '1.0.0' }. Its comment credits both repairs, and its it title drops "no version".
  • The control that rested on the stale id is now «the missing type is what decides it, for BOTH drives — the registry drive's old id is refused on its own». It asserts that the registry drive parses once type is added, as the conflict drive does. The old pkg-a reading is kept, pointed at the old body: { ...registryKeysCompleted, id: 'pkg-a' } is refused on the id alone, and the green parse just above it is its lit control.
  • The residual list is hoisted to DOOR_201_RESIDUALS, and the existing "the door answers 201 to all of them anyway" assertion is unchanged over it.
  • New pin: «✅ clause 1a is CLOSED — every body in the live residual carries a version the declaration accepts». It checks each residual's manifest with ManifestSchema.shape.version. Its lit control is that undefined is refused. It cites the door-side pin rather than repeating it, because this package cannot import the door.

.changeset/19327-install-door-residual-split.md: patch for @objectstack/spec

A changeset is owed, not skip-changeset, because files[] ships src/**/*.zod.ts and the docblock is also emitted into the built declarations. After the build, the new clause text is in dist/api/index.d.ts and dist/api/index.d.mts, and the old spelling "and/or version" hits 0 across dist and src. The bump is patch because it is a text correction in a released package, with no API change.

Note that npm @objectstack/spec@17.4.0 has no PackageInstallBodySchema at all: grep 0, with PackageInstallRequestSchema = 4 as the control in the same file. The stale clause has therefore not shipped yet. The next release would be the first to carry it.

Verification, round 1 (head 826e612b39)

Reverse verification. The fix was committed first. I then restored the old drive transcription { id: 'pkg-a', name: 'A' } through scripts/ablation-replace.mjs, with the anchor counted 1 → 0, the replacement 0 → 1, and the blob e976bff69a → 5b952ed89a.

Spec package

  • Test: pnpm --filter @objectstack/spec exec vitest run --project local --maxWorkers=2 gives Test Files 527 passed (527), Tests 15504 passed | 1 todo, exit 0.
  • Typecheck: pnpm --filter @objectstack/spec run typecheck exits 0. check:test-typecheck reports OK. tsc -p tsconfig.test.json --listFilesOnly lists src/api/package-api.test.ts (1 hit), and that file has no debt-ledger entry.
  • Build: turbo run build --filter=@objectstack/spec exits 0. git status was clean afterwards, so no generated artifact moved.

Gates. From node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands at this head: 82 derived, run one by one with each exit code captured before any pipe. --ran reports: «✓ dispatch-gates --ran: 82 derived famil(ies) accounted for — 82 run, 0 NOT-MEASURED (a DERIVED zero — all 82 recorded an exit code and none of them is 3)».

  • Two gates first answered exit 3 (PREREQUISITE NOT MET) on this fresh worktree: check:dual-build-cjs-loads and check:type-check-debt. After turbo run build --filter='./packages/*' --filter='./packages/*/*' (72 tasks, exit 0), both measured 0:
    • «✓ check:dual-build-cjs-loads — 104 published require entry point(s) across 67 package(s) load».
    • «check-type-check-coverage --re-measure: OK — 4 ledger entr(ies) re-measured».

Lint (a narrowed pass, stated as one). I ran eslint --no-inline-config --format json on the three touched paths. The JSON has 3 results. The two .ts files show 0 errors and 0 warnings. The .md is outside eslint's own population ("File ignored because no matching configuration was supplied"). This narrowing covers everything the full run would: eslint.config.mjs never enables type-aware linting (its note at lines 327-328: no parserOptions.project, no typed rules), so this diff cannot move the verdict of any untouched file. The full pnpm lint union is CI's.

History. The first commit b685c67ad2 also rewrote the pending .changeset/18058-install-door-contract-rebind.md. check:empty-changeset's foreign-changeset rule refused that with exit 1. The second commit 826e612b39 restores the file byte-identical to the merge base, and the gate reads exit 0 at head. Acceptance note 1 below has the details.

Patch round: the registry drive, transcribed as it posts (head 0251069c5)

The at-tier contract review of head 826e612b39 (record 5808378321) failed one item. This PR had transcribed the registry drive as { id: 'pkg-a', name: 'A', version: '1.0.0' } and filed it under the open 201 residual. That drive stopped posting that body at PR #19473; the body is now the drive file's REVERSED pin, answered 400. Commit 0251069c5 corrects every copy in one round. The clause split, 1a's CLOSED marking, the count sentence and the patch level are unchanged.

Read at both refs before any edit. packages/runtime/src/domain-handler-registry.test.ts and packages/runtime/src/package-door-namespace-conflict-code.test.ts are each byte-identical at the merge base fdeeea0cc9 and at origin/main 2c1011b01b:

  • The duplicate-id case posts { id: 'com.example.pkg-a', name: 'A', version: '1.0.0' } (:600). It is answered 409, then 201 on ?overwrite=true (:601-604).
  • The REVERSED pin posts { id: 'pkg-a', name: 'A', version: '1.0.0' } (:622) and asserts 400 (:623).
  • The namespace drive posts { id, name: id, namespace, version: '1.0.0' } (:83) and asserts 201 on the first install.

Clause 1b re-judged against both drives. Both still show it, so 1b stays byte-unchanged.

  • Declaration, from a one-shot tsx probe on src (not committed): both real bodies fail ManifestSchema on type alone, and both parse once type: 'app' is added. The stale body fails on id and type.
  • Door, at 0251069c5: pnpm --filter @objectstack/runtime exec vitest run --project local --maxWorkers=2 --reporter=verbose src/domain-handler-registry.test.ts src/package-door-namespace-conflict-code.test.ts gives Tests 57 passed (57). That includes the duplicate-id case (409, then 201), the REVERSED pin (400) and namespace section 1 (first install 201).

What changed

  • package-api.zod.ts, the drives paragraph only:
  • package-api.test.ts:
    • DOOR_DRIVE_REGISTRY holds the real body.
    • The control that rested on the stale id now asserts that the real body parses once type is added. It keeps the old pkg-a reading, refused on the id alone.
    • The clause-1a pin's comment now names the versionless first transcription it meant.
    • DOOR_201_RESIDUALS and its doc comment are unchanged: with the corrected constant, every entry is a body the door answers 201.
  • .changeset/19327-install-door-residual-split.md: the drives sentence names the body the drive posts, and the pkg-a body the door answers 400.

Reverse verification. Run after the fix was committed, through node scripts/ablation-replace.mjs, which put the stale transcription back.

  • Mutation: anchor ×1 → ×0, replacement ×0 → ×1, blob 37e99f04ca → 0331f39bc4.
  • Predicted beforehand: 1 red, the renamed control.
  • Observed: Tests 1 failed | 74 passed (75). The failure is at package-api.test.ts:945 (expected false to be true), the parse of the real body plus type.
  • Restored: the blob is back to 37e99f04ca, equal to HEAD, and git diff HEAD is empty.
  • Round 1 had no assertion that could go red on this transcription. This head has one.

Verification at 0251069c5

  • package-api.test.ts: Tests 75 passed (75). On origin/main 2c1011b01b plus this branch's patch it gives Tests 79 passed (79); the patch applies cleanly beside feat(spec)!: remove the three unmounted PackageApiContracts entries (upgrade / resolve-dependencies / upload) #19937's changes to the same two files.
  • Spec tests: --project local gives Test Files 527 passed (527) and Tests 15504 passed | 1 todo. The three repo-project tests that read .changeset/ or mention package-api give 136 passed.
  • Typecheck: pnpm --filter @objectstack/spec run typecheck exits 0. package-api.test.ts is in tsconfig.test.json's program and has no debt entry.
  • Build: the built dist/api/index.d.ts and .d.mts each carry the real body once and the stale body zero times.
  • Gates: node scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack derives 82. Deriving on origin/main plus the patch gives the same 82, with an empty set difference both ways.
    • --ran on the record as captured: «82 derived famil(ies) accounted for — 80 run, 2 NOT-MEASURED (2 DERIVED from a recorded exit 3)».
    • The two were check:dual-build-cjs-loads and check:type-check-debt, with PREREQUISITE NOT MET on a fresh worktree.
    • After turbo run build --filter='./packages/*' --filter='./packages/*/*' (72 tasks, exit 0), separate re-runs measured both at exit 0: «104 published require entry point(s) across 67 package(s) load» and «4 ledger entr(ies) re-measured … none above its recorded number».
  • Lint, a narrowed pass stated as one: eslint --no-inline-config --format json on the three paths gives 3 results.
    • Both .ts files have 0 errors and 0 warnings. The .md is outside eslint's population.
    • eslint.config.mjs enables no type-aware linting (its note at :327-328), so this diff cannot move any untouched file's verdict.
    • The full pnpm lint is CI's.

Acceptance notes

  1. The pending release note .changeset/18058-install-door-contract-rebind.md restates the same clause.
    • It reads «a manifest missing type and/or version (both of the runtime's own door drives post one)». That has been half false since fix(runtime): POST /api/v1/packages parses the manifest version leg instead of installing anything it is handed #19326.
    • It will publish into the @objectstack/spec, @objectstack/runtime and @objectstack/client CHANGELOGs at the next release.
    • It is not edited here. Correcting another PR's pending release note is a release decision that check:empty-changeset routes to a person, and that gate stays red on any PR that makes the edit. The proposed replacement sentence goes to the seat in the report.
  2. Clause 5 of the same residual list is stale too.
  3. The header of packages/runtime/src/domains/packages-install-manifest-version.test.ts counts the docblock's five classes differently from the docblock.
    • The header counts version, type, unknown keys, string-typed options and bare-form options.
    • The docblock has type/version as one class and the whitespace id as class 5.
    • This predates this PR and is not made false by it. That runtime file is not touched.

Generated by Claude Code

…and version halves

The PackageInstallBodySchema docblock recorded "a manifest missing `type`
and/or `version`" as one class the install door answers 201 to. The door
now parses ManifestSchema.shape.version by reference and answers 400 /
VALIDATION_ERROR to a manifest missing `version`, so only the `type` half
is still residual.

- package-api.zod.ts (docblock only): clause 1 splits into 1a (`version`,
  marked closed, naming the door-side pin) and 1b (`type`, still open).
  The count sentence says class 1 stays open through its `type` half. The
  paragraph quoting the runtime's door drives quotes the duplicate-id
  drive's current body, which carries a `version`.
- package-api.test.ts: the registry drive transcription matches the body
  the drive posts now. The residual list is hoisted and gains a pin that
  every body in it carries a declared-valid `version`, with a lit control.
- The pending 18058 changeset restated the same clause; its sentence is
  split the same way so the next CHANGELOG entry is true.
- New patch changeset for @objectstack/spec: files[] ships the docblock.

Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr
Co-authored-by: Claude <noreply@anthropic.com>
check-empty-changeset's foreign-changeset rule refuses a PR that modifies a
pending changeset it did not add. Correcting another PR's pending release
note is a release decision that needs a person to confirm it, so the
restated clause in that note is reported to the seat instead of being
rewritten here. The file is byte-identical to the merge base again.

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

github-actions Bot commented Sep 24, 2026 •

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

⚠️ 1 changed file(s) yielded no anchor (packages/spec/src/api/package-api.zod.ts), so the pages documenting them are NOT COVERED by this run — this is not a clean bill of health for those files. Nothing else in this diff resolved to a documentable surface (no symbol, route or SDK anchor derived from 1 changed package(s)).

What this run could not see
  • 1 changed file(s) yielded no anchor (packages/spec/src/api/package-api.zod.ts) — pages documenting those are invisible to this run
  • 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.
  • a key NAME is not a key, so the hand re-read the line above prescribes can land on the wrong schema. The same spelling is authorable on one governed type and a [REMOVED] tombstone on another for each of active, aria, joins, objects, template, tools and version (censused on [finding] tools is a key on BOTH AgentSchema (tombstoned, dead) and SkillSchema (live, cloud-attested), so a name-based search attributes skill examples to the agent key — it produced a false stop-the-line alarm on PR #19059 #19093 over the liveness ledger's governed types, top-level keys); nothing in a search result distinguishes the two, so a grep hit on a LIVE example reads as evidence about the DEAD key. Measured on fix(spec): the agent.tools liveness row says dead — it claimed live on a key the schema tombstoned #19059: content/docs/ai/agents.mdx was reported as contradicting the agent.tools tombstone over its tools: example at :161, which is inside the defineSkill({ block opened at :155 — the page was already correct. Settle ownership by PARSING the value against both schemas, never by the name: that literal PASSES SkillSchema, and as an AgentSchema it FAILS at tools with the tombstone prescription. ⛔ These names are not the whole class — a key retired through a .strict() guidance map leaves no tombstone in the walked shape and none of them here (tool.category, live as AIToolDefinition.category).

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

@objectstack-fleet

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: 61/61 CONTRACT_REVIEW_TIER
Head-sha: 826e612b39566d46dde2ecf0d9992875a108ba68

Isolated at-tier reviewer subagent, run by the domain:spec seat-4 session on the maintainer's instruction in that session (「帮我处理」, with the landing route chosen there); every one of its 61 transcript turns served at the tier the constant names. Adopted by the seat 2026-09-24T05:39Z. The record below is the reviewer's, unedited except the two header lines.

① Derived judgments

Scope read: git diff --stat fdeeea0cc9183f9c80343a552ef6523b1a53f99b...refs/review/pr-19935 gives 3 files, .changeset/19327-install-door-residual-split.md (+29), packages/spec/src/api/package-api.test.ts (+39 -13), packages/spec/src/api/package-api.zod.ts (+18 -7). Merge base is fdeeea0cc9183f9c80343a552ef6523b1a53f99b; origin/main is c1641868a3da71537c9c6c572d2bd2044103236b (10 commits ahead, none touching the residual docblock region or the two runtime drives; the PR's hunks do not overlap main's hunks in either spec file).

  1. Clause 1a (version) marked CLOSED by PR fix(runtime): POST /api/v1/packages parses the manifest version leg instead of installing anything it is handed #19326 — RIGHT. git merge-base --is-ancestor 13d52947d fdeeea0cc9 answers YES (PR fix(runtime): POST /api/v1/packages parses the manifest version leg instead of installing anything it is handed #19326 merged as 13d52947d81aca235133ee9619d80723a7c63348, Fixes #19120). At origin/main, packages/runtime/src/domains/packages.ts:1017 reads const declaredVersion = ManifestSchema.shape.version.safeParse((manifest as any)?.version); and :1018-1026 returns deps.error(..., 400) before any writer runs. deps.error is HttpDispatcher.error (packages/runtime/src/http-dispatcher.ts:484, :1057-1063), which passes no code, so apiErrorResponse derives it from the status; packages/spec/src/api/errors.zod.ts:194-196 standardErrorCodeForHttpStatus(400) is VALIDATION_ERROR (pinned at errors.test.ts:301). The cited door-side pin exists at origin/main (git ls-tree blob 600bc0145a): packages/runtime/src/domains/packages-install-manifest-version.test.ts:183-217 (§1) asserts status 400, body.error.code === 'VALIDATION_ERROR' and neither install writer called, on both body forms. Docblock text at head package-api.zod.ts:520-524 states exactly that. True.

  2. Clause 1b (type) still OPEN, answered 201 — RIGHT in substance. Read the install path at origin/main packages.ts:962-1049: the only gates between body and installPackage are the id gate (:962-973, ManifestSchema.shape.id), the version gate (:1017-1026) and the duplicate-id 409 (:1033-1040). No read of manifest.type anywhere on that path. Both drives post no type and assert 201: packages/runtime/src/package-door-namespace-conflict-code.test.ts:83 ({ id, name: id, namespace, version: '1.0.0' }) with :127-129 asserting 201; packages/runtime/src/domain-handler-registry.test.ts:600-604 posting { id: 'com.example.pkg-a', name: 'A', version: '1.0.0' } and asserting 201 on ?overwrite=true.

  3. The re-transcription of the registry drive is WRONG, and it is the antecedent of 1b — a defect this PR authored. The PR's "in-place fix of the drives paragraph" and the test's new comment both say the drive posts { id: 'pkg-a', name: 'A', version: '1.0.0' } "now". It does not, and did not at the PR's own base:

    • git merge-base --is-ancestor f9977c114 fdeeea0cc9 answers YES: PR fix(runtime): POST /api/v1/packages parses the manifest's id leg #19473 (f9977c114456264ff5723e489d29f71ab9417ba7, "parses the manifest's id leg", 2026-09-21) predates the merge base. Its patch on domain-handler-registry.test.ts changed the duplicate-id fixture from { id: 'pkg-a', name: 'A', version: '1.0.0' } to { id: 'com.example.pkg-a', name: 'A', version: '1.0.0' } ("the LAST one this fixture owed") and added the REVERSED pin at :607-624, which posts exactly { id: 'pkg-a', name: 'A', version: '1.0.0' } and asserts expect(result.response?.status).toBe(400).
    • git show refs/review/pr-19935:packages/runtime/src/domain-handler-registry.test.ts | sed -n '600,604p;620,624p' on the PR ref itself shows the same: com.example.pkg-a gives 409 then 201; pkg-a gives 400.
    • Head package-api.zod.ts:481-483: "{ id: 'pkg-a', name: 'A', version: '1.0.0' } are both refused here (invalid_union) because neither carries type" — that body is not what the drive posts, and the declaration refuses it on the id as well (MANIFEST_ID_PATTERN), which the same PR's test admits at package-api.test.ts:941-945.
    • Head package-api.zod.ts:525-526: "1b. missing type — still OPEN, answered 201 (both door drives above)" — "the drives above" now names a body the door answers 400 to (packages.ts:962-973). A reader of the published docblock (files[] in packages/spec/package.json at origin/main includes src/**/*.zod.ts; the PR says the text is also emitted into dist/api/index.d.ts) is told the door answers 201 to a body it refuses.
    • Head package-api.test.ts:925-927: "gave the drive a version; this is the body it posts now" followed by const DOOR_DRIVE_REGISTRY = { id: 'pkg-a', name: 'A', version: '1.0.0' }; — false. :957 documents DOOR_201_RESIDUALS as "Bodies the door still answers 201 to" and lists that body. No assertion goes red because the file only parses against the declaration and ManifestSchema.shape.version; the prose is the pin, and it is wrong.
    • Head .changeset/19327-install-door-residual-split.md:21: "the duplicate-id drive has posted a version since fix(runtime): POST /api/v1/packages parses the manifest version leg instead of installing anything it is handed #19326, and the docblock now quotes that body" — false; the docblock quotes a body the drive stopped posting at fix(runtime): POST /api/v1/packages parses the manifest's id leg #19473. This sentence would compile into the shipped CHANGELOG.
    • PR body, "The type half, from both existing drives": "The duplicate-id case in domain-handler-registry.test.ts posts { id: 'pkg-a', name: 'A', version: '1.0.0' } on ?overwrite=true" and "each asserts that the door answers 201" — false at fdeeea0cc9; that body is asserted 400 at :622-623. The PR's reverse verification only ablated the version key and never drove the door with the transcribed body, so nothing measured it.
      The PR's own justification for touching this paragraph was that, left alone, it "would have filed a no-longer-posted versionless body under the open residual". The edit files a different no-longer-posted body, one the door refuses, under the open residual — the same defect class, newly authored at this head.
  4. Count sentence "five classes ... class 1 in its type half only, since PR fix(runtime): POST /api/v1/packages parses the manifest version leg instead of installing anything it is handed #19326" (zod.ts:515-516) — RIGHT. Numbering 1 to 5 unchanged; clauses 2 to 5 byte-unchanged in the diff.

  5. No accept/reject result moved — CONFIRMED. Both zod.ts hunks (@@ -478,10 +478,12 @@, @@ -510,9 +512,18 @@) lie inside the /** ... */ block above export const PackageInstallBodySchema, whose body (z.union([PackageInstallRequestSchema, ManifestSchema])) is untouched. No export, schema, or runtime file changes. In the test file no assertion was weakened: DOOR_DRIVE_REGISTRY still parses false, registryKeysCompleted (now without the redundant version: '1.0.0', which the spread already supplies) still parses false, the 201-residual loop is unchanged over the hoisted list, and one pin is added with a lit control (ManifestSchema.shape.version.safeParse(undefined).success === false; manifest.zod.ts:415 declares version: z.string().regex(MAJOR_MINOR_PATCH_VERSION_PATTERN), required).

  6. Check runs at head: commits/826e612b39566d46dde2ecf0d9992875a108ba68/check-runs returns 42, every one completed with conclusion success or skipped; none failed, including Check Changeset, Governed Surface Queue Guard, Lint & Repo Gates, Type Check · source gates, Test Core 1-6, Build Core, Spec property liveness.

② Semver level

patch for @objectstack/spec, declared with Clause-②: no at .changeset/19327-install-door-residual-split.md:2 and :7. Consistent with AGENTS.md (origin/main lines 1081-1085): the diff publishes (the docblock ships via files[] src/**/*.zod.ts and the built .d.ts), so skip-changeset would be wrong, and no accept set or export moves, so no with no arm and patch is the right level. Prose-only ⇒ patch: matches. No ADR-0087 marker owed (not breaking). The changeset's own body, however, carries the false sentence at :21 (item ①.3); the level is right, the text is not.

③ Boundary flags

Implemented-by: claude/issue-19327-install-door-residual-split
Reviewed-by: session_019c3Hi6ZMU1p6m6aA6Bz45d

VERDICT: FAIL

Blocking: the registry-drive re-transcription { id: 'pkg-a', name: 'A', version: '1.0.0' } at package-api.zod.ts:481-483 (the antecedent of clause 1b at :525-526), package-api.test.ts:925-927 and :957, and .changeset/19327-install-door-residual-split.md:21 describe a body the drive stopped posting at PR #19473 (an ancestor of this PR's merge base, domain-handler-registry.test.ts:600 and :622-623) and which the door answers 400, filed as a 201 residual in a published docblock. The clause split itself, the CLOSED marking of 1a with PR #19326 and its door-side pin, the OPEN status of 1b, the count sentence, and the patch changeset level are all correct; the fix is to transcribe the drive as it stands at the base and re-read the one test control that rests on the stale id.


Generated by Claude Code

The install door's residual docblock, its pinning test and the changeset
transcribed the duplicate-id drive in domain-handler-registry.test.ts as
{ id: 'pkg-a', name: 'A', version: '1.0.0' }. That drive stopped posting
that body at PR #19473, which made the door parse the manifest's id leg
and repaired the fixture's id to com.example.pkg-a. The old body is the
REVERSED pin beside it and is answered 400. It had been filed as a 201
residual under clause 1b.

Read at the merge base fdeeea0 and at origin/main 2c1011b (the file
is byte-identical at both): the drive posts
{ id: 'com.example.pkg-a', name: 'A', version: '1.0.0' }, answered 409 and
then 201 on ?overwrite=true. The declaration refuses it on `type` alone.
Clause 1b's "both door drives above" is true of that body, so 1b is
unchanged.

- package-api.zod.ts: the drives paragraph quotes the real body and
  credits both repairs, the version to PR #19326 and the id to PR #19473.
- package-api.test.ts: DOOR_DRIVE_REGISTRY transcribes the real body.
  The control that rested on the stale id now asserts that the registry
  drive parses once `type` is added, like the conflict drive. The old
  `pkg-a` reading is kept, pointed at the old body: it is refused on the
  id alone.
- changeset: the drives sentence names the body the drive posts.

Prose and test only. No schema, accept set, export or runtime change.

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

Copy link
Copy Markdown
Contributor Author

Contract review

Served-tier: 62/62 CONTRACT_REVIEW_TIER
Head-sha: 0251069c51acf362b2219cced9dd24a9e7e0239c

Isolated at-tier reviewer subagent, run by the domain:spec seat-4 session; every one of its 62 transcript turns served at the tier the constant names. Re-review after the takeover patch round (the maintainer's 「你接手派补丁轮」); it supersedes the FAIL record 5808378321 on the prior head. Adopted by the seat 2026-09-24T14:09Z. The record below is the reviewer's, unedited except the two header lines.

① Derived judgments

Scope read: git diff --stat fdeeea0cc9 refs/review/pr-19935 gives 3 files, .changeset/19327-install-door-residual-split.md (+33), packages/spec/src/api/package-api.test.ts (+54 −26), packages/spec/src/api/package-api.zod.ts (+21 −7); +108/−33 in all. Three commits b685c67ad2 → 826e612b39 → 0251069c51, the third a fast-forward whose parent is the prior head (no force, no rebase). Merge base fdeeea0cc9; origin/main is 2c1011b01b (20 ahead; the prior record's c1641868a3 is its ancestor); PR mergeable: true, still draft, no reviews or review comments. The patch-round diff 826e612b39..0251069c51 is +35/−26 over the same three files and touches only the drives paragraph (zod.ts:478-490), the test's registry constant, one control, one comment line, and the changeset's drives sentence.

  1. The real drive bodies, read myself at BOTH refs. Blob hashes are equal at fdeeea0cc9, origin/main 2c1011b01b and the PR ref: domain-handler-registry.test.ts fe7814bb3786, package-door-namespace-conflict-code.test.ts c9854e491a89. Registry drive: :600 const manifest = { id: 'com.example.pkg-a', name: 'A', version: '1.0.0' }, :602 toBe(409), :603-604 { overwrite: 'true' } then toBe(201); the REVERSED pin titled at :607 posts { id: 'pkg-a', name: 'A', version: '1.0.0' } at :622 and asserts toBe(400) at :623. Namespace drive: :83 ({ id, name: id, namespace, version: '1.0.0' }), :127-129 first install toBe(201). Every line the PR body, docblock, test comments and changeset cite (:600, :601-604, :622-623, :83) is what it says. Provenance: git show f9977c114 (PR fix(runtime): POST /api/v1/packages parses the manifest's id leg #19473, merged 2026-09-21T02:47Z, ancestor of the merge base) is the commit that changed the fixture from pkg-a to com.example.pkg-a and added the REVERSED pin; git show 13d52947d (PR fix(runtime): POST /api/v1/packages parses the manifest version leg instead of installing anything it is handed #19326, merged 2026-09-20, ancestor) added version: '1.0.0' to the then-pkg-a fixture. Both attributions in the prose ("PR fix(runtime): POST /api/v1/packages parses the manifest version leg instead of installing anything it is handed #19326 gave it the version", "PR fix(runtime): POST /api/v1/packages parses the manifest's id leg #19473 replaced its id") are right.

  2. Clause 1b "still OPEN, answered 201 (both door drives above)" is TRUE of both real bodies. Door side: packages/runtime/src/domains/packages.ts (blob 7c76f0d88626, identical at merge base and main) gates the install path on id (:962-973, ManifestSchema.shape.id), version (:1017-1026, ManifestSchema.shape.version) and the duplicate 409 (:1034-1041); a grep of :900-1100 for any .type read is empty. com.example.pkg-a and com.acme.crm both match MANIFEST_ID_PATTERN (manifest.zod.ts:263, /^[a-z][a-z0-9-]*(\.[a-z][a-z0-9-]*)+$/), 1.0.0 matches the version regex (:416), so both bodies pass every door gate and reach installPackage, which is what the runtime drives assert (201 at :604 and :129). Declaration side: ManifestSchema.type is a required z.enum (:431-437; 'app' is in CORE_PLUGIN_TYPES, plugin.zod.ts:94), namespace is optional under /^[a-z][a-z0-9_]{1,19}$/ (:390-392, crm fits), name a required string; so both bodies fail on type alone and parse once type: 'app' is added, which the test asserts at :943 and :945 and CI's Test Core ran green. The docblock's :481-483 "refused here (invalid_union) on type alone, and the door answers both 201 (the second on the ?overwrite=true limb of its duplicate-id case)" is right in every part. Clause 1b's own text at :528-529 is byte-unchanged from the prior head.

  3. Clause 1a CLOSED by PR fix(runtime): POST /api/v1/packages parses the manifest version leg instead of installing anything it is handed #19326: RIGHT, unchanged. packages.ts:1017-1026 returns deps.error(…, 400) before any writer runs; the cited pin packages-install-manifest-version.test.ts (blob 600bc0145a2a at both refs) §1 :183-216 asserts 400, error.code === 'VALIDATION_ERROR' and neither writer called, on wrapped and bare forms. The PR body's "labels 1a/1b match the table in fix(runtime): POST /api/v1/packages parses the manifest version leg instead of installing anything it is handed #19326's own body": that PR's table rows are labelled 1a version and 1b type, so they match.

  4. The blocking item of record 5808378321 is cleared in every copy. git grep "id: 'pkg-a'" on the PR ref: within this PR's files the only hits are package-api.test.ts:955 (asserted false, pointed at the old body), :986-987 (named as the first transcription that was wrong) and .changeset/…:21 (quoted as the old wording, followed by "the door answers 400 to pkg-a"). zod.ts files no pkg-a body under the residual; :486-487 says the door answers that old body 400 "so it is no part of the residual". DOOR_DRIVE_REGISTRY at :930 is the real body. Every other pkg-a hit on the tree is the runtime drive's own REVERSED pin or mocks (:543, :580, :622) or pre-fix(runtime): POST /api/v1/packages parses the manifest's id leg #19473 history prose (packages.ts:902, packages-install-manifest-id.test.ts:14, the two 19417 changesets, protocol.ts:22676), none filing it as a 201.

  5. Every other prose claim checked against the tree. Count sentence zod.ts:518-519 right; numbering 1-5 kept; clauses 2-5 (:530-537) are context lines in the diff, byte-unchanged. Test comment :947 "refused since [finding] ManifestSchema.id is a bare z.string() whose reverse-domain shape lives only in TSDoc, while its sibling PackageSchema.manifestId enforces that shape with a regex — one identifier, two declarations, only one of them machine-readable #17534 (PR feat(spec)!: manifest.id enforces the reverse-domain rule its registry face already had #18319)": PR feat(spec)!: manifest.id enforces the reverse-domain rule its registry face already had #18319 is Part of #17534, merged 2026-09-21T00:08Z, and did not touch the registry drive (git show --stat 097d268594 on that file is empty); right, and it settles the prior record's attribution note. PR body "byte-identical at fdeeea0cc9 and 2c1011b01b": right (item 1). PR body "applies cleanly beside feat(spec)!: remove the three unmounted PackageApiContracts entries (upgrade / resolve-dependencies / upload) #19937's changes to the same two files": feat(spec)!: remove the three unmounted PackageApiContracts entries (upgrade / resolve-dependencies / upload) #19937 (43460b95a, merged 2026-09-24) is the only main-side commit on either spec file since the merge base; its -U0 hunks sit at zod.ts :24 and :566 onward and test.ts :508-548, none inside this PR's regions (:478-529, :919-996). PR body "package-api.test.ts:945 (expected false to be true)" for the ablation: :945 is the green parse of the real body plus type, the one assertion that goes red when the constant regresses to pkg-a (fails the id pattern), so the prediction and the schema agree. .changeset/18058-install-door-contract-rebind.md is blob c670fa2a2bcc at merge base, main and head, untouched as the body says. The "Verification, round 1" section is labelled as history of 826e612b39 and matches that head's file (git show 826e612b39:… still carried the title "the missing keys are what decide it — and since [finding] ManifestSchema.id is a bare z.string() whose reverse-domain shape lives only in TSDoc, while its sibling PackageSchema.manifestId enforces that shape with a regex — one identifier, two declarations, only one of them machine-readable #17534 …"). No sentence in the docblock, test comments, changeset or PR body cites a body, line or PR that is not what it says.

  6. No accept/reject result moved. Both zod.ts hunks (@@ -478,10 +478,15 @@, @@ -510,9 +515,18 @@) lie inside the docblock above export const PackageInstallBodySchema (:549-552, z.union([PackageInstallRequestSchema, ManifestSchema]), untouched); no schema, export or runtime file changes. In the test, the refusals at :933, :937 and :974 are kept; :945 moves from asserting false (a premise that was stale) to true (the real body plus type parses), which is the correct reading of the declaration; :955 keeps the old refusal, now pointed at the old body. Nothing the door accepts is declared refused or the reverse.

  7. Check runs at head: commits/0251069c5…/check-runs returns 42, every one completed: 37 success, 5 skipped, 0 failed, including Check Changeset (×2), Governed Surface Queue Guard, Lint & Repo Gates, TypeScript Type Check and its three sub-jobs, Test Core 1-6, Build Core, Spec property liveness, Temporal Conformance (live PG + MySQL), Dogfood Regression Gate 1-3.

② Semver level

patch for @objectstack/spec, Clause-②: no with no arm (.changeset/…:2 and :7; PR body line 3). Consistent with AGENTS.md :1081-1085: the diff publishes (packages/spec/package.json files[] at origin/main lists src/**/*.zod.ts, so the docblock ships verbatim; version there is 17.4.0), so skip-changeset would be wrong; nothing widens or narrows an accept set, so no with no arm; a text correction in a released package is patch. Prose-only ⇒ patch: matches. No ADR-0087 marker owed (not breaking). The changeset body is now true in every sentence (①.4, ①.5) and follows the tree's shape (frontmatter, one-line title, Clause-② line, body; cf. .changeset/19417-install-door-parses-manifest-id.md:1-7).

③ Boundary flags

  • Prior BLOCKING item: cleared (①.1, ①.4). The registry drive is transcribed as it posts at the base and at main, the old body is filed as 400, and both repairs are credited to the right PRs.
  • New test control: weight-bearing; one inherent limit, non-blocking. :945 pins that the transcribed body is refused by the declaration on type alone (it goes red if the constant regresses to a non-reverse-domain id, which is the single red the dev's ablation reports); :955 is the id-only refusal with :945 as its lit control; the 1a pin :989-995 carries the undefined lit control at :995. Limit, stated in the test itself at :969-972: this package cannot import the door, so no assertion here can detect a future change of the runtime fixture; the transcription stays a prose pin whose 201 is measured in domain-handler-registry.test.ts.
  • Deviation from the card, non-blocking: the drives-paragraph rewrite and the DOOR_DRIVE_REGISTRY re-transcription remain extra surface over "split clause 1"; defensible because that paragraph is clause 1b's antecedent, and it is now correct. The claim surface covers all three files (comment 5805704583 amended it for the test file; takeover claim 5814380306 names all three).
  • Prior non-blocking, pre-existing, still standing, outside the card's fence: clause 5 at zod.ts:536-537 still says a whitespace-only id is one "this declaration admits"; ManifestSchema.id refuses it (MANIFEST_ID_PATTERN, pinned at package-api.test.ts:998-1008), while the door half ("trims before keying") is still true (packages-install-manifest-id.test.ts:348, 400). Reported in Acceptance note 2; not this card's.
  • Prior non-blocking, pre-existing, still standing: .changeset/18058-install-door-contract-rebind.md:28 restates clause 1 as "type and/or version"; blob unchanged at merge base, main and head; routed to a person, consistent with AGENTS.md :697. Confirmed.
  • Prior non-blocking, confirmed at head: a git grep for the old spelling "and/or version" on the PR ref hits only the 18058 changeset and the new changeset's quotation of the old wording (:11); content/docs/** has no restatement (references/api/package-api.mdx:30 only imports the symbol).
  • Prior non-blocking attribution inconsistency: resolved. The test now reads "[finding] ManifestSchema.id is a bare z.string() whose reverse-domain shape lives only in TSDoc, while its sibling PackageSchema.manifestId enforces that shape with a regex — one identifier, two declarations, only one of them machine-readable #17534 (PR feat(spec)!: manifest.id enforces the reverse-domain rule its registry face already had #18319)" (:947), the PR body "Since PR feat(spec)!: manifest.id enforces the reverse-domain rule its registry face already had #18319"; PR feat(spec)!: manifest.id enforces the reverse-domain rule its registry face already had #18319 is Part of #17534.
  • Acceptance note 3, confirmed pre-existing and non-blocking: packages-install-manifest-version.test.ts:27-30 and packages.ts:992-996 enumerate the five classes as version / type / unknown keys / string-typed options / bare-form options, unlike the docblock's own numbering; runtime files, outside this card, not made false by it.
  • Non-blocking bookkeeping slip, card comment only: dev report 5815120917 says "DOOR_201_RESIDUALS and its :957 doc comment"; at head that comment is :958 (:957 was its line at the prior head 826e612b39). Not in the PR body or the tree.
  • Not re-measured here, not load-bearing: the npm 17.4.0 grep, the dist/*.d.ts emission counts and the local test counts (75; 79 on main plus patch) are the dev's readings; the assertions they rest on are covered by Test Core and Build Core success at this head, and I re-ran no gate family.
  • Trailers and vocabulary: all three commits end with the model-free trailer pair AGENTS.md :451-455 requires, and the three changed files contain no model identifier (grep 0 in each).

Implemented-by: claude/issue-19327-install-door-residual-split
Reviewed-by: session_019c3Hi6ZMU1p6m6aA6Bz45d

VERDICT: PASS


Generated by Claude Code

@objectstack-fleet
objectstack-fleet Bot marked this pull request as ready for review September 24, 2026 14:10
@objectstack-fleet
objectstack-fleet Bot added this pull request to the merge queue Sep 24, 2026
Merged via the queue into main with commit ba77509 Sep 24, 2026
44 checks passed
@objectstack-fleet
objectstack-fleet Bot deleted the claude/issue-19327-install-door-residual-split branch September 24, 2026 14:35
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

Development

Successfully merging this pull request may close these issues.

[finding] the package install door's residual docblock records type and version as one 201 class — the version half is closed now

1 participant