Skip to content

feat(s03): additive migration for task requirements, rehearsed on a restored store - #60

Merged
im-tyler merged 1 commit into
mainfrom
w5/s03-requirements-migration
Oct 4, 2026
Merged

im-tyler merged 1 commit into
mainfrom
w5/s03-requirements-migration

Conversation

@im-tyler

@im-tyler im-tyler commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

What

S03 storage, finished to the boundary the programme draws: migration
008-ship-task-requirements for src/task-requirements.ts (the drafted
accepted/waived requirements store), rehearsed on a restored copy of the
real store. No write path is enabled — taskRecord() stays a
read-only projection; wiring the store into enqueue/run page remains
future work.

Migration shape

Follows the 006/007 new-table convention exactly (the most recent table
additions, PRs #41/#51, add no migration at all; 006/007 exist to give a
new table the two guards that only a migration entry provides):

  • needed(): write-shaped probe (hasColumns, never a SELECT — Nucleus
    resolves unknown SELECT columns to NULL); false when the table is
    absent, because the store's own CREATE TABLE IF NOT EXISTS creates it
  • run(): rename-aside + CREATE TABLE matching the store DDL
    (enforced by the DDL-parity test); nothing copied (no other shape was
    ever released), nothing dropped — same as 006/007
  • recorded once in ship_migrations under the KV lock; replay is a no-op

Rehearsal (receipt: evals/receipts/2026-10-04-s03-requirements-migration.json)

Driver scripts/check-task-requirements-migration.mjs (the
check-intake-concurrency isolation contract:
SHIP_ISOLATED_CHECK=1 + NUCLEUS_URL), run against a throwaway
engine on a private docker network holding the verified
pre-80c9187-shadow-deploy-2026-10-04 backup
. Live ship-*
containers and /deployments/ship were never touched (verified Up
before and after, no restart); only the proof container/network/dir were
created and removed.

  • Before/after: 45 tables, 37 populated, 4223 rows — identical after
    migrate() except ship_migrations 7 → 8 (the 008 ledger row). The
    applied-ids list is empty because the table does not exist on the real
    store (the store is unwired): ledger-only is the designed path; the
    rename-aside limb is covered by the unit tests against a stale shape.
  • Idempotency: second migrate() applied nothing (12 ms).
  • Real-engine store proof (replacing the unit-test fake): the store
    DDL creates the table; idempotent add; different content under the
    same id refused via the primary key; waiver exactly-once via the
    conditional UPDATE; waived requirements stay listed. Proof rows
    deleted afterwards (final count 0, everything else unchanged).
  • Timings: engine boot 3 s, check wall 951 ms, migrate 34 ms.

Tests

  • 008 probes ship_task_requirements write-shaped, and is a no-op on a fresh install
  • 008 is ledger-only on a current-shape table, and replay is a no-op
  • DDL-parity owner entry for task-requirements.ts
  • Existing task-requirements (14) / task-record (8) tests unchanged and
    green
  • Full suite: 2163 + 222 pass, 0 fail, 1 documented local skip
    (grader-sensitivity port 8901; the one failure seen first was the
    documented environmental web/node_modules absence in a fresh
    worktree, fixed by installing web deps)

Not done (deliberate)

Wiring into enqueue + run page, accept/waive authorisation on approval
grants, feeding waived/active requirements into taskRecord()
acceptance, versioned plans, lost-response / two-tab / steer scenarios
on a real run — all still listed on the programme's S03 rollout row.

Do not merge from this session.

…estored store

Migration 008-ship-task-requirements follows the 006/007 new-table
convention exactly: ledger-recorded on every store, guarded by the
write-shaped shape probe (hasColumns) and the DDL-parity test, and a
rename-aside rebuild limb that is unreachable today because no other
shape of ship_task_requirements was ever released (nothing copied,
nothing dropped, the aside keeps what it held). The store itself stays
deliberately unwired: taskRecord() remains a read-only projection, no
route/worker/UI writes the table, and a write path is future work gated
on the programme's S03 rollout list.

Rehearsed on a restored copy of the production store (verified
pre-80c9187-shadow-deploy-2026-10-04 backup; isolated throwaway engine
on a private docker network; live ship-* containers never touched):

- additive-only: 45 tables / 4223 rows identical before and after,
  the only change being ship_migrations 7 -> 8 (the 008 ledger row)
- replay: a second migrate() applies nothing
- real-engine store proof (not the unit-test fake): DDL creates the
  table, idempotent add, conflict refusal via the primary key,
  exactly-once waiver via the conditional UPDATE, waived rows stay
  listed; own proof rows deleted afterwards

Driver scripts/check-task-requirements-migration.mjs (the
check-intake-concurrency isolation contract: SHIP_ISOLATED_CHECK=1 +
NUCLEUS_URL); receipt
evals/receipts/2026-10-04-s03-requirements-migration.json.

Unit tests: 008 write-shaped probe, no-op on fresh install, rename-aside
on a stale shape, ledger-only on a current shape, replay no-op, plus the
DDL-parity owner entry for task-requirements.ts. Full suite green
(2163 + 222 pass, 1 documented local skip). No upstream defect found;
no Neutron or Nucleus change.
@im-tyler
im-tyler merged commit c764cf0 into main Oct 4, 2026
4 checks passed
@im-tyler
im-tyler deleted the w5/s03-requirements-migration branch October 4, 2026 09:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant