Skip to content

docs(spec): the api install door doc block says what the in-process primitive does with enableOnInstall - #19710

Merged
os-support-ai merged 4 commits into
mainfrom
claude/issue-19339-package-api-denial
Sep 22, 2026
Merged

os-support-ai merged 4 commits into
mainfrom
claude/issue-19339-package-api-denial

Conversation

@os-support-ai

Copy link
Copy Markdown
Collaborator

Fixes #19339

Clause-②: no

The second and final increment on this card. PR #19691 corrected three of the four carriers the executable criterion names — the kernel .describe() and the two reference pages generated from it — and landed as 7e1b048a1d. One carrier remained, in the file that #19691's dispatch had fenced out by mistake (recorded on the card at comment 5773730316): the doc block on PackageInstallRequestSchema.enableOnInstall in packages/spec/src/api/package-api.zod.ts.

That block is the map a reader follows from the authority to the other two declarations of this key, and its entry for the kernel copy said:

Same type, same default, same meaning; its own implementation does not read it, and this door does not forward it down that seam.

It ships. src/**/*.zod.ts is in this package's files[], and the comment survives bundling into dist/api/index.js and dist/browser/api/index.mjs — so this is published text, not an internal note.

⭐ Only ONE half of that sentence was false

It is a compound claim about two different layers, and they were re-derived separately from the source rather than rewritten together. Correcting the true clause would have swapped one false sentence for another.

clause layer it is about verdict on main what this PR does
"its own implementation does not read it" the in-process primitive MetadataProtocol.installPackage FALSE since 482d584121 corrected, and scoped
"this door does not forward it down that seam" the HTTP door POST /api/v1/packages still TRUE kept, and its mechanism spelled out

Half one, measured. packages/metadata-protocol/src/protocol.ts, the requestedEnabled arms: === true calls enablePackage, === false calls disablePackage, an absent key makes no lifecycle call at all. === true / === false, never a truthiness test and never a ?? default, so the three states are three outcomes.

Half two, measured. packages/runtime/src/domains/packages.ts, the install handler: it calls protocolSvc.installPackage({ manifest, settings: body.settings }) — the key is not in that call — then performs the enable/disable flip itself against the registry, and writes the durable record from the row it returned (setPackageDisabled(environmentId, pkgId, rowDisabled)). So the clause is still accurate, and the replacement text now says WHY rather than only THAT.

The scope words are load-bearing, and they follow #19691's pattern. The primitive moves the registry row — what every in-process reader serves, for the life of the process. The durable disabled-package record is keyed by ENVIRONMENT, which an InstallPackageRequest does not carry, so POST /api/v1/packages still owns the half that survives a restart. Stating the primitive "honours it" without that qualifier would overstate it in the other direction.

Premise, re-verified on this tree rather than relayed

  • 482d584121 is an ancestor of this branch: git merge-base --is-ancestor 482d584121 origin/main exits 0. Control leg on the same checkout with a commit known to be in that history (596090efbe, the commit that WROTE the denial) also exits 0, so the positive reading is not a shallow-clone artefact. git rev-parse --is-shallow-repository reads false independently.
  • Carrier census before the edit, radius stated: git grep over the whole tracked tree. The exact phrase stood in packages/spec/src/api/package-api.zod.ts for this key. Dark control on a near-miss spelling of the same shape over the same file: 0 hits, so the probe discriminates.

The criterion is fully satisfied — measured after merging origin/main

origin/main (with #19691 in it) is merged into this branch through scripts/pm/os-regen-merge.sh, so the four places the criterion names can be read on ONE tree:

place the criterion names denial hits corrected text present who corrected it
packages/spec/src/kernel/package-registry.zod.ts 0 1 #19691
content/docs/references/api/protocol.mdx 0 1 #19691
content/docs/references/kernel/package-registry.mdx 0 1 #19691
packages/spec/src/api/package-api.zod.ts 0 1 this PR

Dark control on a near-miss spelling over the same four files: 0 on each. #19691's implementation body was re-asserted present after the merge, along with #19685's incoming entries, so nothing was swallowed in either direction. That is why this PR uses a closing keyword where #19691 used Part of.

No generated page changes — measured, not assumed

pnpm --filter @objectstack/spec check:generated reports all 15 generated artifacts up to date, before and after the edit, with no regeneration and no --fix run. That is the expected reading and it is the measurement: the corrected text is a TSDoc block inside the schema factory, not a .describe(), and only .describe() text reaches content/docs/references/**. The generated row for this key on content/docs/references/api/package-api.mdx already read "honoured at POST /api/v1/packages", and still does, byte-identical. ⛔ No reference page was hand-edited; none needed regenerating.

The same text does reach packages/spec/json-schema/** for the KERNEL copy only — that tree is gitignored, generated at build time from the kernel .describe() #19691 corrected, and it is clean on this branch.

Changeset — measured against files[], not pattern-matched

patch on @objectstack/spec; skip-changeset would be a false declaration. Answered against the package's own files[] after a real build, npm pack --dry-run --json, 2031 files:

  • subjectsrc/api/package-api.zod.ts is present in the listing (files[] carries src/**/*.zod.ts), and the corrected comment also reaches dist/api/index.js, dist/api/index.mjs, dist/browser/api/index.js and dist/browser/api/index.mjs, all under dist.
  • positive controldist/index.d.ts is in the same listing, as it must be.
  • negative controls0 *.test.ts paths and 0 content/ paths are in that listing, so the instrument is not simply answering yes.

Level: nothing is added, removed, renamed or retyped and no default moves — check:api-surface and check:authorable-surface are green with no diff — so this is a correction to a published description, not a widening.

Verification, at 3e94943aa7

Gates — ⛔ not a recalled list. Derived from the real change set with node scripts/pm/dispatch-gates.mjs --repo objectstack-ai/objectstack --commands, each exit code captured BEFORE any pipe, then reconciled with --ran:

✓ dispatch-gates --ran: 76 derived famil(ies) accounted for — 76 run,
  0 NOT-MEASURED (a DERIVED zero — all 76 recorded an exit code and none of them is 3).

All 76 exit 0. On the pre-merge head three of them first answered exit 3 = PREREQUISITE NOT MET (check:doc-formula-expressions, check:dual-build-cjs-loads, check:lean-entry-closure) because the workspace was not built; that was cleared with pnpm build and all three re-run to exit 0. ⛔ An exit 3 was never read as a pass. The whole union was then re-derived and re-run on the merged head — byte-identical family set, 76/76 at exit 0.

Tests

run result
pnpm build (turbo, excluding docs) 73 tasks successful
pnpm --filter @objectstack/spec test 515 files, 15021 passed, 1 todo
pnpm --filter @objectstack/spec typecheck pass — tsc --noEmit, plus check:scripts-typecheck and check:test-typecheck (53 files / 257 errors / 142 pinned signatures held, shrink-only), so the test layer is MEASURED, not assumed
pnpm lint (repo-wide eslint . --no-inline-config) exit 0 — full run, no narrowing claimed
pnpm --filter @objectstack/spec check:generated all 15 up to date

No new test, and this is deliberate. check:docs can only ever prove a page equals its .describe(); nothing can compare a sentence to an implementation, which is the card's own finding. Inventing an instrument here would be a new verification surface the card did not ask for.

Acceptance notes

The pending changeset carrying the same root is still standing, and is still not a dev edit. .changeset/18605-enable-on-install-one-authority.md states that the kernel copy's published description "now records that this layer does not read it". #19691 made that sentence false and left the note alone on purpose — pr-automation.yml route 0 classes editing another card's pending note as a DELIBERATE CORRECTION requiring written confirmation on the PR. The card body ruled the same way. It is a release decision, handed to the review seat, and it is outside this card's executable criterion, which names four places and not this one.

content/docs/releases/v17/17-0.mdx also carries the phrase and is correctly untouched. Release notes record what shipped in the version they document; that surface is RELEASE-OWNED and read-only to a code PR.

Sequencing with #19273. That card is the SHAPE half of this field (.default(true) versus the ruled 「缺省 = 保持」) and is fenced out here. It rewrites the same field's published text from the other side; whichever lands second regenerates the same rows. ⛔ Not this PR's to sequence.

Clause-② hint, recorded and answered. dispatch-gates flags packages/spec/src/** as a clause-② SUSPECT surface, so construction is at CONTRACT_REVIEW_TIER. It is a hint, not a verdict: this diff adds no key to a published payload, changes no accept or reject behaviour, and leaves the accept set byte-for-byte — the declaration stays no, which is also the dispatching seat's reading.

⛔ No label was written by this branch. needs:contract-review is the seat's to place, and skip-changeset is refuted by the measurement above.


Generated by Claude Code

…n-process primitive reads enableOnInstall

`MetadataProtocol.installPackage` has honoured the key since `482d584121`:
`true` enables, `false` disables, an absent key makes no lifecycle call.
The doc block on `PackageInstallRequestSchema.enableOnInstall` still said
its own implementation does not read it.

Only that half of the compound sentence was false. The second half — this
door does not forward the key down that seam — is still true on `main`:
`handlePackages` calls `installPackage({ manifest, settings })` and performs
the flip itself, so the durable environment-keyed record follows the row
this door returned. The replacement states both, and scopes the primitive's
half to the registry row, because an `InstallPackageRequest` carries no
environment to key the durable record by.

Claude-Session: https://claude.ai/code/session_013RDBh5DqXd2xnLwvHLgLFr
Co-authored-by: Claude <noreply@anthropic.com>
@github-actions github-actions Bot added size/s documentation Improvements or additions to documentation tooling labels Sep 22, 2026
@github-actions

github-actions Bot commented Sep 22, 2026

Copy link
Copy Markdown
Contributor

📓 Docs Drift Check

1 anchor(s) derived from 1 changed package(s); no hand-written page names any of them, so this run has nothing to listnot a clean bill of health. This check sees only pages that NAME a derived anchor: one that documents this change in prose, or enumerates it in an authoring dialect, names none and stays invisible to it on every run.

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

Which tree this was computed on

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

node scripts/docs-audit/affected-docs.mjs --json 106d4c8dd7fe35d6195c507304ac21a299c510ea

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

…ckage-api-denial

One content conflict, in the `PackageInstallRequestSchema.enableOnInstall` TSDoc
block (`packages/spec/src/api/package-api.zod.ts`), resolved by hand so both
sides' meaning survives:

- `origin/main` re-declared the key `z.boolean().optional()` and rewrote the
  kernel-copy bullet to "Same type, same OPTIONALITY, same meaning". That half
  is taken verbatim — the branch's inherited `same default` wording and its
  `.default(true)` belong to the pre-merge state, not to this PR's change, and
  reinstating either would silently revert the three-state ruling.
- This branch's correction is kept whole: the in-process primitive HONOURS the
  key on the REGISTRY ROW (`true` enables, `false` disables, ABSENT makes no
  lifecycle call), with the "on the REGISTRY ROW" scope qualifier intact — the
  durable half is environment-keyed and this door still does not forward the key
  down that seam.

The paragraph above the bullet list (the two-state prose) merges from main's
side unchanged; main had already replaced it there.

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

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: 100/100 CONTRACT_REVIEW_TIER (seat-measured, 2026-09-22T14:18Z)
Head-sha: 3e94943aa7c3b5282ffab6c7d8dab97452a2dc9a

⚠️ This record binds THIS head only. The PR is mergeable_state: dirty against current main, and the conflict falls inside the exact TSDoc paragraph this review judged. A hand-resolved hunk in the reviewed text is ⛔ not a pure regeneration, so the merged head owes its own record — this one is not carried across it. A dev is resolving the conflict now under the resolution this review derived (see ③).

Rendered by an isolated at-tier review subagent.

① Derived judgments

  • Read at head, ⛔ not from the PR prose: 2 files, +30/−2 — one TSDoc block inside the PackageInstallRequestSchema factory (packages/spec/src/api/package-api.zod.ts:377–394) and a new changeset. No code line, no .describe(), no key, type or default moves; enableOnInstall: z.boolean().default(true) at head is byte-identical to the merge-base. Accept set unchanged, no new export, no new key on a published payload, no refusal deleted ⇒ Clause-②: no is right on both limbs. The path limb fires, which is why this record exists at all.
  • The corrected sentence was a compound claim over two layers. Judged separately, against source:
    • Half 1 — «its own implementation does not read it» — FALSE since 482d584121 (an ancestor of head): packages/metadata-protocol/src/protocol.ts:22725–22732 reads requestedEnabled === true ⇒ enablePackage, === false ⇒ disablePackage, otherwise no lifecycle call. The rewrite states exactly those three outcomes. Correct.
    • Half 2 — «this door does not forward it down that seam» — STILL TRUE at head: packages/runtime/src/domains/packages.ts:1045 calls installPackage({ manifest, settings: body.settings }); the flip is the door's own at :1094–1101; the durable write at :1143–1145 is setPackageDisabled(…, pkg?.enabled === false), from the returned row. The rewrite keeps the clause and adds its mechanism — meaning neither inverted nor narrowed. Correct.
  • The «on the REGISTRY ROW» qualifier is accurate and load-bearing. After the flip the primitive's only persistence is pkgSvc.publish({ manifest, metadata })INSERT INTO sys_packages (id, version, manifest, metadata, hash, …) (packages/services/service-package/src/index.ts:384) — no enabled column; InstallPackageRequestSchema carries no environment field; the disabled-package file is environment-keyed (packages/runtime/src/package-state-store.ts:112–113). ⛔ Dropping the qualifier would promise durability the seam cannot deliver.
  • Card spec: three published copies of "this protocol primitive does not read it" are falsified the moment PR #19338 lands — and no gate can see it #19339's executable criterion, measured across all four named places on the head tree (which contains 7e1b048a1d): denial hits / corrected hits — kernel/package-registry.zod.ts 0/2, api/package-api.zod.ts 0/1, references/api/protocol.mdx 0/1, references/kernel/package-registry.mdx 0/1. Dark control (a near-miss spelling): 0 on each. Every replacement names true ⇒ enable, false ⇒ disable, absent ⇒ no lifecycle call. ⇒ Fixes #19339 is consistent with the criterion as written.
  • The changeset prose was checked claim by claim (a changeset is a review face): the quoted old sentence, the 482d584121 attribution, the files[] fact, the dist reach, the two-layer split, the setPackageDisabled scope reason — all true at head. Clause-②: no present in both carriers.
  • One looseness, ⛔ not a falsity: «which is also why this door still does not forward the key» asserts a causal necessity the door's own comment does not state — nothing prevents forwarding; the door flips itself and writes the durable record from the row. The facts around it are right. Not a blocker.

② Semver level

patch on @objectstack/spec. Measured against files[] after a real build at this head in an isolated worktree (BUILD_DTS included, exit 0), then npm pack --dry-run --json → 2031 files.

  • Positive controls: subject src/api/package-api.zod.ts present (via src/**/*.zod.ts); dist/index.d.ts present. Negative controls: *.test.ts 0, content/ 0. The instrument discriminates.
  • Reach, re-derived rather than carried, using three phrases unique to the new comment, with the case-insensitive kernel-describe spelling as the false-positive contrast:
    • JS bundles 4/44dist/api/index.{js,mjs} and dist/browser/api/index.{js,mjs}, all four in the pack listing. Positive control (the api .describe() string): the same 4. ⚠️ Contrast «honours it on the registry row» case-insensitively: 8 — the kernel bundles join. That is what a careless probe would have counted.
    • Declaration files 0/126 (.d.ts + .d.mts); the pre-existing in-callback block also 0/126; class control — the export-level doc block on the same const — 2/126. So the non-reach is a property of the position inside the lazySchema callback, ⛔ not of this text.
    • Sourcemaps 0/44; json-schema/ 0/1538 (the api and kernel .describe() strings reach 3 each — positive); content/docs/** 0.
  • Description text only, no accept/reject or shape movement ⇒ patch is right, and skip-changeset would be false because the bytes ship in src and four bundles. Agreed.

③ Boundary flags

  • mergeable_state: dirty, confirmed locally with git merge-tree: the conflict is in packages/spec/src/api/package-api.zod.ts on exactly the reviewed paragraph. main (fb59fb5e37, fix(spec): enableOnInstall becomes optional() so absence survives the parse #19690) reads «Same type, same optionality, same meaning…» with z.boolean().optional(); this head reads «Same type, same default, … HONOURS it on the REGISTRY ROW …» and still carries z.boolean().default(true)the pre-fix(spec): enableOnInstall becomes optional() so absence survives the parse #19690 state the branch inherited, ⛔ not a change of this diff. Correct resolution keeps main's .optional() and "same optionality" plus this PR's honours clause; the stale two-state paragraph above it merges from main's side without conflict. That is the resolution the dispatched dev is applying.
  • The one red check is on the card, ⛔ not in the diff. The card this PR closes must claim this branch (run 35713968561) failed because at run time the governing Claim: named the first increment's branch; the seat's late claim now carries Branch: claude/issue-19339-package-api-denial, the directive line the guard reads. The guard triggers only on pull_request and merge_group, so a comment does not re-run it — the conflict-resolution push is the synchronize that clears it.
  • Escalated, and the seat is recording it: .changeset/18605-enable-on-install-one-authority.md:17 (pending, unreleased — and a changeset is a review face) still states «Its published description now records that this layer does not read it». It sits outside this card's executable criterion, so Fixes remains consistent — but Fixes closes the only open tracker naming that carrier. ⇒ The seat records a disposition before or at landing so the contradiction does not ship untracked. ⛔ Not a defect of this diff, and ⛔ not the dev's to edit.
  • Dev flags: open_questions empty; of three out_of_scope_findings, the references/api/package-api.mdx row is confirmed correct and unchanged, and the claim that «content/docs/releases/v17/17-0.mdx carries the same phrase» is measured falseenableOnInstall has 0 hits under content/docs/releases/**, and the one «does not read it» at :1010 is about a driver. Harmless, recorded so nobody goes looking.
  • Runtime, permission and security behaviour: none touched.
  • Carried, by rule (derived gate families are read from CI, ⛔ never re-run by the reviewer): check:generated "15 up to date", check:api-surface/check:authorable-surface, lint, the 76-family reconciliation and the test counts — all taken from this head's green check-runs, ⛔ not from the PR prose.

Implemented-by: claude/issue-19339-package-api-denial
Reviewed-by: session_013RDBh5DqXd2xnLwvHLgLFr

VERDICT: PASS — on head 3e94943aa7 only, per the binding note above.


Tier control — measured by the seat, not reported by the reviewer

The reviewer self-reported 18/18 and said plainly that a self-description is not a reading. It is right: the governing control is the transcript's per-request harness model stamp, and get_session is ⛔ not admissible as corroboration. So the seat counted the transcript:

  • 100 assistant requests, 100 stamped claude-fable-5-1 — every per-request "message":{"model":…}, no exceptions. (The reviewer's self-count was low by more than 5×, which is exactly why the self-count is not the reading.)
  • 118/118 counting every model field, per-request stamps and advisorModel alike.
  • Dark control: 0. Filtering every "model"/"advisorModel" occurrence for anything other than claude-fable-5-1 returns empty.
  • The four fallback/overload keyword hits were read in context: tool-schema prose and the reviewer's own sentences about tier. ⛔ None is a notice.
  • CONTRACT_REVIEW_TIER re-read at source (origin/main:scripts/pm/dispatch-gates.mjs) = claude-fable-5-1.

One finding that did not change the verdict, recorded for its own lane

packages/spec/tsup.config.ts's header asserts that "non-annotation comments are dropped from the bundle output". Measured false for comments inside the factory callback — 4 of 44 bundles carry them, as ② shows. It is an internal config comment, not published and not this PR's; no card filed by the reviewer (it made no writes). Noted here so the next person who trusts that header has the measurement.


Generated by Claude Code

Copy link
Copy Markdown
Collaborator Author

Contract review

Served-tier: 122/122 CONTRACT_REVIEW_TIER (seat-measured, 2026-09-22T14:53Z)
Head-sha: 6697f75093a5a1167181760ccc18847eada5326c

Rendered by an isolated at-tier review subagent. This head is the hand-resolved merge that the prior record (comment 5778173554, binding 3e94943aa7 only) said owes its own record. Every claim inside the resolved paragraph was re-measured against source here; nothing was concluded from the prior record. What the two heads share byte-for-byte is named as such below.

① Derived judgments

  • Diff at head, read from the merge-base fb7b74691f (three-dot against origin/main), not from the PR prose: 2 files, +30/−2 — one TSDoc block inside the lazySchema callback of PackageInstallRequestSchema (packages/spec/src/api/package-api.zod.ts:405–415) and one new changeset (.changeset/19339-package-api-install-door-denial.md). No .describe(), no key, type, optionality or default moves in this diff: enableOnInstall: z.boolean().optional() at package-api.zod.ts:434 is byte-identical to the merge-base (it is main's fix(spec): enableOnInstall becomes optional() so absence survives the parse #19690 state, not this PR's). Accept set unchanged, no new export, no refusal deleted ⇒ Clause-②: no is right on both limbs. The path limb fires, which is why this record exists. The line-leading Clause-②: no is present in the governing claim, the PR body (line 3) and the changeset (line 9).
  • What the merge commit hand-touched, measured with git diff-tree --cc: exactly one conflict hunk, in the reviewed paragraph. The only text that changed inside it between 3e94943aa7 and this head is «Same type, same default, same» → «Same type, same optionality, same», taken from main (fix(spec): enableOnInstall becomes optional() so absence survives the parse #19690, fb59fb5e37). The eleven honours-clause lines are byte-identical across the two heads and the changeset blob is identical (4383f1df1d). «Same type, same optionality» is true at head: api :434 and kernel package-registry.zod.ts:356 both read z.boolean().optional().
  • Layer 1 — the in-process primitive, judged at source: packages/metadata-protocol/src/protocol.ts:22773–22779requestedEnabled = request.enableOnInstall; === truethis.engine.registry.enablePackage(manifest.id); === falsedisablePackage; otherwise no lifecycle call. Never a truthiness test, never ??. The text's «HONOURS it on the REGISTRY ROW — true enables, false disables, an ABSENT key makes no lifecycle call at all» is correct. The «REGISTRY ROW» qualifier is load-bearing and true: after the flip the primitive's only persistence is pkgSvc.publish({ manifest, metadata: {} })INSERT INTO sys_packages (id, version, manifest, metadata, hash, created_at, updated_at) (packages/services/service-package/src/index.ts:384) — no enabled column; the kernel InstallPackageRequestSchema carries manifest, settings, enableOnInstall, platformVersion and no environment; the durable record is setPackageDisabled(environmentId, packageId, disabled) at packages/runtime/src/package-state-store.ts:168, file-keyed by environment (:112–113). The changeset's «for the life of the process» follows from the same facts.
  • Layer 2 — the REST door, judged at source: packages/runtime/src/domains/packages.ts:1045 calls protocolSvc.installPackage({ manifest, settings: body.settings }) — the key is not in that call, so «this door still does not forward the key down that seam» is still true. The door's own arms at :1094–1101 are === trueregistry.enablePackage, === falseregistry.disablePackage, absent ⇒ nothing, so «the same three states this door implements» is correct. :1143–1145 reads rowDisabled = pkg?.enabled === false and writes setPackageDisabled(_context?.environmentId, pkgId, rowDisabled), so «the record that survives a restart follows the row this door returned rather than the request's intent» is the door's own stated rule (:1102–1103), not a paraphrase.
  • The retained looseness, re-judged rather than carried: «which is also why this door still does not forward the key down that seam» is present at head, byte-identical to the prior head. Environment-keying explains why the primitive cannot write the durable record and why the door must own that write; it does not entail that the door must not forward the key (forwarding and then writing from the returned row would yield the same row), and neither packages.ts nor protocol.ts states that «why». The kernel doc block that arrived from main under fix(spec): enableOnInstall becomes optional() so absence survives the parse #19690 makes the same causal gloss at package-registry.zod.ts:337–340, so the two published blocks agree with each other, and every factual clause around the gloss is true. Non-blocking; recorded so nobody reads it as a mechanism the code states.
  • Card spec: three published copies of "this protocol primitive does not read it" are falsified the moment PR #19338 lands — and no gate can see it #19339's executable criterion, measured on the head tree across all four named places (it contains 7e1b048a1d and fb59fb5e37): «does not read it» hits — kernel/package-registry.zod.ts 0, api/package-api.zod.ts 0, references/api/protocol.mdx 0, references/kernel/package-registry.mdx 0. Dark control (near-miss spelling): 0 on each and 0 tree-wide. Equivalent-denial sweep (not read / never read / reads past / ignore / reads manifest and settings only) in the enableOnInstall context of the four: 0. Replacement text: the kernel block (:305–318) and the api block (:406–408) both state the third state literally as «no lifecycle call at all»; the two generated pages (protocol.mdx:1913, package-registry.mdx:187) carry the kernel .describe() as fix(spec): enableOnInstall becomes optional() so absence survives the parse #19690 rewrote it — «true enables, false disables, and ABSENT keeps the row's current lifecycle state (a fresh install lands enabled)», the third state by its outcome rather than by mechanism. Judged criterion-satisfying: the outcome is exactly what «no lifecycle call» produces (packages/objectql/src/registry.ts:4271–4290 carries an existing row's enabled/status through a re-install), it is the maintainer-ruled spelling (batch Fix fumadocs-mdx validation: flatten nested pages in concepts meta.json #210 item 4 letter A), and it reached this head from main under its own at-tier PASS, not from this diff. ⇒ Fixes #19339 is consistent with the criterion. The PR body's four-row table was measured at 3e94943aa7, before fix(spec): enableOnInstall becomes optional() so absence survives the parse #19690 regenerated those two rows — a prose staleness, not a diff defect.
  • Changeset prose checked claim by claim (a changeset is a review face): the quoted old sentence matches the merge-base text; 482d584121 is an ancestor of head (exit 0) and is the commit that added the requestedEnabled arms — controls: 596090efbe (wrote the denial) exit 0, a nonsense sha exit 128; the files[] fact and the four-bundle reach are measured in ②; the two-layer split and the setPackageDisabled(environmentId, …) reason are the source facts above; «no default changes / accept set byte-for-byte» is true relative to the merge-base. The check:generated «all 15 up to date» sentence is carried from CI, not re-run.

② Semver level

patch on @objectstack/spec — agreed. Measured against the package's own files[] after a real build at this head in an isolated worktree (gen:schema, gen:openapi, tsup, BUILD_DTS=true tsup — DTS success in 181 s; check-dts-emitted 34/34; check-dts-references 126 files, 378/378 — exit 0), then npm pack --dry-run --json2031 entries.

  • Subject: src/api/package-api.zod.ts present (via src/**/*.zod.ts, 203 of them, equal to the tree count). Positive controls: dist/index.d.ts present; dist/api/index.js, dist/api/index.mjs, dist/browser/api/index.js, dist/browser/api/index.mjs present. Negative controls: *.test.ts 0, content/ 0, src/api/package-install-one-authority.test.ts absent. The one src/ non-zod entry is src/migrations/entries/README.md, an npm always-included README — explained rather than left as a stray count.
  • Reach into built output, three phrases unique to the new comment, with controls: JS bundles 4/44 — exactly the four api bundles above (subject 4, positive control the api .describe() string 4, negative control a test-only phrase 0). Case-insensitive contrast on «honours it on the registry row»: 8/44 — the kernel bundles join, which is what a careless probe would have counted. Declaration files: subject 0/126; the api .describe() 0/126; the sibling in-callback overwrite doc block 0/126 (position control); class control — the export-level doc block on the same const — 2/126, so the non-reach into .d.ts is a property of position inside the lazySchema callback, not of this text. Sourcemaps 0/44 (positive control also 0/44 — no sourcesContent). json-schema/** 0/1538 (positive control: the api .describe() 3/1538, the kernel .describe() 3/1538). content/docs/** 0.
  • Description text only; no accept/reject, shape, default or export movement ⇒ patch is the right level, and skip-changeset would be a false declaration because the bytes ship in src and in four bundles.

③ Boundary flags

  • CI check-runs at head, newest run per name, read at the END of the review: 0 failed. The red the prior record flagged (The card this PR closes must claim this branch, run 35713968561) is green at this head. Test Core (5/6) was still in_progress at the reviewer's final read and recorded as NOT MEASURED; the seat re-read it after the review returned: 32 success, 3 skipped, 0 failures across 35 names. The head is fully green.
  • Mergeability: measured locally, git merge-tree --write-tree origin/main 6697f75093… against 106d4c8dd7 exits 0 — clean. The dirty state the prior record flagged is resolved by this head's single hunk, and the resolution is the one that record derived (main's .optional() and «same optionality» plus this PR's honours clause).
  • Manual floor: no ADR, no releases page, no CHANGELOG.md, no security, permission or runtime behaviour touched. content/docs/releases/** has 0 enableOnInstall hits; the dev's «17-0.mdx also carries the phrase» is measured false (its one «does not read it» at :1010 is about a driver). Harmless.
  • Carrier checker rows, and their disposition. C8 — two live Claim: comments by one author on the card — was a real board defect and it was this seat's own: a second Claim: written under a live one to close the branch-naming gap, when the protocol's spellings were a Release: then one fresh claim, or a Clause-②-correction: line. Repaired before this record: Release: 5778691928, fresh Claim: 5778700906, and the checker now reads «no author holds more than one LIVE claim comment on this thread», 1 comment in the pool. C6 is the missing record on this head — this comment is that remedy.
  • .changeset/18605-enable-on-install-one-authority.md, re-measured at head: line 17 still states «Its published description now records that this layer does not read it: the implementation reads manifest and settings only …», and at this head its «Same type, same default, same meaning» is false as well (both declarations are .optional() since fix(spec): enableOnInstall becomes optional() so absence survives the parse #19690). No sibling changeset corrects it, and no open issue names it other than spec: three published copies of "this protocol primitive does not read it" are falsified the moment PR #19338 lands — and no gate can see it #19339 — re-checked by the seat through a repo-scoped search, which returned spec: three published copies of "this protocol primitive does not read it" are falsified the moment PR #19338 lands — and no gate can see it #19339 plus three closed cards, the nearest being finding(changeset): the pending field-rows/option-description changeset still says the canonical field-level spelling is depends_on — false for this package, and it is release-notes input #15058, the same class and completed. Route 0 at .github/workflows/pr-automation.yml:717–735 means editing it here would have made this PR a DELIBERATE CORRECTION needing written confirmation with Check Changeset left red. ⇒ Not this PR's to solve — outside the card's four-place criterion, and the card body itself withheld the edit — but Fixes #19339 closes the only open tracker naming it. Disposition, recorded here so it does not ship untracked: the seat files it as its own card before this lands. Not a blocker of this diff.
  • Dev flags: open_questions empty; of the three out_of_scope_findings, the references/api/package-api.mdx row is confirmed correct and byte-unchanged by the PR (:498, :661), the releases claim is answered above, the 18605 note is answered above.
  • Carried by rule, ⛔ never re-run by the reviewer: the derived gate families — check:generated «15 up to date», check:api-surface / check:authorable-surface, lint, the 76-family reconciliation and the test counts — read from this head's check-runs.

Implemented-by: claude/issue-19339-package-api-denial
Reviewed-by: session_013RDBh5DqXd2xnLwvHLgLFr

VERDICT: PASS


Tier control — measured by the seat, not reported by the reviewer

The reviewer declined to self-count and said the transcript stamps are the control. Correct. The seat measured its transcript:

  • 122 assistant requests, 122 stamped claude-fable-5-1 — every per-request "message":{"model":…}, no exceptions.
  • 156/156 counting every model field, per-request stamps and advisorModel alike.
  • Dark control: 0. Filtering every "model"/"advisorModel" occurrence for anything other than claude-fable-5-1 returns empty.
  • The four fallback/overload keyword hits are tool-schema prose. ⛔ None is a notice.
  • CONTRACT_REVIEW_TIER re-read at origin/main:scripts/pm/dispatch-gates.mjs:12282 = claude-fable-5-1.

⚠️ A correction the reviewer caught in the seat's own citations

The dispatch cited references/contract-review.md:54 for the transcript-stamp rule. The reviewer reported that line does not exist, and it is right — measured: that file is 24 lines on origin/main (e37ea4d060) and 60 lines in the shared local checkout (1f53b0b685), which is behind. The rules were refactored out of it, so every contract-review.md:53/:54/:56 citation this seat has written points into a file that no longer has those lines.

The method is not affected — the rules are on origin/main, elsewhere:

  • references/platform-readings.md:387 — 无它时的合法替代 = grep 本会话 transcript 里 harness 写的 "model":,逐请求写入、非自述
  • SKILL.md:76 — 子代理裁决逐份过转录核验采信
  • SKILL.md:535 — 条款②自审资格按车道…未达档 ⛔ 不自审,起隔离达档子代理
  • contract-review.md:16 — check 结论取 head 的 check-runs,⛔ 永不本地重跑派生门禁族
  • contract-review.md:18 — 隔离复核子代理暂存全写按所审 PR 命名的目录
  • contract-review.md:22 — 纯重生成 head 后移原记录继续管 (which is exactly why this head needed its own record: a hand-resolved hunk is not a pure regeneration)

⇒ This is the same failure shape as the two the seat has already recorded today — reading a copy instead of the source. Citing a line number from a stale checkout is exactly that, and it is the third instance. The seat's patrol text is corrected to cite origin/main only.


Generated by Claude Code

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

Labels

documentation Improvements or additions to documentation size/s tooling

Projects

None yet

Development

Successfully merging this pull request may close these issues.

spec: three published copies of "this protocol primitive does not read it" are falsified the moment PR #19338 lands — and no gate can see it

2 participants