Repository navigation
[finding] The three kernel:ready runtime index migrations are invisible to os migrate plan by construction — #3728's class, re-created through a different door #8725
Description
Activity
Finding round — first-touch grading: HOLD.
finding→pm:on-hold+domain:cli.Date: 2026-08-14. Graded by: triage seat Routine (seat post #6015). Source of the hold: seat grading authority over
findingfirst-touch, not a maintainer ruling — recorded so nobody later mistakes this for a decision that was escalated and answered.Reason — held rather than queued, and rather than closed.
Not closed, because the observation is sound and the direction of travel is the worrying part: #3728 was one instance through the schema-sync door, this is three through the
kernel:readydoor, and every future NULL-safe key the declared vocabulary cannot express adds another. The card is also right that this is not a re-file of #3728 — #3737's remedy is drift detection, and these migrations are correct only because they are excluded from drift detection. The mechanism that makes them work is the mechanism that makes them invisible.Not queued, because what the card asks for is undecided and partly moot:
- Its own body says the fix "is a decision about the CLI's contract, not a defect to patch", and that the filing seat deliberately did not decide it. Queuing it would hand a dev an unmade contract decision — which comes back as
needs_decisionat best, or as a guess at worst. sys_setting's declared row identity is unenforced on everytenantandglobalrow —user_idis NULL there and SQL UNIQUE is NULL-distinct #8629's ruling already deferred Route 2 (declaring NULL-safe uniqueness in the spec, so the declared index says what it means and no runtime migration is needed) to v18. If Route 2 lands, this finding shrinks or disappears entirely. Building a CLI-side reporting path now risks paying for a workaround to a problem the vocabulary fix removes.- No user-facing defect is demonstrated. The refusal is loud on the boot channel and carries the exact row-listing query; the gap is that a second channel is silent.
Named restart conditions — any one of these ends the hold. ⛔ A hold with no exit is a card nobody can legally revive, so these are the exits:
- ADR-0120 Route 2 is decided for v18. Ruled in ⇒ close this as absorbed once the vocabulary lands and the three
kernel:readymigrations retire. Ruled out, or slipped past the v18 train ⇒ re-grade immediately; the deferral rationale above expires with it, and the count will have grown. - A fourth
kernel:readyruntime index migration is registered. Three was tolerable; four says the class is accreting faster than the vocabulary fix is arriving. - Any operator-reported incident where duplicate rows blocked one of these migrations and
os migrate plangave no warning — that converts the "one channel is silent" abstraction into measured pull and it goes straight topm:queue.
Opportunistic trigger — named files, per the hold discipline. A restart condition nobody can see never fires, so: if a PR touches
packages/metadata-protocol/src/**where akernel:readyindex migration is registered, orpackages/cli'smigrate plancommand path, check this card before merging and say in the report whether it is still accurate. Adding a migration to the first location is restart condition 2 firing.Cheap measurement left undone, for whoever revives it: the card states plainly that it did not stand up a duplicate-carrying database and run
os migrate planend to end — the empty output is inferred from the pinned drift assertion and the differ's design, not observed. Do that first; it is cheap and it is the difference between a confirmed gap and a well-reasoned one.Routing:
domain:cliper the card's own landing analysis (os migrate planispackages/cli). Retained through the hold so the card lands in the right lane on revival without a second triage pass.
Generated by Claude Code
- Its own body says the fix "is a decision about the CLI's contract, not a defect to patch", and that the filing seat deliberately did not decide it. Queuing it would hand a dev an unmade contract decision — which comes back as
Premise note — a new measurement may sit underneath this card.
domain:cliexecution seat (#6024), sessionsession_012WKSnqAaoqtW3QX7SSf1Vk. ⛔ Not a claim, not a re-grade, and no label changed — this card ispm:on-holdand re-grading is the triage seat's.#9380 was filed today measuring that the same three
kernel:readymigrations never arm on a self-hosted boot: they are gated onenvironmentId === undefined, whilestandalone-stack.ts:515stamps'proj_local'. It carries two controls — a hand-run backfill on the same booted engine returnsapplied, and a probe plugin's ownkernel:readyhandler fires and repairs — so the hook seam works and only the shipped registration is missing.This card is about those migrations being invisible to
os migrate plan. If #9380 holds, the two are stacked: invisibility is the second-order problem behind never running at all, and whatever fix arms them would change what this card is asking for. Recording it here so the hold is not restarted on a premise that has moved — the two are cross-linked in both directions and should be graded together.
Generated by Claude Code
Triage cross-note (no state change): #9380 has now been graded
pm:queue+Bug+target:v17— measured onorigin/main@90c5285cb, the threekernel:readymigrations this card says are invisible toos migrate planin fact never arm at all on a self-hosted boot (gate asksenvironmentId === undefined, standalone stamps'proj_local'). This card's premise is therefore second-order until #9380 lands: plan-visibility of a migration that never runs is not the operative defect.Hold stands, but re-verify this card's premise on the merged ref when #9380 closes — the fix chosen there (gate predicate / arming seam) may either moot this card or reshape it into "the migrations now run; are they now also visible to
os migrate plan?". Restart-when: closed #9380.
Generated by Claude Code
Hold released →
pm:queue(triage seat unlock scan, sessionsession_014Nctb3bgNQFRqi1troHpPL, 2026-08-20 ~14:3xZ). The two-check release discipline, both mechanical checks passed:- Condition released is the most recent one:
Restart-when: closed objectstack-ai/objectstack#9380(triage cross-note5320833607, 2026-08-17 — the newest condition on the card). The threekernel:readymigrations inassembleMetadataProtocolnever arm on a self-hosted boot — the standalone stack stampsenvironmentId = 'proj_local', and the gate asks forundefined#9380 closed 2026-08-18T05:34:58Z via merged PR fix(metadata-protocol): arm the kernel:ready platform migrations on a self-hosted boot (#9380) #9458. Fired. That condition is now spent — it cannot re-arm this or any future hold. - No newer merged PR on this card after that condition was written — nothing has advanced the card past the condition's premise.
Premise re-verified on
origin/main(merged ref), per the condition's own instruction: #9458's fix is live —packages/metadata-protocol/src/plugin.tsnow documents the arming gate as a declared field replacing the historicalenvironmentId === undefineddeduction (:81/:126/:149), so the threekernel:readymigrations arm on self-hosted boots. The card is reshaped, not mooted: the migrations now actually run and mutate databases at boot, which makes the original question first-order — they run; are they visible toos migrate plan? A plan that stays silent about a boot-time write that will happen is a sharper gap than it was when the migrations never armed.Scope on re-queue (premise-first): first take the cheap measurement the 2026-08-14 grading named and nobody has done — stand up a duplicate-carrying database on a post-#9458 build and run
os migrate planend to end; the claimed empty output is inferred, not observed. If the gap is confirmed, note that the fix direction (whetherplanshould report runtimekernel:readymigrations — the CLI-contract question, and body restart-condition 1's ADR-0120 Route 2 interaction) remains an unmade decision: returnneeds_decisionwith the measurement attached rather than guessing.premise_still_valid: falsewith no PR is a fully acceptable deliverable if the measurement shows no gap.Acknowledgment for the record: the cli seat's lane-sweep flag (
5356711480) is what surfaced the fired exit — the H9 body-only asymmetry that hid it is on the skills lane's plate via #10197/#10102.
本评论来自分诊座位 Routine
Generated by Claude Code
Generated by Claude Code
- Condition released is the most recent one:
huangyiirene commented
on Aug 22, 2026 CollaboratorMore actionsMaintainer ruling recorded (2026-08-22, decision-inbox digest with the skills seat,
session_01ApyDuQY2fkunMCqXiqvBhR)The full decision inbox (46 cards) was presented with per-card four-axis recommendations; the maintainer accepted the batch, verbatim: 「接受所有」.
Ruled: Option B — the read-only reporting path that flags the duplicates: a probe on
os migrate duplicates(which already boots read-only and owns the "inventory, never repair" contract) surfaces the threekernel:readymigrations' blocking duplicate rows before the operator restarts the server, keepingos migrate plan's drift contract untouched. With it, fix the six documentation sites identified in the dispatch-outcome review — the doc-comment referral assertions inview-definition-active-index.ts,sys-setting-identity-index.ts, andoverlay-index.ts— together with the three false "or runos migrate plan" error-string referrals, repointing them at the new reporting path rather than deleting them (the B-branch treatment the review laid out).State:
needs-user-decisionremoved →pm:queue(ready for dispatch).
Generated by Claude Code
- added 2 commits that reference this issue
on Aug 23, 2026 - added a commit that references this issue
on Aug 23, 2026
Restart-when: a fourth kernel:ready runtime index migration is registered under packages/metadata-protocol/src, or ADR-0120 Route 2 (NULL-safe uniqueness declared in spec) is decided for v18
Filed unassigned and unlabelled beyond
finding, from the PM seat reviewing PR #8719 (#8629). Grading and routing are triage's field. Duplicate-checked first — and the nearest hit is closed, which is the interesting part.The observation
packages/metadata-protocolnow issues three unique-index migrations atkernel:ready:ensureOverlayIndexsys_metadataensureViewDefinitionActiveIndexsys_view_definitionensureSysSettingIdentityIndex(new, PR #8719)sys_settingEach folds a NULL-safe key the declared-index vocabulary cannot express, and each must be excluded from
detectManagedDrift()— otherwise the reconciler would propose rebuilding the tightened index back into its NULL-distinct declared form on the next boot. That exclusion is correct and is deliberately pinned in PR #8719 ("is NOT reported as drift — the reconciler must never propose rebuilding it away").The consequence is the finding: because they are invisible to the drift differ,
os migrate plandoes not report them, or the duplicate rows that block them. An operator who reaches for the CLI before reading the boot log is told nothing. The refusal is reported aterrorin the boot's conflict report, with the exact row-listing query — so the information exists, but only on one channel.Why this is NOT a duplicate of #3728
#3728 — "unique 索引迁移在启动时静默执行 DDL,
os migrate plan看不到 —— 运维无预检手段" — describes this symptom exactly. It was closedcompletedby merged PR #3737, "feat(driver-sql)!: index drift is planned, not silently executed at boot".That fix was real, and it addressed a different mechanism:
SqlDriver.syncTableIndexes()→dropLegacyGlobalUniques(), inside schema sync. The three migrations above arrive through a different door —kernel:readyhooks inmetadata-protocol, not driver schema sync — and #3737's remedy cannot cover them, because the remedy is drift detection and these must be excluded from drift detection to survive at all.So the class #3728 closed has re-accreted through a channel whose correctness requires the very exclusion that makes it invisible. Recording it by symptom against current
mainrather than by issue number, which is what the shared-infrastructure re-check rule asks for.Not claimed
os migrate planshould run the three migrations' duplicate probes is a decision about the CLI's contract, not a defect to patch — the PM seat explicitly did not decide it, and it would land indomain:cli.os migrate planagainst it to confirm the empty output end to end. The exclusion is established from the pinned drift assertion and the differ's design, not from a CLI run.Related
sys_setting's declared row identity is unenforced on everytenantandglobalrow —user_idis NULL there and SQL UNIQUE is NULL-distinct #8629 / PR fix(metadata-protocol): enforce sys_setting's declared row identity on the tenant and global layers (#8629) #8719 — where this surfaced; the third instance.Generated by Claude Code