The pattern test coerces an absent value to the string 'undefined', which
matches, so the typeof halves are the only refusal for a missing value. Adds
a case for each, and M15-M17 in BUILD.md; keeps the config-console-key check
name on purpose. Not run yet.
stack-release config_checks refuses a target whose loader refuses a key the
config holds; it looked only for console, so a federation-only config passed
to a pre-203 copy. The probe now reads console or federation. The fixture's
B loader replace follows the new rejectUnknownKeys list, which the federation
key changed.
Adds a test for each federation refusal in test-config.sh (six uncovered,
plus trailing slash and duplicate peer id), and one stack-release test for
the target-loader refusal. Mutants M7-M14 in BUILD.md. Not run yet.
The loader validates an optional federation block: self and peers[].id
labels, peers[].origin as an https origin with no path, query or userinfo,
peers[].label non-empty and at most 80 characters, peer ids unique and
distinct from self. Unknown sub-keys refuse. Labels and URLs only; the key
grants no trust. Absent key: config loads with no federation output.
Tests in scripts/test-config.sh cover each refusal and the no-key case.
Notes and mutants M1-M6 in agents/quackerjack/work/queue-203/BUILD.md.
Not run yet: the gate runs on CLEAR.
thread_shares (row 193 design 5.2) with its index and append-only triggers; the
3l-3m step only adds them. thread.share, thread.unshare and thread.shares for a
thread's owner; #readable holds design 5.4's four rules, #threadRead serves
thread.messages only, and thread.list {shared:true} adds owner. replies and
trail show shared user threads to the owner and readers. COUNTED, LISTED,
timedTables and the clock list take the table. Tests: thread-shares.test.mjs
and the 3l→3m pins. Packet agents/launchpad/work/queue-206/BUILD.md. Not run
yet: node waits for Sage's CLEAR (sage-2789).
sessions and session_defaults are new and append-only. thread_claims, message_threads and
thread_opens are rebuilt in place with a session column, and thread_claims gains a generation
counted from claim rows. Each existing thread gets a default session with the thread's id; new
threads get a random one. Holder, per-run, release and delivery triggers key on the session,
and two id triggers keep session and thread ids apart (Duckworth checks 1-7, duckworth-0106).
The broker binds a thread run to a session (the default when the record names none), and
thread.open, thread.send and thread.messages take an optional session. New verbs session.list
and session.create (idempotent on op). Tests: sessions.test.mjs for T1 bus half, T2 and T7 with
a pinned 3k fixture; count, version and verb pins; hand-made test threads write the legacy
default session.
Built on row 236 (8f51900b5), which takes 3j-3k, so this step is 3k-3l by landing order
(row264 Q2, sage-2412). The op values are legacy-3l and migration-3l. gitea-migration.test reads
its store after both steps. Not run yet: every run waits for Sage's CLEAR.
Gitea grant executor (steps 0-8, settle, restart, revoke and fire, sweep,
status), the grants.* verbs and mosaic grants, the 3j-3k migration with
gitea_users and gitea_grants, and the section 9 tests with a fake Gitea and
a scratch gitea/gitea:1.27.1 X-G3 test. Build notes in
agents/megavolt/work/queue-236/BUILD.md.
Unify halt and fire cancellation by verified run identity with durable first-owner receipts and shared deadlines. Integrate the instance control with the current host, library and tester-socket paths. Focused nine-file evidence is 62 passing tests; broad suites, mutants and review remain pending.
B1: row 200's rollback to a Console without --sso kept the pointer's --send,
which F39 b forbids once --send can sit beside --sso. send_for_target drops it
whenever the run drops the pointer's --sso, and the plan and steps say so.
N1: an ok line with no activation refuses sso-off-flags instead of being
skipped.
activate --send turns send on only when the run's Console flags hold
--sso, given or carried (F39 b), and puts it after --sso and before the
tester flags. activate --no-sso turns --sso off with the Console flags
from the set of the activation that turned it on, drops --send (F39 a)
and keeps the pointer's tester. Both are usage errors under --units
external. T58 covers the brief's T1-T4; the external test covers T5.
Design and the set-walk choice: agents/darkwing/work/queue-273/BUILD.md.
Candidate quackerjack/fed-r5-r5 a74cd9f87 on c3d88840c, manifest of 6
paths (CANDIDATE.sha256). Duckworth APPROVE round 5, rev 3026 (#1700
c30184). C1 gate on Sage's slot by Quackerjack on the merge of a74cd9f87
onto a849d89b7 (3b86180b9), finished 01:56:24Z: cli suite 771/771, skip 0
(run 2; run 1 red on a too-long gate TMPDIR, harness only), every
scripts/test-*.sh rc 0 with Docker up, mutants M1-M13 killed by their
named tests. Landed onto 9c9732983 with the candidate's blobs and modes
for the 6 paths; c3d88840c..9c9732983 touches none of them.
Candidate morgana/row244 94ab4c2fe on a36523713, manifest 6e7e3e3c (8 paths).
Webby APPROVE r1, rev 3008 (webby-0084, REVIEW-r1.md at webby/reviews
ebb3d83bf; request #1757 c30154). Gate on Sage's slot at 81e037d20
(sage-2147): the five gate-lane files whole 51/0, test-task.sh 91/0,
test-release.sh 14/0, mutants M-G, M-L, M-A, M-T and M-F killed.
94ab4c2fe adds only BUILD.md results. Webby's drift check: a36523713..d37210288
changes none of the 8 paths and no path either suite reads; merge-tree clean.
Rulings A and C (sage-1914, sage-2061). The dropped real-model coverage goes
to DEFERRED in the follow-up doc commit.
Lands Sage's sage/row66-r5 at 7ffb640ac, round 4 (r4d 82c830779) rebased onto a36523713 after row 197,
approved by Dewey (comment 30085) and Filbert (30086) on manifest 6cfd812da4ca (23 paths, rev 2905). The
rebase had no conflict. The range-diff is = except one pi-session.test.mjs hunk header. Patch-id without
agents/ is d67275ac4251 on 7b9737953, a36523713 and this commit's parent. Gate (mos-1186): unslop,
harness, cli and every scripts/test-*.sh green on r4d, harness 149/0 again on r5. Landing sessions on a
scratch root from the candidate tree: Claude Code r4ab24630d323 showed the note, the injected notice and
counts checked 2, flagged 1, notices 1. Pi r1dd94171bede is launch only until #383 (Vikunja #454).
Gosalyn's 6d43618a3 (gosalyn/c2-20b), Webby round 1 APPROVE webby-0076 (rev 2852), manifest 6d3f279f692b,
4 paths. Webby's row 221 notes N1, N2 and N4. Slot runs sage-1969 (red base, the builder's comma
regression) and sage-1971 (14/0, M1-M4 killed). M1/M2 sit at :248 in this commit, not :237 as
BUILD.md says (gosalyn-0111). JSON.stringify leaves DEL, C1, U+2028/9 and bidi raw, a known limit
by sage-1964.
Sage gate on 5c6f73ed7 plus these 4 paths (12:10-12:21Z): the test file alone 14/0, cli 680/0,
every scripts/test-*.sh 429/0. f8276c5eb adds only docs/SESSIONS and docs/case2 sheets no test reads.
Lands Gosalyn's round 1 candidate, 8f403d0d4 on gosalyn/c2-20-r2 (48f044ccc plus its BUILD.md, on
62ddc96cc), approved by Webby (webby-0073, REVIEW-r1 a0a00630e, rev 2808). 22 paths, manifest
20f05e3a6ad5. No 221 path changed between 62ddc96cc and this commit's parent. Patch-id dfbb754c6fb4, the
same as 62ddc96cc..8f403d0d4. The two state.db fixtures and auth.json are short text stand-ins.
Webby's N1, N2 and N4 go to the C2-20b follow-up row before Jason's import run. N3 (mutants.py:48
anchor) is in DEFERRED.md. The script itself runs only under Jason's go (design 10 step 6, 9.1).
Evidence: Gosalyn's singles on my slot, 419/0 on 48f044ccc (gosalyn-0104).
Gate on my slot, 11:07:50-11:19Z on b1fece6f0 plus the 22 paths: case2-persona-import.test.mjs alone
11/0, harness 140/0 (bundle.mjs changed), cli 677/0, and every scripts/test-*.sh green (auth 15,
conductor 17, config 31, discord 78, extension-package 18, foundation 44, land-work 86, queue 156 and 28,
release 14, task 98, all 0 failed). Lands before row 197 r7: no shared paths, and r7's proof candidates
both carry it (sage-1954).
Lands Darkwing's manifest round 1 candidate, 37171f65b (base 4ced5e63d), approved by Duckworth
(duckworth-0088, REVIEW-m1 6afcee2a5, #1693 comment 29988, rev 2758). Five paths:
scripts/case1-accept.sh (100755), packages/cli/tests/case1-accept.test.mjs, docs/TOOLS.md,
agents/darkwing/work/queue-195/DESIGN.md (r7, approved duckworth-0087) and BUILD.md. No 195 path changed
between 4ced5e63d and this commit's parent. Patch-id 656e43ff25de, the same as 4ced5e63d..37171f65b.
Evidence: Darkwing's slot runs: single 22/22 (sage-1829), 12/12 mutants killed and gate cli 662/0 plus
selftests 429/0 on ddf7dce6c (sage-1865, sage-1870).
Gate on my slot (sage-r195land, base 38b83ea5c plus 195's 5 and 213's 39 paths, 09:45:52-09:57:52Z): business 200/0, bus 542/0, harness 140/0, cli 663/0; scripts auth 15/0, conductor 17/0, config 31/0, discord 78/0, extension-package 18/0, foundation 44/0, land-work 86/0, queue 156/0 plus 28/0, release 14/0, task 98/0; the models mutant L1 killed (launch-settings 15/1), restore matched.
Between the gate base 38b83ea5c and 4364dddb0 only docs/plans/2026-10-20_host-credentials.md changed (01e1fe512, 4364dddb0), which no test reads.
Duckworth's notes N1-N3 go to docs/plans/DEFERRED.md in the next commit.
Lands Gosalyn's round 3 candidate, a2c72ac95 (commit candidate, base c771a36f4), approved by Webby
(29979, rev 2731). 21 paths: the new packages/library (src, tests, fixtures, package.json, README),
scripts/stack-copy.sh (packages/library/src in COPY_PATHS), packages/cli/tests/stack-copy.test.mjs,
scripts/mosaic and agents/gosalyn/work/queue-212/BUILD.md. The 21-path patch-id, ea84d3e35470, equals
the candidate's.
Evidence: Gosalyn's slot run on round 3: 20 files 451 pass, 0 fail; test-queue 28/0 (gosalyn-0086).
Landing gate (Sage, 08:51-09:03Z, base 4ced5e63d) on a tree that also held row 197's 32 paths:
GATE_PASSED, node tests 981 pass 0 fail (library 39, cli 681, control-board 38, harness 16, webui 51, plus
the scripts/test-*.sh node suites), selftests 429 passed 0 failed. Row 212 lands alone (mos-1162); row
197 waits for an r6 after its lane proof stopped. No 212 path changed between c771a36f4 and this commit's
parent.
`queue review verify-commit 212` -> ok row 212 round 3: a2c72ac95 matches the commit candidate (21 paths).
Lands Launchpad's round 3 candidate, manifest 1bf831f3 (4c90099ea on launchpad/row238-r4, base
c515273db), approved by Webby (29956, rev 2717). Three paths: scripts/stack-release.sh, packages/cli/tests/
stack-release.test.mjs and docs/TOOLS.md. It is row 200's follow-up N1. Round 3 only merges docs/TOOLS.md
onto the row 240 landing: the landed blob plus the one hunk at :599. The 3-path patch-id, b9d0bb0ee3c7,
is the same in all three rounds, and stack-release.sh stays 100755.
Evidence: Launchpad's slot run on round 2, 2afbfbb01: stack-release.test.mjs 196/0 (sage-1794). Sage's
combined gate with row 228 on c515273db plus both rows' paths (07:11:30Z to 07:23:34Z): packages/seat 165 pass, 0 fail; packages/cli 640 pass, 0 fail; all ten scripts/test-*.sh 585 pass, 0 fail (out/, run.log). Row 238's mutant, the sso-config first step dropping --instance NAME, was killed (2 fails), survived: none, and stack-release.sh was restored to its blob.
`queue review verify-commit 238` matches the manifest on all 3 paths. Round 1's N1 stays on the #1744
carry list.
Lands Darkwing's round 1 candidate, manifest 573ece95a4a9 (ad1b23ab2 on darkwing/row240-r3, base
c771a36f4), approved by Filbert (29945, rev 2696). Nine paths: scripts/case1-hostcheck.sh (new),
scripts/case1-pins.sh (new), packages/cli/systemd/mosaic-hostcheck.service (new),
packages/cli/tests/case1-hostcheck.test.mjs (new), scripts/stack-upgrade.sh,
packages/cli/tests/stack-upgrade.test.mjs, docs/guides/host-run-sheet.md, docs/TOOLS.md and
agents/darkwing/work/queue-240/BUILD.md. File modes match the candidate. case1-hostcheck.sh and
stack-upgrade.sh are 100755. case1-pins.sh is 100644, because the host check sources it.
Evidence: Darkwing's first slot run failed one test, the N1 test's after hook (ENOENT once the
fixture had removed its root). The fix b45852daa is test-only. The second run on b45852daa was green:
singles 37/0, mutants M1-M8 8 of 8 killed, cli 639/0, scripts 585/0. Since c771a36f4 no commit
touches the nine paths. Sage's gate on 060a17b54 plus the nine paths (06:45:05Z to 06:56:21Z):
packages/cli 639 pass, 0 fail, and all ten scripts/test-*.sh 585 pass, 0 fail (out/, run.log). `queue review verify-commit 240` matches the manifest on all 9 paths.
Filbert's notes: N1 (the boot unit orders only mosaic-bus@mosaic-stack) and N2 (the run sheet writes
the apt hook before the first --check) are in docs/plans/DEFERRED.md. N3, repeated DRIFT lines after
bwrap is closed, is by design and is noted with N2.
Lands Gizmoduck's round 3 candidate, manifest 4782c8b5ecb7 (c783d5e39 on gizmoduck/row234-r3, one commit
on 57f59e0c3), approved by Duckworth: content on round 1 (861182fa6, 29919), the round 2 rebase check
(29928, rev 2670) and round 3 (REVIEW-r3.md 7c8c38b69, posted 29935, rev 2683). 41 paths.
Round 3 fixes the two failures in Sage's combined gate with row 233: bus schema.test.mjs now counts 18
tables and 71 triggers for the new hosts and host_grants tables, and stack-release.test.mjs T46 puts both
tables in the untimed restore-guard list, since every write shares a transaction with a grant.* event.
Round 3 rebuilt on the row 233 landing as one commit. 38 of the 39 round 2 blobs are unchanged and BUILD.md
is append-only. Gizmoduck's singles on round 3: stack-release 196/0, schema 1/0.
Evidence: Gizmoduck's slot runs, 25 of 25 mutants killed (A22b and A23 included, A9 structural), singles on
r2 387/0 over 47 files. Sage's landing gate on the c783d5e39 tree: 06:02:12Z to 06:15:26Z, every package except queue 2890 pass, 0 fail (15 packages; bus 539, cli 622), and all ten scripts/test-*.sh 585 pass, 0 fail.
This is code only. The live stack runs from a pinned copy at schema 3i. The 3i to 3j migration runs in the
10-24 window (mos-1145), agent-run under Q173, with a backup first. Duckworth's N10 (hosts.mjs
Object.hasOwn) and N12 (doc-only) go to row 235's carry list.
Lands Darkwing's round 1 candidate, manifest 464ce9eb177a (1a1dc80ca on darkwing's branch), approved by
Filbert (29922, rev 2659). Seven paths: scripts/stack-upgrade.sh (new), scripts/stack-release.sh,
docs/guides/host-run-sheet.md (new), docs/TOOLS.md, packages/cli/tests/stack-upgrade.test.mjs (new),
packages/cli/tests/stack-release.test.mjs and agents/darkwing/work/queue-233/BUILD.md.
Evidence: Darkwing's gate on d99166ade (base e65a3ab4d) 1201/0, mutants m1-m8 8 of 8 killed. The N1 fix
08db6ac3f (flock sleep leak in stack-upgrade.test.mjs) reran c0 20/0 with m1 killed (sage-1708); BUILD.md
doesn't record that rerun. Since e65a3ab4d no commit touches the seven paths. Rows 241 and 242 changed bus
and cli files. Only cli tests read the seven paths, so Sage ran the cli suite on 5465fd52d plus the seven
paths: 617 pass, 0 fail (out233/pkg-cli.log, 05:26:33Z to 05:35:00Z). A combined gate with row 234 failed bus schema.test.mjs:522 on 234's new tables, and
Gizmoduck found a second 234 failure (stack-release.test.mjs T46), so Sage stopped it. Row 234 round 3 fixes
both and rebuilds on this landing, since its T46 lines go in a file this commit changes.
Filbert's N1, N4 and N5 go to row 240 (brief b00d7d94d). N2 and N3 are in DEFERRED.md.
Blobs of Darkwing's round 2 da2da2f6c on origin/darkwing/row232-r2 (base
8fede5fc6), the 9 manifest paths, manifest 7af3c943. Round 2 is a
rebase-only round over row 230 (c8bc5cf4c): packages/cli/README.md is a
clean 3-way merge with 232's hunk unchanged, and the other 8 paths keep the
round 1 hashes (manifest 7a3b4ea1). Filbert approve round 1 (comment 29829)
and round 2 (comment 29865).
Under --units user every command line and every byte of output is the
base's. Under external the script reads root's system units, starts and
stops none, stages a copy behind AWAITING-UNITS, and confirm gates root's
start on the bus unit's ActiveEnterTimestamp and launches list.
Mutants 17 of 17 killed (BUILD.md). Darkwing's landing gate on da2da2f6c,
03:07:15Z to 03:17:48Z: packages/cli 593 pass 0 fail, 10 scripts/test-*.sh
suites 429 pass 0 fail (plus 156 node tests in the queue suite). Refactor
moved to 0444acd08 since, three commits touching no manifest path. No live
change: the stack runs from copy 4429a104f, and --units user stays the
default.
Squash of Gosalyn's 96768766c on gosalyn/c2-13a-r2 (base 722136772), the
5 manifest paths, build.patch 56649f9a, manifest fbb0d7e42c67 (Webby
approve round 2, rev 2563). Gosalyn's gate on 96768766c: cli 541 pass
0 fail; scripts/test-*.sh 429 pass 0 fail (summed by script); mutants
10 of 10 killed. packages/cli/README.md is a 3-way merge with row 199
(cb5fdb064): the hunks don't overlap (line 710 vs 1022). host.test.mjs:855
is the only test that reads that file. On cb5fdb064 plus the patch,
case2-unit.test.mjs 30/0 and host.test.mjs 42/0. Refactor moved to
390ea5261 since, a README fix touching no manifest path. The script
runs on a host only in a separate window with Jason's go.
Squash of Megavolt's 627979a17..86963d281 on origin/megavolt/row199-on-190
(base 00d4cc1f6, row 190's landing), the 37 manifest paths, build.patch
05383075, manifest 35f900fd (Duckworth approve round 1, comment 29779,
rev 2540). Mutant batches 1 and 2 in agents/megavolt/work/queue-199/BUILD.md:
batch 2 26 of 28 killed, M17 and M27 equivalent. docs/TOOLS.md is a 3-way
merge with row 200 (9e1085816): row 199's hunk applies unchanged (patch-id
d88379111e2e) and no test reads the file. Gate on 9e1085816 plus the patch:
15 package suites 2735 pass 0 fail, 10 scripts/test-*.sh suites 429 pass
0 fail (plus 156 node tests in the queue suite). Refactor moved to 76134dfde
since, a brief touching no manifest path. No live change: the stack runs from
copy 4429a104f until the 10-24 re-pin window.
Squash of Launchpad's 30e4bb1a3..9df40878f (round 3 on row 190's landing
00d4cc1f6), the 3 manifest paths: scripts/stack-release.sh,
packages/cli/tests/stack-release.test.mjs, docs/TOOLS.md. Manifest
b3ead55c8b48 (Webby approve round 3, rev 2539, comment 29772).
Patch b2a8f5de18b7. Gate on 9df40878f: 15 package suites 2675 pass
0 fail, 10 scripts/test-*.sh suites 429 pass 0 fail (plus 156 node tests
in the queue suite). Mutants 13 of 14 killed, M12 the ruled equivalent
(sage-1446). Refactor moved to 722136772 since the base, touching none of
the 3 paths. No live change: the stack runs from copy 4429a104f until a
re-pin.
Squash of Darkwing's 176a6bf4b..df65a3d4c on origin/darkwing/row190-r3,
the 25 manifest paths, build-r3.patch efe898c2, manifest cc0ea9e8
(Filbert approve round 3, comment 29713). Design revision 3 in
agents/darkwing/work/queue-190/DESIGN.md. Gate on base d0bbc5c1c with
the patch: 15 package suites 2664 pass 0 fail, 10 scripts/test-*.sh
suites 429 pass 0 fail (plus 156 node tests in the queue suite).
Refactor moved to 99a406ed3 since, a window record touching no manifest
path. Mutants 20 of 20 killed (BUILD.md, round 3). No live change: the
stack runs from copy 4429a104f until a re-pin.
Squash of Launchpad's launchpad/row192 53bb59462..37582e820, manifest round 2
6f8cee147713c53652b58d47c5efd15ed47da1efbef6a126464857269b0b7787 (18 paths).
Sage's gate on 088087ac9: package suites 2758 pass 0 fail, scripts/test-*.sh
429 pass 0 fail; mutants 22 of 23 killed, #23 (second origin check) declared
equivalent by Launchpad (sage-1407). New dependency oauth4webapi 3.8.8: the
canonical checkout and any stack copy need npm ci. No live change; the live
re-pin is a window with Mos (mos-1099 item 3).
Row 178 gave the Console --proof-header NAME --proof-file PATH, but on the pinned stack only
activate --tester-port could write a new Console flag into the pointer. A hand install of the pair
would make every later activate, pm-on and rollback refuse unit-flags-drift. activate now takes
the pair, which replaces any pair in the pointer, and --no-proof, which drops it. The pair goes
after the preview flags and before --send and --tester-port, the order webui-service.sh render
writes and flags_drift compares. With neither flag, a target Console that takes --proof-header
carries the pair; a rollback to one that doesn't drops it and says so, and an activate to one
refuses proof-target unless --no-proof is given.
An activate may name the pointer's copy only when it sets a pair or drops one with --no-proof, so
Part B's B3 and its corrected rollback line B-R1 (activate <live copy> --pm --no-proof) can run;
every other activate of the live copy still refuses already-active. Before anything stops, the run
refuses proof-needs-preview, proof-header-invalid (proofOptions' own rules) and proof-file-invalid:
not absolute, a character the unit can't carry, a symlink, missing, not a regular file, inside the
repository or the copies, mode other than 600 or 400, or another owner. The file gets lstat, stat
and realpath only; the script never opens it and no line it writes holds the value. --plan prints
one proof: line. docs/TOOLS.md "Pinned stack update" lists the flags and describes the pair.
stack-release.test.mjs adds T53, eleven subtests. Each ends with a scan of every written file and
output for the proof file's sentinel and an inotify watch that counts no open or read of it. The
five brief mutants and Darkwing's two are killed. Filbert's M8 (a carried pair skips the checks)
and M9 (the repository case dropped) survive T53; both are follow-ups on P19, with a stale comment
in proof_for_target and the stale row 178 paragraph in docs/TOOLS.md.
This is step A0 of mos-0928 Part B and builds the tool only; nothing turns the proof on for the
live Console. Filbert approved round 1 on #1685.
activate --plan printed the backup line whenever the store file existed. But run_steps rolls back
with store-newer or migration-needs-flag before backup_store, so in those two cases the plan said
both that the run rolls back and that it backs up the store. The plan now prints
"backup: none; the run rolls back before the backup" there. The no-store line comes first, since
with no store file the run skips the whole store block. When S_CHANGED is set the plan keeps the
backup line: a wrong backup line costs nothing, a wrong none line hides the directory a --restore
needs. run_steps, backup_store, refusals, exit codes and flags are unchanged; the diff to
stack-release.sh is inside plan only. docs/TOOLS.md "Pinned stack update" step 3 names the
backup line. These are Filbert's N1 and N2 on row 185 (P19 404).
stack-release.test.mjs updates T26's not-given case and adds T52, six activate cases that plan,
check the one backup line and that nothing was written, then run and check the backup exists
exactly when the plan said so. The three brief mutants are killed, and so is Filbert's M7 (an
S_CHANGED read counted as early, by T29). Filbert's M6 (the early test ahead of the no-store test)
survives, since no test plans a missing store with an out-of-chain pointer schema; that is a
bounded follow-up with the store-unclean reading, on P19 404.
Filbert approved round 1 in #1684 comment 29419, with two non-blocking notes.
stack-release.sh activate now backs up the stopped store under its activation id on every run
with a store file, before any migration. Before this, backup_store ran only when the schemas
differed, so a same-schema activate took no backup and rollback --restore had nothing to name.
store-newer and migration-needs-flag still roll back before the backup, since those runs never
touch the store, and a missing store takes no backup.
BACKUP and TAKEN now mean two things. BACKUP is set by every backup, so the log line names it and
from_log backup finds it. TAKEN is set only right before a migration, so a gate failure after a
same-schema activate puts back the units and the wrapper and leaves the store as the failed run
left it. activate --plan prints a backup line, or "backup: none" with no store file. Rollback and
pm-on are unchanged; a same-schema rollback --restore already works through the row 181 guard.
docs/TOOLS.md "Pinned stack update" steps 5-7 say so.
stack-release.test.mjs updates T5, T11, T26 and T29 and adds T47-T51, with one fixture fault
(start-writes-row-<copy>). The three brief mutants are killed, and Filbert's two more (the backup
ahead of the schema checks, the plan line on a rollback) are killed by T6, T9 and T50.
This is the precondition for mos-0928 Part A, the re-pin from 89af38a83.
Filbert approved round 1 in #1683 comment 29404, with two non-blocking notes.