Skip to content

[decision] What may a run of adr-0030-notification-event claim in the sys_migration ledger — which columns, and is it a gate or a receipt? #15710

Description

@claude

Ungraded and unrouteddomain:*, priority and type are triage's. Filed unassigned by the domain:engine execution seat at landing time, because PR #15450 carries Closes #14025 and this question would otherwise lose its only carrier the moment that PR merges.

The landing seat's choice was the one the contract review named: 「either a domain:spec card is filed for it or #14025 stays open」. This is that card. ⚠️ It lands on packages/spec, whose single owner is the domain:spec seat — stated by that seat itself when it pre-authorised #14025's one-file boundary (5542597792): 「If the registration cannot be written without committing to an answer, that is a fork — stop and report, and it comes here as a spec-lane card rather than being resolved inside an engine-lane PR.」 The registration turned out to be writable without committing to an answer, so this is the report, not the fork.

What was reserved

总监批 #21 (5491064532, 2026-09-01T08:20:43Z), item 3, maintainer verbatim — quoted untranslated because rewriting a ruling's words rewrites the ruling:

顺带核对 #14023 的发现对注册语义的影响 —— 一条从未能通过文档路径运行的迁移,注册进台账时其 applied 状态语义要写清

The surface owner scoped the handling in the same breath: the dev registers the id and reports the finding; it ⛔ does not decide the semantics inline, and ⛔ does not add prose to the constant asserting an answer. PR #15450 did exactly that. What remains is the deciding.

⭐ The premise the question was framed on is false on today's main — measured, and it changes the question's shape rather than retiring it

The reservation rests on 「从未能通过文档路径运行」 — a migration that could never have run through the documented operator path. That was true when #14025 was filed. It is not true now:

reading value
packages/metadata/src/migrations/driver-exec.ts first lands at 2a181174a (PR #14084) — binds these migrations to IDataDriver.execute(), with raw() only as a fallback
its commit time 2026-09-01T04:39:31Z
the ruling's comment time 2026-09-01T08:20:43Z
⇒ the gap 3h41m12s — the premise was already false when the ruling was written

SqlDriver, its SqliteWasmDriver subclass and the cloud-side Turso driver all expose execute(), so step 2 of docs/handoff/adr-0030-notification-convergence.md is a working call today, not a guaranteed {status:'error', migrated:0}.

⚠️ What this does and does not overturn. The historical answer is untouched and ⛔ not reopenable here: the ADR-0030 cut-over never ran on a live PG/MySQL deployment (维护者 逐字 「13998 没跑过」), #13998 closed on that basis, and option B — a one-way rewrite of production timestamp columns for a population confirmed empty — stays excluded. What changes is that the newly registered id now describes a migration operators can run, which makes 「what may a run of it claim in the ledger」 a live question rather than a hypothetical one. That strengthens the case for having registered it; it does not answer what was reserved.

(This measurement is the contract review's own escalation 3.ii at CONTRACT_REVIEW_TIER (5548654852), re-measured independently here before filing.)

The vocabulary already exists — the fork is about which columns this migration may claim

DataMigrationFlagSchema (packages/spec/src/system/migration.zod.ts) already separates the three readings:

  • last_run_at — a gated (apply-mode) run completed on this deployment;
  • applied_at — the backfill ran with writes enabled;
  • verified_at — the self-check PASSED (withdrawn again by a later failing run, and separately by the lax-escape column rather than by clearing it).

And attestFreshDatastore already writes the 「verified but nothing applied」 combination explicitly — applied_at: null, details carrying { attested: 'datastore-created-empty' } (pinned at packages/objectql/src/adr0104-attestation-evidence.test.ts:143). ⇒ 「was it applied here?」 is expressible today without inventing anything. Nothing new is needed; what is missing is a decision.

The three open questions, stated as they were measured — ⛔ the seat answers none of them

  1. Which ledger columns may a run of adr-0030-notification-event legitimately claim, and in particular may verified_at ever be set for a migration that has no self-check to pass? The two ADR-0104 ids take their column semantics from an os migrate command that scans, self-checks, and only then records. This migration has no CLI command and no self-check: it returns migrated / already_done / not_applicable / error to its caller and nothing else.
  2. Should anything ever gate on a row under this id, or is it a receipt an operator reads? Nothing writes or reads such a row today. sys-migration.object.ts already documents the receipt shape for the seed-tenancy repair (verified_at: null, blocking: 0 by construction), so both readings have precedent on this surface.
  3. Does a datastore created after the cut-over belong in CREATION_ATTESTED_MIGRATION_IDS? ⚠️ A real decision, not a formality. Such a store has no legacy inbox rows by construction — the same 「true by birth, observably」 argument that array's own docblock makes for its two current members. But adding a third member moves live pins: packages/objectql/src/adr0104-attestation-evidence.test.ts and packages/platform-objects/src/system/migration-flag.test.ts (:137-139, :190, :258 all read the array's length or its members). PR feat(spec): register the ADR-0030 notification cut-over in sys_migration's well-known migration ids #15450 deliberately left the array untouched.

What is ⛔ NOT being claimed

The registration itself is landed and sound — it was reviewed at CONTRACT_REVIEW_TIER and passed. The new constant's docblock states that these questions are open and that its silence is not an answer, which is the one thing that stops the next author from reading the two neighbours' semantics onto this id by pattern-match. So nothing regresses while this is decided, and this card is not urgent on that account; it exists so the reserved question keeps a carrier after #14025 closes.

⛔ This seat does not rank these three against each other, does not pick a shape, and does not grade the card.

Re-check

git show origin/main:packages/spec/src/system/migration.zod.ts | grep -nE 'last_run_at|applied_at|verified_at'
git log --format='%H %aI' -1 origin/main -- packages/metadata/src/migrations/driver-exec.ts   # 2a181174a 2026-09-01T04:39:31+00:00
git grep -n CREATION_ATTESTED_MIGRATION_IDS origin/main -- packages/platform-objects/src packages/objectql/src
git grep -c MIGRATION_ID origin/main -- packages/spec/src/system/migration.zod.ts             # positive control: 6

Dedup: ⚠️ /search/issues answers 403 in this container and an empty search is not a reading — so this was a repo-scoped REST list of open cards plus a local grep. No open card carries the applied-semantics question for sys_migration. Related: #14025 (the parent decision card, closed by PR #15450) · PR #15450 (the registration) · 总监批 #21 5491064532 · surface-owner pre-authorisation 5542597792 · contract review PASS 5548654852 (escalation 3.i is this card, 3.ii is the premise measurement above) · #14023 (driver.raw is defined by no driver) · #14084 (driver-exec.ts, which falsified the premise) · #13998.


Generated by Claude Code

Activity

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

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions