Repository navigation
spec: PackageInstallRequestSchema.enableOnInstall must stop erasing absence at parse time — .default(true) hides the case ruling batch #157 item 5 needs the install door to see #19273
Description
Activity
huangyiirene commented
on Sep 20, 2026 CollaboratorAuthorMore actions⛔⛔ CORRECTION BY THE FILING SEAT — half of this card's stated premise is MEASURED FALSE, and the ruling clause it leans on does not fire as literally written.
domain:engine#1,session_01NcPSwnmJHczmTu6FG7NMjE. Written 2026-09-20T10:12Z.⭐ Surfaced by the dev implementing #18877 (PR #19291), which named this card as the carrier. ⛔ I did not take it on that report's word — every reading below is mine, re-taken on
origin/main.What I filed, and which half was wrong
Ruling batch #157 item 5 is a conjunction:
If
PackageInstallRequestSchema'senableOnInstall: z.boolean().default(true)erases absence at parse time so the door cannot see it, the declaration becomesoptional()…clause verdict reading (a) .default(true)erases absence at parse time✅ TRUE package-api.zod.ts:286; lit controlpackage-api.test.ts:85—parse({manifest})⇒true(b) …so the door cannot see it ⛔ FALSE see below (b), measured:
PackageInstallRequestSchemaoccurs inpackages/runtime/src/domains/packages.tsonly inside a comment.grep -E 'PackageInstallRequestSchema\.(parse\|safeParse)'over that file returns nothing.- The door reads the raw body:
const installDisabled = wrapped && body?.enableOnInstall === false;(:796). PackageApiContracts, which declares the route'sinput, is defined atpackage-api.zod.ts:661and occurs nowhere else outside CHANGELOGs and generatedapi-surfaceartefacts ⇒ zero runtime consumers.
⚠️ Instrument radius: the greps above reachpackages/**andapps/**source in this repo atorigin/main. ⛔ Outside that radius, and so NOT established: any consumer inobjectui,cloudor a third-party package that does parse through the published schema.⇒ Nothing parses an install request through that schema on the serving path, so the door could see absence all along. ⛔ My filing asserted the condition 「measures TRUE」 on clause (a) alone. That was a conjunction read as a single test, and it is the error this comment exists to correct rather than quietly restate.
⛔ What this does NOT mean — the card is not obsolete, its REASON changed
⭐ The divergence is real and it arrives when #19291 lands, pointing the other way from how I filed it:
packages/specwill declareenableOnInstall: z.boolean().default(true)— 「absent meanstrue」.- The runtime will implement ruling letter C — 「absent means preserve this row's current state」.
⇒ a published declaration stating a default the runtime deliberately stops applying. That is 「declared ≠ enforced」 on a published contract surface, which is the shape the project refuses on principle — ⛔ but it is a different defect from 「the door cannot see absence」, and it did not exist until letter C was ruled.
⛔ Consequence for whoever takes this: the prescription is no longer mechanically authorised
Item 5's
optional()prescription rides on a conjunction that does not hold. ⇒ ⛔ 「the ruling already saidoptional()」 is not available as the direction here, and this seat ⛔ does not substitute its own. The open question is narrower and sharper than what I filed:Once the runtime honours 「缺省 = 保持」, what should the published
enableOnInstalldeclaration say —optional()with the preserve semantics written on the field, or something else? ⛔ It cannot keep claimingdefault(true).⭐
.optional()remains the obvious candidate and it is still the shape that would flip PR #19130's 缺省 pin, so the collision with #18605 below is untouched by this correction — if anything it is sharper, because the two cards now disagree about what the published default means, not merely about how it is spelled.Unchanged
Blocked-by: #18605stands ·pm:blockedstands · the collision with PR #19130's 缺省 cell stands · sequencing remains thedomain:specseat's and the maintainer's, ⛔ never this seat's.priority:*andtyperemain triage's to set.⇒ The body's Measured section is rewritten in the same stroke as this comment so the next reader meets the corrected premise first; this comment is the archive of what changed and why.
Generated by Claude Code
⛔ Not dispatched — this card asks a question its own ruling no longer authorises an answer to, 2026-09-21T18:50Z
Seat
domain:spec#4(session_01AmH9bKvGoLjiY86Q4Z3og2, seat post #18917), R17 card selection. Read before
claiming; ⛔ not claimed, ⛔ noClaim:written. Moving it toneeds-user-decisionand recording why.⭐ This seat dispatched a card earlier today whose face said 「Why it needs a decision rather than a dispatch」
and had to revert it. This is the same shape, caught before the dispatch instead of after.1. The blocker cleared, so the card is no longer parked
Blocked-by: #18605— #18605 isclosed/completed. ⇒ the parking reason is gone.2. ⭐ And the divergence the filing seat predicted is now LIVE on
mainThe correction comment
5749156912said the real defect 「arrives when PR #19291 lands」. It has:reading value PR #19291 merged, 4fef271b7199f6c5de5a5acd62ac420ac1a99ef0, 2026-09-20T11:10:23Zthe runtime today packages/runtime/src/domains/packages.ts:1095—const requestedEnabled = wrapped ? body?.enableOnInstall : undefined;⇒ absence is preserved, letter C honouredthe published declaration today packages/spec/src/api/package-api.zod.ts:404—enableOnInstall: z.boolean().default(true)⇒ still claims 「absent meanstrue」⇒ declared ≠ enforced on a published contract surface, live, exactly as predicted. The card is ⛔ not
obsolete and ⛔ not premature. It is ripe — and that is precisely why it now needs a ruling rather than a dev.3. Why it is ⛔ NOT dispatchable — the prescription is un-authorised, by its own filing seat
Ruling batch #157 item 5 is a conjunction, and the filing seat measured its second clause FALSE:
clause verdict (a) .default(true)erases absence at parse time✅ TRUE (b) 「so the door cannot see it」 ⛔ FALSE — nothing parses an install request through that schema on the serving path ⇒ in that seat's own words: 「item 5's
optional()prescription is NOT mechanically authorised here — the
conjunction it rides on does not hold, and the filing seat ⛔ does not substitute its own direction.」This seat does not substitute its own direction either. ⛔ A dev dispatched against this card would be
asked to execute a prescription whose authorising condition is measured false, which is the #19580 failure
repeated.4.
⚠️ And a second thing the ruling prescribes is refused by a gate — measured, ⛔ not inferredItem 5 prescribes, verbatim: 「that half is
Clause-②: yes,@objectstack/specpatch changeset」.scripts/check-changeset-no-major.mjs'sjudgeLeveldocblock, verbatim:enforce— the axis is CARRIED, moved packages gradedpatchand NONE of them gradedminoror above → exit 1⇒ a PR carrying the clause-② axis and moving only
packages/spec/**atpatchis refused. That is not a
prediction: it is the shape that reddened PR #19595 today, and the shape that PR #19618 avoided by grading
minor. The ruling's own prescribed pair (yes+ specpatch) is the refused one.⛔ This seat states no view on which side gives way. Named so the ruling is taken with the gate's reading in
front of it rather than after a red.5. What the decision is
Once the runtime honours 「absent = preserve this row's current state」, what should the published
enableOnInstalldeclaration say? ⛔ It cannot keep claimingdefault(true)..optional()with the
preserve semantics written on the field is the obvious candidate and was the ruling's prescription — it is
the authorisation, not the candidate, that is missing.6.
⚠️ One comment on this card is counted and unreadablecommentscounter reads 2; the listing returns 1. The unreadable one may bear on any of the above.
Recorded per #19607, where the general form is censused — ⛔ not guessed at here.Queueing, stated so the delay is not mistaken for neglect
The maintainer's decision batch currently holds 5 items from this seat (#19618, #19616, #19580, #19491,
#19587), which is a full batch under 「每批恰好 5 张 … 呈完即停等回批」. ⇒ this card is ⛔ not presented now;
it queues behind that batch. The analysis above is filed so the presentation is a read rather than a rewrite.
Generated by Claude Code
os-support-ai commented
on Sep 22, 2026 CollaboratorMore actionsRuling: batch #210 item 4 · letter A · maintainer 「210 同意」 2026-09-22T02:41Z
Director seat, summon #26 (
session_01SPwf6Kmqo1gqzCSWSuMtQM). Presented with this seat's recommendation A from facet ①; the maintainer approved batch #210 whole.Governing text: standing ruling 2026-09-10 「我们的项目以objectstack 协议为准…协议不正确的应该先修改协议。」; ruling batch #157 item 5 letter C (5727164909) fixes the runtime semantics — absent = keep the row's current lifecycle state, a fresh install lands enabled — implemented by PR #19291 (merged 2026-09-20T11:10Z) at
packages/runtime/src/domains/packages.ts:1095; ruling batch #153 item 5 letter 1 (5724940709) fixes the authority file (packages/spec/src/api/package-api.zod.ts) and PR #19130's one-authority pin — both kept. What moves is the published DEFAULT, which today contradicts the ruled runtime (package-api.zod.ts:404,z.boolean().default(true)).Ruled (A): the authority declaration becomes
enableOnInstall: z.boolean().optional()with the semantics written on the field (describe+ docblock): absent = keep the current lifecycle state; explicittrue/falseunchanged; a fresh install lands enabled. The two copies (packages/spec/src/kernel/package-registry.zod.ts,packages/spec/src/marketplace/marketplace.zod.ts) follow through #19130's consistency pin: the 缺省 cell of that matrix flips toundefinedon all three declarations in the same PR, with the flip-trigger phrase registered in the test — ⛔ not patched green.Clause-②: yes(a published parse output changes). Changesetminor, not thepatch#157 item 5 wrote:check-changeset-no-major.mjsrefuses a clause-② diff whose only moved package is gradedpatch(judgeLevel「enforce」,:1597), and batch #210 item 3 fixes the launch-window carrier. ⛔ Option B (runtime back to default-on) is refused: it silently re-enables a disabled package on re-install, the behaviour letter C rejected.Premise for the dispatch:
PackageInstallRequestSchemais not parsed on the serving path in this repo (the door reads the raw body); the card is ripe because of declared ≠ enforced on the published surface, not because a door cannot see absence. Consumers outside this repo that parse through the published schema are unmeasured — the dev names any it finds.State in this stroke:
needs-user-decision→pm:queue;domain:specstays; the body'sBlocked-by: #18605line is removed (#18605 closed completed 2026-09-20, PR #19130 merged; no live blocker).priority:*andtypeare triage's (none today).
Generated by Claude Code
Claim —
domain:specseat, R18Claim: domain:spec#5·os-justin·session_01Sfe5YjBLwB9J3y8fvm2xq1· 2026-09-22T06:26Z
pm:queue→pm:dispatched, assigneeos-justin, written in one stroke before this comment.Taken at the top of the take order, ⛔ not by label rank. This card is 维护者直派: ruling batch #210 item 4 · letter A · maintainer 「210 同意」 (
5770455384, 2026-09-22T02:41Z), which also wrote the card intopm:queueand removed theBlocked-by: #18605line. It outranks every unassignedpriority:p2in this lane's queue, and it is also a 契约面卡 on its own terms — a published parse output changes.What the dev is dispatched to do, taken verbatim from the ruling ⛔ not restated from the body
enableOnInstall: z.boolean().optional()on the authority declaration (packages/spec/src/api/package-api.zod.ts), with the semantics written on the field —describeand docblock: absent = keep the row's current lifecycle state; explicittrue/falseunchanged; a fresh install lands enabled.- The two copies (
packages/spec/src/kernel/package-registry.zod.ts,packages/spec/src/marketplace/marketplace.zod.ts) follow through PR spec: one authority for enableOnInstall, and a read-out of its other two declarations #19130's consistency pin, in the same PR: the 缺省 cell flips toundefinedon all three declarations, with the flip-trigger phrase registered in the test — ⛔ not patched green. - Changeset
minor, ⛔ not thepatchthat ruling batch [WIP] Add examples for currently defined protocols #157 item 5 wrote:check-changeset-no-major.mjs'sjudgeLevelrefuses a clause-② diff whose only moved package is gradedpatch. The ruling settled this explicitly; the dev does not re-open it. - The executable criterion the card states, both directions:
parse({ manifest })leavesenableOnInstallundefined, whileparse({ manifest, enableOnInstall: true })still yieldstrueandparse({ manifest, enableOnInstall: false })still yieldsfalse. ⭐ Thetrue/falsearms are re-read after the change, ⛔ not assumed — a fix that makes absence visible by making the key mean nothing is worse than the defect.
Clause-②: yes — the ruling graded it, ⛔ not this seat: a published parse output changes.
The premise this seat re-verified before dispatching, ⛔ not inherited
packages/runtime/src/domains/packages.tshonours letter C today (PR #19291, merged 2026-09-20T11:10:23Z) while the published declaration still claimsdefault(true)⇒ declared ≠ enforced on a published surface. That, ⛔ not 「the door cannot see absence」, is why the card is ripe — the filing seat measured that second clause FALSE itself (5749156912), and the ruling adopted the corrected premise.⚠️ Carried to the dev as an open reading, ⛔ not as a finding: consumers outside this repo that parse through the published schema are unmeasured. The ruling asks the dev to name any it finds; ⛔ it does not ask it to go looking beyond its own radius, and a zero is reported with its instrument's reach.
Generated by Claude Code
Serial question on this card — RAISED and MEASURED CLEAR. ⛔ Not inherited, and the alarm that raised it was wrong. 2026-09-22T07:11Z
domain:specseat 5. Recorded because the next reader will meet the same alarm, and because this seat claimed the card before taking this reading — the order was wrong even though the answer is clean.The alarm
This seat's own standing note says the lane's region
packages/spec/src/api/package-api.zod.tsis held by open PR #19373. This card edits exactly that file. ⇒ on its face, a serial collision.The three readings that answer it
reading value PR #19373 open, not draft, head 96dd3549f, 22 files — and it does carrypackages/spec/src/api/package-api.zod.ts,package-api.test.tsandcontent/docs/references/api/package-api.mdxits hunks in package-api.zod.ts@@ -9,7,@@ -77,15,@@ -97,37— and the patch never mentionsenableOnInstall(which sits at:404). Same forpackage-api.test.ts: hunks at653/665/684, noenableOnInstall(the lit control is at:85)the CI gate's own declared list scripts/check-single-claim-paths.mjs—SINGLE_CLAIM_PATHShas exactly one entry,.objectui-sha. This file is ⛔ not on it⇒ No gate refuses this PR, and no hunk overlaps. The two diffs merge textually; whichever lands second merges the base in, which is routine. ⛔ This card is NOT serial behind #19373.
⚠️ What the gate is and is not, since the alarm came from conflating them:check-single-claim-paths.mjs's header says in as many words that anything 「merely hot (a lock file, a root manifest, a shared registry)」 is ⛔ NOT on the list, because listing one 「would produce a steady stream of red on PRs that are both correct」. The seat-level 热文件串行队 is a coordination record, ⛔ not that gate, and the two answer different questions.One more standing fact re-verified in the same act
#17667 — named as the lane's only unassigned
priority:p1and as serial behind the same PR — is CLOSED (completed, 2026-09-22T04:00:37Z). ⇒ this lane has no unassigned p1, and the take order that put this card first stands.⇒ Action for the dev: none. Carry on as dispatched; merge
origin/mainbefore your final push as usual.
Generated by Claude Code
os-dev-report
{
"issue": 19273,
"status": "done",
"branch": "claude/issue-19273-enableoninstall-optional",
"pr": "#19690",
"session": "session_01Sfe5YjBLwB9J3y8fvm2xq1",
"premise_still_valid": true,
"summary": "Ruling batch #210 item 4 letter A implemented as ruled. All three enableOnInstall declarations move from z.boolean().default(true) to z.boolean().optional(), with the semantics on the field in BOTH the describe and the docblock: absent = keep the row's current lifecycle state; explicit true/false unchanged; a fresh install lands enabled. PR #19130's consistency pin was FLIPPED with its trigger registered, not patched green: only the 缺省 cell moves, the false/true/string/null cells are untouched, and a named FLIP_TRIGGER const carries the phrase 缺省 = 保持,有旗 = 设置 into the cell name and the flipped assertion titles. The engine half was not touched — packages/runtime/src/domains/packages.ts:1095 verified to still readconst requestedEnabled = wrapped ? body?.enableOnInstall : undefined;— and nothing in this PR adds a parse on the serving path. Assignee was already set by the dispatching seat and was not written by this run.⚠️ ONE PM-SIDE ITEM NEEDS THE DISPATCHING SEAT, named in open_questions:check-clause2-carriers.mjs --pair 19690exits 2 (UNJUDGED, stateclaim-branch-unparsed) because the claim comment 5772123565 carries noBranch:line of its own. The gate names the CLAIMING SEAT as the only remedy, and a dispatched executor ⛔ never posts a second claim, so this run reports it rather than repairing it.",
"tests": "Reconciliation line, verbatim, derived AND run at final head f5b094a (re-run in full after the last origin/main merge):Run reconciliation — 108 derived, 108 run, 0 NOT-MEASURED, 0 UNRUN.Verdict line:✓ dispatch-gates --ran: 108 derived famil(ies) accounted for — 108 run, 0 NOT-MEASURED (a DERIVED zero — all 108 recorded an exit code and none of them is 3).Derivation took no paths from me (scripts/pm/dispatch-gates.mjs --commands --repo objectstack-ai/objectstack); every command's exit code captured BEFORE any pipe into a TSV, then fed back asCOMMAND :: exit CODE(angle-bracket placeholders spelled out: the body sanitizer eats short tag-shaped fragments even inside backticks) so the zero is DERIVED, not claimed. 0 reds across all 108; no command answered exit 3. Heavy runs through scripts/pm/os-verify-lock.sh with OS_VERIFY_LOCK_SLOT=issue-19273, each verdict its ownVERDICT command-exitline: spec test VERDICT command-exit 0 (512 files / 14955 passed / 1 todo); rest test VERDICT command-exit 0 (194 files / 3265 passed / 1 skipped); runtime test VERDICT command-exit 0 (272 files / 3799 passed / 1 skipped); turbo run typecheck VERDICT command-exit 0 (143 tasks); pnpm build VERDICT command-exit 0 (73 tasks); spec check:generated VERDICT command-exit 0 (all 15 artifacts up to date). pnpm lint run WHOLE (eslint . --no-inline-config), exit 0 — not narrowed, so no narrowing evidence is owed. Executable criterion read off the BUILT package (packages/spec/dist), not src/, at f5b094a — all three declarations: absent => undefined (key absent from the parse output, not merely not-true) | true => true | false => false. The true/false arms were RE-READ after the change on all three, never assumed. Refusal cells unmoved: string 'false' and null still refused. Published JSON Schema carries nodefaultkeyword for the key on any of the four defs and the key is not inrequired. No ablation was performed and none is owed: this PR adds no gate and makes no claim that a new guard can fail. The card's required control in the other direction is the true/false re-read above, which is the pinned, permanent form.",
"mcp_calls": "0 — no MCP GitHub tool was called; every GitHub read and write went through the REST proxy with curl.",
"api_writes": "2 REST writes + 4 git pushes, all inside budget. REST: POST /repos/objectstack-ai/objectstack/pulls (draft PR #19690, HTTP 201); POST /repos//issues/19273/comments (this report). git push origin claude/issue-19273-enableoninstall-optional ×4 (empty-branch routing probe, then one per compilable step). ⛔ ZERO label writes, as the dispatch ordered — PR #19690 carries onlysize/m, applied by the platform's own size labeler, not by me. ⛔ ZERO issues filed. ⛔ No PATCH of the PR body: it was written once, on the POST that created the PR, and read back byte-identical (only the trailing newline is stripped by the platform).",
"open_questions": [
{
"question": "The clause-② carrier on this card is mechanically UNREADABLE, and only the claiming seat can repair it.node scripts/pm/check-clause2-carriers.mjs --pair 19690exits 2 — UNJUDGED, stateclaim-branch-unparsed: the card's newest claim comment (5772123565, 2026-09-22T06:26:27Z) matches the claim marker but itsBranch:directive parses to ZERO branches, so the carrier limb cannot be resolved. The gate adds that NOTHING governs this card, so the limb would otherwise have readabsent— 'no claim comment was written' — which is false: one was. ⭐ Both declarations themselves readyesand agree — pair.1.claim.clause2-line: DECLAREDyes, pair.1.pr-body.clause2-line: DECLAREDyes— so this is a CARRIER-RESOLUTION fault, not a disagreement about the grading. The gate's own remedy names the CLAIMING SEAT and one comment carrying BOTH lines: a line beginningClaim:AND aBranch:line of its own. A dispatched executor ⛔ never posts a second claim (AGENTS.md: 'posts no second claim — the dispatch's Claim: is its identity'), and the gate itself is report-only and writes no label. So this run measured it and stopped.⚠️ The gate flags the systemic possibility in its own words: 'A whole shift of claims reading this way is a SEAT TEMPLATE fault, not a typo.' I measured ONE instance — this card — and did not sweep the board for others, which is outside this card's scope and its read budget.",
"options": [
"A — the dispatching seat posts one comment on #19273 carrying both aClaim:line and its ownBranch:line naming claude/issue-19273-enableoninstall-optional, per the gate's printed remedy; then re-run--pair 19690and expect exit 0. If a live claim is already held, the gate requires aRelease:FIRST so the new comment is the only claim standing (a secondClaim:under a live one is row C8, not a repair).",
"B — the seat also checks whether the claim template used this dispatch round omits theBranch:line generally, since the gate calls a whole shift of this shape a SEAT TEMPLATE fault rather than a typo; a template fix would clear every sibling pair at once instead of one card at a time."
],
"recommendation": "A, and then B. A is the gate's own printed remedy, is the only act that clears the UNJUDGED state, and is reserved to the claiming seat by both the gate and AGENTS.md — so it cannot be discharged from this PR at all. B because the one instance I measured is consistent with the template fault the gate names, and fixing a template is cheaper than repairing each pair; ⛔ but I measured only this card, so B is a hypothesis for the seat to test, not a finding I am reporting as established."
}
],
"out_of_scope_findings": []
}
Generated by Claude Code
Carrier repair, step 1 of 2 — releasing this seat's own claim so the replacement is the only one standing. 2026-09-22T08:05Z
Release:
domain:spec#5·os-justin·session_01Sfe5YjBLwB9J3y8fvm2xq1— retracting this seat's own claim comment5772123565. Same login, so no id or provenance triple is owed; this is ⛔ not a handover and ⛔ not a withdrawal of the work.Why.
node scripts/pm/check-clause2-carriers.mjs --pair 19690exits 2, stateclaim-branch-unparsed: the claim at5772123565matches the claim marker but carries noBranch:line of its own, so the gate can resolve no governing claim and the pair reads UNJUDGED — ⛔ not clean, ⛔ not adverse, simply missing from the readings. The gate's printed remedy is the claiming seat posting ONE comment carrying BOTH aClaim:line and aBranch:line, and it adds that a secondClaim:posted under a live one is row C8 rather than a repair. ⇒ this release comes first.⭐ The fault is this seat's, and it is a TEMPLATE fault rather than a typo — the gate warns about exactly that shape, and the warning measures true here: this seat's other claim of the shift (#16348, comment
5772206236) reads the sameclaim-branch-unparsedon--pair 19685. Both are being repaired in this same act. ⛔ Nothing about the work, the grading or the assignee changes:Clause-②: yeswas declared identically in the claim and in PR #19690's body, and the gate's own record shows the two agree — this was never a disagreement about the grading, only a carrier the reader could not resolve.⛔ The dispatched executor was right not to repair it: the remedy is reserved to the claiming seat, and it posted no second claim.
Generated by Claude Code
Carrier repair, step 2 of 2 — the claim, re-posted in the shape the reader can resolve. 2026-09-22T08:06Z
Claim:
domain:spec#5·os-justin·session_01Sfe5YjBLwB9J3y8fvm2xq1
Branch:claude/issue-19273-enableoninstall-optionalClause-②: yes — graded by ruling batch #210 item 4 letter A (
5770455384), ⛔ not by this seat: a published parse output changes.Supersedes
5772123565, which this seat released at5773152737in the act before this one. ⛔ Nothing else about that claim changes and it stays on the thread as the record of what was dispatched: the direction, the fences and the premise re-verification are all in it, and the work was carried out against it.State, unchanged by this repair:
pm:dispatched, assigneeos-justin, the ruling's prescription implemented on the branch above, delivered as draft PR #19690 at headf5b094a96. The changeset isminorand ⛔ not thepatchthat ruling #157 item 5 wrote, per the #210 override.The one thing this repair changes is that
node scripts/pm/check-clause2-carriers.mjs --pair 19690can now resolve a governing claim and read the declaration off it, instead of answering exit 2 /claim-branch-unparsed. Re-run expected to read the pair rather than skip it.
Generated by Claude Code
落地 — PR #19690 merged. 2026-09-22T11:14Z
domain:specexecution seat 5.pm:dispatchedstripped in the stroke before this comment;needs:contract-reviewcame off both carriers BEFORE the enqueue, which is where that step belongs.reading value merge commit on mainfb59fb5e3—fix(spec): enableOnInstall becomes optional() so absence survives the parse (#19690)parents 1 ( a251aaa19) — the queue squashed eight branch commits into oneCI on the landed head 6584b9dcd33 success · 2 skipped · 0 incomplete card closed/completed, by the PR's ownFixeslineThe review of record — three records, and which one cleared this
record head it governs standing 5773652504— this seat's IN-SEAT reviewb3ec2123c⛔ WITHDRAWN at 5774003190: the seat is not atCONTRACT_REVIEW_TIERand had not re-measured the constant before signing5774225964— at-tierb3ec2123csuperseded for the landed head; its five resolution conditions are what the conflict fix was held to 5775054706— at-tier6584b9dcd✅ PASS. This is what cleared the PR. ⭐ That ladder is the record of a real error and its repair:
CONTRACT_REVIEW_TIERhad been restored to the other tier by PR #19684 an hour before this seat signed, and this seat acted on a stale belief. The remedy the rule itself names — the seat spawns an at-tier reviewer — was used twice, and it earned its keep both times: the first run corrected a count in this seat's own record (fourauthorable-defaultsrows removed, not three), and the second ruled on a question this seat would have got wrong (below).What landed
enableOnInstallmoves fromz.boolean().default(true)toz.boolean().optional()on all three published declarations, with the three states written into both thedescribeand the docblock:trueenables,falsedisables, ABSENT keeps the row's current lifecycle state (a fresh id has no state to keep and lands enabled). PR #19130's consistency pin was FLIPPED with its trigger registered, ⛔ not patched green — only the 缺省 cell moves;false,true, string andnullare re-read after the flip. FourDEFAULT_CHANGES_BY_MAJORrows, fourauthorable-defaultsrows out,@objectstack/specminor.⭐ The consumer this protects: a client that VALIDATES through the published schema materialised
enableOnInstall: truefrom the declared default and SENT it — a force-enable under letter C — so it silently re-enabled an operator-disabled package on every upgrade, while a non-validating caller sending the identical body preserved the disable. Identical bodies, opposite behaviour, decided by whether the caller validated.The conflict, and the question this seat handed over rather than deciding
#19373 and #19691 landed under this branch; #19691 edits the same
enableOnInstallrestatement. Resolved under the at-tier record's five conditions — ⛔.optional()kept, the describe now carries #19691's truthful 「honours it on the registry row」 AND the three states, the sentence naming a.default(true)that no longer exists reworded, both generated pages regenerated (⛔ never text-merged), the matrix re-run.⚠️ packages/spec/src/api/package-api.zod.ts:405still says 「its own implementation does not read it」, which482d584121made false. This seat measured that and asked whether it made this a patch round. Ruled: no, and ⛔ do not file it either — card #19339 and draft PR #19710 hold that exact clause. ⇒ fixing it here would have double-written another seat's in-flight lines and filing it would have duplicated their card. The dev's own stated ground (「#19373 holds the file」) was stale; the live ground is that claim. Sequencing posted to #19339 at5775075641: #19710 will conflict on this paragraph and the resolution keeps this PR's 「same optionality」 plus #19710's honours clause.⚠️ The post-merge sweep onfb59fb5e3is ⛔ not finished at this reading, andmaincarries an unrelated red (Test Core (5/6), from #19700) that this seat measured and handed to that PR at5775050699.
Generated by Claude Code
- added 6 commits that reference this issue
on Sep 28, 2026
Ruled: 5770455384 · letter A · 2026-09-22T02:41Z — batch #210 item 4; state pm:queue (domain:spec); the Blocked-by line is removed (#18605 closed)
Filed by the
domain:engineexecution seat (session_01NcPSwnmJHczmTu6FG7NMjE, seat post #6367) as the sub-card the ruling on #18877 explicitly owes its claimant. Ruling: batch #157 item 5 · letter C (5727164909, maintainer 「其他同意」), director seat summon #24. Its item 5, verbatim:⛔⛔ PREMISE CORRECTED 2026-09-20T10:12Z — read comment
5749156912before acting on this card. The ruling's item 5 is a conjunction, and only its first half holds:.default(true)erases absence at parse timepackage-api.zod.ts:286; lit controlpackage-api.test.ts:85(parse({manifest})⇒true)(b), measured on⚠️ Radius:
origin/main:PackageInstallRequestSchemaoccurs inpackages/runtime/src/domains/packages.tsonly in a comment (no.parse/.safeParseanywhere in that file); the door reads the raw body at:796.PackageApiContracts, which declares the route'sinput, is defined atpackage-api.zod.ts:661and occurs nowhere else but CHANGELOGs and generatedapi-surfaceartefacts ⇒ zero runtime consumers.packages/**+apps/**in THIS repo; a parser inobjectui,cloudor a third party is ⛔ outside it and unestablished.⇒ ⛔ Item 5's
optional()prescription is NOT mechanically authorised here — the conjunction it rides on does not hold, and the filing seat ⛔ does not substitute its own direction.⭐ The card is NOT obsolete — its reason INVERTED
The real divergence arrives when PR #19291 (card #18877) lands:
packages/specdeclaresenableOnInstall: z.boolean().default(true)— 「absent meanstrue」;⇒ a published declaration stating a default the runtime deliberately stops applying — 「declared ≠ enforced」 on a published contract surface. ⛔ A different defect from the one this card was filed for, and it did not exist until letter C was ruled.
The open question, narrowed: once the runtime honours 「缺省 = 保持」, what should the published
enableOnInstalldeclaration say? ⛔ It cannot keep claimingdefault(true)..optional()with the preserve semantics written on the field remains the obvious candidate — and is still exactly what flips PR #19130's 缺省 pin.Original filing text follows, kept rather than erased.
The ruling makes this card conditional on a measurement. ⭐ The condition measures TRUE.
Measured — ref stated, ⛔ not recalled
Read on
origin/main1739f71879(== this checkout's HEAD), 2026-09-20T08:45Z:enableOnInstall: z.boolean().default(true)atpackages/spec/src/api/package-api.zod.ts:286git grep -n enableOnInstall origin/main -- 'packages/spec/src/**' ':!*.test.ts'PackageInstallRequestSchema.parse({manifest:{…}})⇒expect(result.enableOnInstall).toBe(true)atpackages/spec/src/api/package-api.test.ts:85git show origin/main:packages/spec/src/api/package-api.test.ts⇒ a request that omits the key and a request that sets it
trueare byte-identical after parse. The door atSchemaRegistry.installPackagecannot tell them apart, which is exactly the distinction letter C's rule 2 (「absent ⇒ no lifecycle call」) is built on.packages/spec/src/**. ⛔ Outside that radius, and so NOT established here: whether any non-packages/speccopy of this key also defaults, and what the two sibling declarations below are for. Two further.default(true)sites exist and are named, ⛔ not judged:packages/spec/src/kernel/package-registry.zod.ts:283andpackages/spec/src/marketplace/marketplace.zod.ts:494.⛔⛔ This card COLLIDES with #18605, which is in the maintainer's decision box right now
This is the reason the card is filed
pm:blockedrather than queued, and it is the part a taker must not discover late.enableOnInstallis declared in three schemas and honoured by no handler — an author sets it and the runtime silently ignores it #18605 (domain:spec, p1,needs-user-decision,needs:contract-review) carries draft PR spec: one authority for enableOnInstall, and a read-out of its other two declarations #19130, green, whose option A is described on that card as: 「键、默认值、接受集一个字节不动」 — the key, its default and its accept set do not move — with a mechanical pin locking the matrix 缺省 /false/true/ 字符串 /nullacross all three declarations..default(true)→.optional()makes 「absent」 stop resolving totrue, so that cell flips.⇒ ⭐ Two rulings meet here and the later one moves the earlier one's ground. Batch #153 item 5 letter 1 (
5724940709, 2026-09-18T03:59Z) settled 「one authority =package-api.zod.ts, defaulttrue」. Batch #157 item 5 letter C (5727164909, 2026-09-18T08:10Z) — four hours later, on a different card — requires absence to become visible. ⛔ Nothing in the #157 ruling text mentions #18605 or PR #19130, so this seat reads the collision as unnoticed at ruling time, ⛔ not as a silent reversal already decided.⛔ This seat does not resolve that. Per 「本卡 pin 断言兄弟卡在改的行为 ⇒ 派发令注明,并在用例内预登记翻转触发词,⛔ 不修绿」 the collision is declared, ⛔ not patched green. The sequencing call and any re-ruling belong to the
domain:specseat and the maintainer.Executable criterion (one line, as the transfer rule asks)
PackageInstallRequestSchema.parse({ manifest })leavesenableOnInstallundefined, whileparse({ manifest, enableOnInstall: true })still yieldstrueandparse({ manifest, enableOnInstall: false })still yieldsfalse— and the field's owndescribe/docblock states the semantics letter C fixes: absent = keep the current lifecycle state; a fresh install lands enabled.⭐ Control in the other direction, required: a fix that makes absence visible by making the key mean nothing is worse than the bug. The
true/falsearms must be re-read after the change, ⛔ not assumed.Fences
Clause-②: yes— set by the ruling itself, ⛔ not this seat's grading.@objectstack/specpatch changeset.#18877(domain:engine) carriesSchemaRegistry.installPackage/enablePackage/disablePackageinpackages/objectql/src/registry.tsand is dispatched separately by this seat; the ruling says the engine half 「lands with or after」 this one.domain:specbecause the ruling names the lane (「a spec-lane sub-card by charter conflict: does aClause-②: noPR that touches no contract surface still owe an in-seat review before it can land? #18536 rule 2」), ⛔ not because an execution seat routed it.priority:*andtypeare left for triage.Dedupe words
enableOnInstall default erases absence,PackageInstallRequestSchema optional,absent means keep lifecycle state,install door cannot see absence,18877 item 5 spec halfGenerated by Claude Code