Filed by the domain:engine lane PM (session session_01F3jdziLbAPGeceVNmSox5L) on the seat's recommendation while working #13998. ⛔ Only the maintainer can answer the first question — it depends on operational knowledge the repository does not contain.
Why it is being asked
#13998 measured that migrateSysNotificationToEvent writes String(row.created_at) into the new timestamp columns. On Postgres/MySQL that is a Date.toString() spelling ("Sun Aug 30 2026 18:19:25 GMT+0800 (China Standard Time)"), not ISO. The migration is one-way, so whatever landed is what a deployment carries afterwards.
The code half is fixed (PR #14024, Part of #13998). This card is the data half: if any deployment already ran it against a live dialect, rows exist today carrying a skewed, de-precisioned, process-zone-baked timestamp — and repairing existing data is a maintainer floor, not something a dev may write.
The repository cannot answer it — measured, four ways
| Reading | Result |
|---|
| Callers in this repo | none — no CLI command under packages/cli/src/commands/migrate/, no boot hook; only the export in migrations/index.ts, its tests, seven changelogs and the docs |
| The documented operator path | docs/handoff/adr-0030-notification-convergence.md, under a heading that reads "Data migration (not auto-run)"; step 2 is Run migrateSysNotificationToEvent({ driver, data }) |
sys_migration ledger | ⚠️not registered.DATA_MIGRATION_FLAG_OBJECT's well-known ids in packages/spec/src/system/migration.zod.ts are only adr-0104-file-references and adr-0104-value-shapes ⇒ no applied-migrations row, no verified_at, no last_run_at, on any deployment |
| Published? | yes — ships from @objectstack/metadata/migrations; the ADR-0030 change landed |
⇒ The platform has a migration ledger and this migration is simply not in it. That absence is why the question is unanswerable at all, and it is arguably the more durable defect.
⭐ What narrows the population — and it may be zero
#14023 measures that every migration in packages/metadata/src/migrations/ calls driver.raw(...), and no driver in this repo defines raw: SqlDriver, MemoryDriver, MongoDriver and the Turso transport all expose execute(); IDataDriver declares execute?() and no raw. Verified with a firing positive control — the raw sweep returns only an unrelated HTTP harness, while the identical execute sweep returns all four drivers.
⇒ The cut-over step the handoff doc hands operators verbatim returns {status:'error', migrated:0} and does nothing. The only remaining way to have run it is with a hand-built knex-like handle — which the repository can see even less than the rest.
The question, and three shapes
- A — ask and act on the answer. Confirm from operational knowledge whether the ADR-0030 cut-over was ever run on a live PG/MySQL deployment; write a backfill only if yes.
- B — write a defensive idempotent repair now that rewrites any non-ISO
created_at/at on sys_inbox_message and sys_notification_receipt, on the theory that it no-ops where nothing ran. - C — register this migration in the
sys_migration ledger so the question becomes answerable for every future deployment, and treat the historical question as permanently unanswerable.
Recommendation: A, with C as a follow-up card.⚠️ ⛔ Not B, and the reason is not cost: a speculative one-way data-mutating pass over production timestamp columns, authored against a population nobody has shown exists, inverts the risk this card is about. #14023 makes the documented path a no-op on every driver this repo ships, so B would be writing a production data mutation for a hypothesis. ⇒ That is precisely the shape that should require a human decision rather than an agent's inference.
C is the contract-first answer to why this was unanswerable, and it costs nothing to decide separately.
⛔ Nothing further is dispatched against the data half until this is answered. The code half is unaffected and lands on its own.
Refs: #13998 (the card) · PR #14024 (the code half) · #14023 (driver.raw is defined by no driver) · #13973 (the census that found the class) · #13382 (the OCC seam, same class).
Filed by the
domain:enginelane PM (sessionsession_01F3jdziLbAPGeceVNmSox5L) on the seat's recommendation while working #13998. ⛔ Only the maintainer can answer the first question — it depends on operational knowledge the repository does not contain.Why it is being asked
#13998 measured that
migrateSysNotificationToEventwritesString(row.created_at)into the new timestamp columns. On Postgres/MySQL that is aDate.toString()spelling ("Sun Aug 30 2026 18:19:25 GMT+0800 (China Standard Time)"), not ISO. The migration is one-way, so whatever landed is what a deployment carries afterwards.The code half is fixed (PR #14024,
Part of #13998). This card is the data half: if any deployment already ran it against a live dialect, rows exist today carrying a skewed, de-precisioned, process-zone-baked timestamp — and repairing existing data is a maintainer floor, not something a dev may write.The repository cannot answer it — measured, four ways
packages/cli/src/commands/migrate/, no boot hook; only the export inmigrations/index.ts, its tests, seven changelogs and the docsdocs/handoff/adr-0030-notification-convergence.md, under a heading that reads "Data migration (not auto-run)"; step 2 isRun migrateSysNotificationToEvent({ driver, data })sys_migrationledgerDATA_MIGRATION_FLAG_OBJECT's well-known ids inpackages/spec/src/system/migration.zod.tsare onlyadr-0104-file-referencesandadr-0104-value-shapes⇒ no applied-migrations row, noverified_at, nolast_run_at, on any deployment@objectstack/metadata/migrations; the ADR-0030 change landed⇒ The platform has a migration ledger and this migration is simply not in it. That absence is why the question is unanswerable at all, and it is arguably the more durable defect.
⭐ What narrows the population — and it may be zero
#14023 measures that every migration in
packages/metadata/src/migrations/callsdriver.raw(...), and no driver in this repo definesraw:SqlDriver,MemoryDriver,MongoDriverand the Turso transport all exposeexecute();IDataDriverdeclaresexecute?()and noraw. Verified with a firing positive control — therawsweep returns only an unrelated HTTP harness, while the identicalexecutesweep returns all four drivers.⇒ The cut-over step the handoff doc hands operators verbatim returns
{status:'error', migrated:0}and does nothing. The only remaining way to have run it is with a hand-built knex-like handle — which the repository can see even less than the rest.The question, and three shapes
created_at/atonsys_inbox_messageandsys_notification_receipt, on the theory that it no-ops where nothing ran.sys_migrationledger so the question becomes answerable for every future deployment, and treat the historical question as permanently unanswerable.Recommendation: A, with C as a follow-up card.⚠️ ⛔ Not B, and the reason is not cost: a speculative one-way data-mutating pass over production timestamp columns, authored against a population nobody has shown exists, inverts the risk this card is about. #14023 makes the documented path a no-op on every driver this repo ships, so B would be writing a production data mutation for a hypothesis. ⇒ That is precisely the shape that should require a human decision rather than an agent's inference.
C is the contract-first answer to why this was unanswerable, and it costs nothing to decide separately.
⛔ Nothing further is dispatched against the data half until this is answered. The code half is unaffected and lands on its own.
Refs: #13998 (the card) · PR #14024 (the code half) · #14023 (
driver.rawis defined by no driver) · #13973 (the census that found the class) · #13382 (the OCC seam, same class).