Files
stack/agents/sage/work/window-plan/repin-de352dfaf.md
jason.woltjeandClaude Opus 5.5 f5b5aa509f sage: window plan repin-8e7ea89a1 on row 162's verbs, de352dfaf superseded (mos-0814, mos-0815)
W2 adopt on Mos's go with Jason present, W2b pointer check (copy 52f35e7a0,
schema 3f) before W3, W3 plan line-by-line match against Darkwing's scratch
rerun, and the W2 undo: remove the pointer only. Landing commit 7e7f8e208.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-18 15:39:38 -05:00

19 KiB
Raw Permalink Blame History

Re-pin for rows 148 and 141, with the bus 3f to 3g migration

Superseded 2026-10-18 by repin-8e7ea89a1.md (mos-0814): this window never ran. The rest stays as the record.

Sage, 2026-10-16. One shared window for rows 148 and 141, per mos-0638. Mos runs every step, with Jason present (A4). Sage ran none of the window steps. P1 and P2 are done; the controls and fixture tests Sage ran are listed at the end.

The shape is repin-3190ad762.md, the last window with a migration: a stopped-bus backup, a rehearsal and a live migration. It adds the P4 business check from repin-52f35e7a0.md, a Console check for row 148 and row 141's manifest checks.

  • OLD, live now: 52f35e7a0933e9fa845f59860a5de1709f9e5756. Bus, Console and CLI all on it per stack-copy.sh check at 22:48Z. Bus started 2026-10-16T21:12:54Z (pid 2536547), Console 21:12:58Z (pid 2536917). Bus schema 3f.
  • NEW: de352dfaff539e7df9db7a360bda6921a3410fa3, committed 2026-10-16T22:35:27Z, on origin refactor. Copy, made in P1: /home/jwoltje/.local/share/mosaic-stack/copies/de352dfaff539e7df9db7a360bda6921a3410fa3. Bus schema 3g.
  • Data root: /home/jwoltje/.mosaic-dev. The store is bus/bus.sqlite with its -wal and -shm files: about 1.9 MB plus a 0.7 MB WAL at 22:48Z (ls -l, no file opened).
  • Scripts: P=/mnt/storage/scratch/sage-pin-de352dfaf. The reviewed copies are committed beside this file in repin-de352dfaf/, with SCRIPTS.sha256. P0 checks that P matches them.

What NEW carries over OLD

Two commits in 52f35e7a0..de352dfaf touch COPY_PATHS. The commits after de352dfaf, up to d4c3b2838, are docs and queue only and touch none. Nothing under scripts/, adapters/, package*.json, RELEASE, packages/cli/systemd or packages/seat changed, so bus-service.sh, webui-service.sh, stack-copy.sh and working.mjs are the same as in the 52f35e7a0 window.

  • Row 148 (d4ea7e5a6, #1642): a Console editor for the project's spec and docs. The Console serves GET /api/project-files and GET /api/project-file through the new human-only bus verbs project.files and project.file, and a new static markdown-preview.js. POST /api/project-file calls project.file.save, which commits to the project repo. The Console wires Save only under --send, which the live unit has, and serves it only to the public name with the signed-in header. Loopback and any other Host get 403 read-only before the body is read. /api/config gains edit. No schema change.
  • Row 141 (de352dfaf, #1635): messages addressed by agent name. Schema 3f to 3g: migrations/3f-3g.sql adds nullable messages.to_name and an append-only STRICT instance_names table with four triggers (unique name per business, no update, no delete, no replace). New CLI verbs mosaic name (a human write) and mosaic names (a read). roles/chief-of-staff.json gains instance.name. No bus tool covers it until row 155, so the harness lists it in the chief's manifest untyped and the chief's prompt carries the line "Actions you hold that have no bus tool yet: instance.name. Say so instead of working around it." pm does not hold it.

Nothing names an instance during the window. mosaic name writes an append-only row to the live store, so it is Jason's to do, after the window. mosaic names must print [] with --json.

NEW opens nothing older than 3g. A normal bus start under NEW on the 3f store refuses with schema-mismatch and changes nothing. OLD refuses 3g the same way. Both directions are in the rehearsal.

No refs/heads/root section this time. packages/seat/src/working.mjs is unchanged, so the bus start seeds working refs exactly as it did on OLD. Row 148's packages/project changes add the human file read and writeHumanFile, which commits only on a Save. Keep the 52f35e7a0 window's refs-window.tsv.

Rollback set

P2 captured it at /mnt/storage/scratch/sage-pin-rollback-52f35e7a0/: the mosaic wrapper, [email protected], mosaic-webui.service, both systemctl --user show outputs, DROPINS.txt (no drop-in directories), SOURCES.txt, SHA256SUMS and verify-live.sh. It printed MATCH three times.

The bus backup from step 3 is the second half. It records the rollback set's path and SHA256SUMS.

Scripts

All in $P. None of them reads or prints a row of the store, and none prints file content.

Script Used in What it does
capture-rollback.sh [OUT] P2 Saves the OLD wrapper and units. Read-only on live files. Done.
business-check.mjs COPY_ID P4 Loads the live business file with COPY's loader and roles, as the bus does at start. Prints counts, HOLDS chief instance.name yes|no, HOLDS pm ... and LOAD_OK, or REFUSED.
stopped-check.sh DATA_ROOT 3, R Fails on bus/writer.lock or any process holding a file under bus/. Prints STOPPED_OK. Never opens the store, never removes a lock.
backup.sh DATA_ROOT OUT ROLLBACK_SET 3 As in the 3190ad762 window. BACKUP_OK.
rehearse.sh BACKUP NEW_COPY OLD_COPY 4 On two 0700 copies of the backup: (a) NEW refuses the 3f copy at a normal open, (b) migrate-schema.mjs exits 0 ending bus schema 3g ready, with its run time, (c) NEW opens it at 3g, (d) a second migrate is a validated no-op, (e) OLD refuses the 3g copy, (f) the backup is unchanged. REHEARSAL_PASS or REHEARSAL_FAIL.
verify-open.mjs COPY DATA_ROOT 4 OPEN_OK <version> or REFUSED <code>. Refuses ~/.mosaic-dev.
restore.sh DATA_ROOT BACKUP R As in the 3190ad762 window. Renames bus to bus.post-attempt-<UTC>, deletes nothing. RESTORE_OK.
verify-new.sh 8 Live wrapper and units equal the P2 set with the copy id swapped, and equal the NEW renders. 5 × MATCH.
check-launch.mjs INSTANCE SINCE COPY_ID 8, 9 Sources under COPY, prompt digest, row 140 text, read_spec, no land_work, send_message; new for row 141: untyped is ["instance.name"] for chief and [] for pm, and the prompt carries the untyped line for chief and lacks it for pm. pm also needs request_staff. write_record is INFO. CHECK3_PASS or CHECK3_FAIL.
console-check.sh COPY_ID [PORT] 8 Row 148 on loopback: markdown-preview.js served and equal to the copy's file; /api/config gives send=false edit=false; project.files for project stack lists spec/north-star.md; project.file reads it with a 40-hex commit (prints the length only); a loopback POST gets 403 read-only, sent=false. CONSOLE_PASS or CONSOLE_FAIL.
verify-live.sh R In the rollback set. MATCH per file.

The two project.* reads go through the human-only bus verbs, so a PASS also shows the Console's human proof is accepted. That needs the bus, Console and wrapper on one tree, which step 8 checks first.

Steps

REPO=/mnt/storage/src/mosaic-stack
P=/mnt/storage/scratch/sage-pin-de352dfaf
D=/home/jwoltje/.mosaic-dev
OLD=52f35e7a0933e9fa845f59860a5de1709f9e5756
NEW=de352dfaff539e7df9db7a360bda6921a3410fa3
COPIES=/home/jwoltje/.local/share/mosaic-stack/copies
C=$COPIES/$NEW; OC=$COPIES/$OLD
B=$P/backup            # the bus backup; holds live bus data, owner-only, nobody but Mos opens it
RS=/mnt/storage/scratch/sage-pin-rollback-52f35e7a0

Before the window (P0 to P4, nothing stopped)

# Who Step
P0 Mos cd $P && sha256sum -c $REPO/agents/sage/work/window-plan/repin-de352dfaf/SCRIPTS.sha256, 11 × OK.
P1 Sage, done; Mos checks The copy exists. Copy check below, rc 0 with no output. Then test -f $C/packages/bus/src/migrations/3f-3g.sql && test -f $C/packages/webui/src/public/markdown-preview.js && echo NEW_COPY_HAS_141_148. grep -l git.merge.protected $C/roles/*.json prints nothing.
P2 Sage, done; Mos checks bash $RS/verify-live.sh, MATCH × 3, rc 0.
P3 Mos node -e 'console.log(JSON.parse(require("fs").readFileSync(process.env.HOME+"/.config/mosaic-dev/config.json","utf8")).dataRoot)' prints a path that resolves to $D. test -e $B && echo B_EXISTS prints nothing.
P4 Mos node $P/business-check.mjs $NEW: counts, HOLDS chief instance.name yes, HOLDS pm instance.name no, LOAD_OK. REFUSED, or other HOLDS answers, stops the window before it starts.

The P1 copy check:

paths=$(sed -n '/^COPY_PATHS=(/,/^)/p' $REPO/scripts/stack-copy.sh | grep -v '^ *#' | sed '1d;$d')
T=$(mktemp -d) && git -C $REPO archive $NEW -- $paths | tar -xf - -C $T && diff -rq --exclude=node_modules $T $C; echo "rc=$?"

The window

# Who Step
1 Jason, then Mos PM-idle check. Jason, in his own shell: mosaic agents --business mosaic-stack; chief and pm must not be working. Mos: mosaic launches list. Nobody talks to chief or pm until step 8 passes. Write down the UTC start stamp: date -u +%FT%TZ.
2 Mos Stop the Console, then the bus: systemctl --user stop mosaic-webui.service, then systemctl --user stop [email protected]. systemctl --user is-active mosaic-webui.service [email protected] prints inactive twice.
3 Mos bash $P/stopped-check.sh $D, STOPPED_OK. Then bash $P/backup.sh $D $B $RS, BACKUP_OK. Record the sha256 lines it prints.
4 Mos bash $P/rehearse.sh $B $C $OC. REHEARSAL_PASS, six PASS lines. Record the migrate run time. Any FAIL ends the window here: start the bus and the Console again on OLD, nothing else changed.
5 Mos Live migration, bus still stopped: node $C/packages/bus/src/migrate-schema.mjs $D; echo "rc=$?". rc 0, last line bus schema 3g ready. Then (cd $B/bus && sha256sum -c --quiet ../SHA256SUMS) && echo BACKUP_INTACT.
6 Mos From the canonical checkout: $REPO/scripts/bus-service.sh install --copy $NEW --pm --claude /home/jwoltje/.local/bin/claude, then $REPO/scripts/webui-service.sh install --copy $NEW --port 7332 --public-host mosaic.woltje.com --auth-header X-authentik-username --allow-user jason.woltje --send.
7 Mos $REPO/scripts/stack-copy.sh wrapper $NEW.
8 Mos Start the bus, then the Console. Run the checks below in order.
9 Mos, then Jason Smoke from tmux, then Jason's browser check, below.

Between steps 5 and 7 the live store is 3g while the wrapper and units still name OLD. Run no mosaic command in that gap. A bus started on OLD by mistake would refuse 3g and change nothing: a stop condition, not damage.

The migration takes the store's exclusive writer lock, checks the 3f source digest, schema version, WAL, quick_check, foreign keys and timestamps, then runs the 3f-3g step in one transaction. A refusal prints migration refused: <code> and exits 1. The transaction rolls back, so the store is still 3f; restore anyway (rollback A), because the backup is the known state.

Step 8 checks

$REPO/scripts/stack-copy.sh check                     # bus, Console, CLI on NEW
bash $P/verify-new.sh                                 # 5 x MATCH, rc 0
systemctl --user status [email protected] mosaic-webui.service
curl -fsS http://127.0.0.1:7332/healthz
mosaic launches list                                  # one running chief, launched by the bus start
node $P/check-launch.mjs chief <step 1 stamp> $NEW    # CHECK3_PASS
mosaic names --business mosaic-stack --json           # []
bash $P/console-check.sh $NEW                         # CONSOLE_PASS

The bus start is itself the check that the live store is 3g: a normal open under NEW verifies digest, version, WAL, quick_check, foreign keys and timestamps, and the bus does not start on any mismatch.

check-launch.mjs chief must show untyped ["instance.name"] and the untyped prompt line. P4 predicted it from the live business file. If the chief's untyped list is [], NEW's roles did not reach the launch; that is CHECK3_FAIL.

mosaic names printing anything but [] means a name row exists that nobody should have written yet. Stop and report; do not roll back for it alone without Mos's call.

Step 9, smoke and the browser check

mosaic talk chief "[mos test] re-pin de352dfaf: reply with one line, no staffing request" --tester
mosaic talk pm "[mos test] re-pin de352dfaf: reply with one line, no staffing request" --tester
node $P/check-launch.mjs pm <step 1 stamp> $NEW       # CHECK3_PASS, untyped [], request_staff

Every talk starts with [mos test] and runs with --tester. If pm is stopped, talk pm prints the row 146 waiting line and chief launches pm. Talk by name is not smoked: no instance has a name, and naming one is Jason's live write.

Jason's browser check of row 148, at https://mosaic.woltje.com/#/docs signed in: the file list shows, spec/north-star.md opens with its preview, and the Save button is present. Nobody presses Save in the window. A save commits to Jason's project repo (~/.mosaic-dev/projects/mosaic-stack/stack, HEAD ae0b17c9 at plan time), which is a smoke that leaves a commit in Jason's history and waits for Jason outside the window.

Stop conditions

Before step 5, any one ends the window with nothing changed. Start the bus and the Console again on OLD and report:

  • P0 to P4 fail, or stopped-check.sh fails in step 3. A writer.lock with the bus stopped goes to the stale-lock procedure; nobody deletes it.
  • backup.sh prints anything but BACKUP_OK.
  • rehearse.sh prints REHEARSAL_FAIL.

From step 5 on, any one means rollback:

  • Step 5 exits non-zero or does not end bus schema 3g ready: rollback A.
  • The bus fails to start on NEW, or the newest launch-log line for this start is "event":"refused", or journalctl --user -u mosaic-bus@mosaic-stack --since "<UTC stamp> UTC" shows PM launch refused or a store code such as schema-mismatch or integrity-failed: rollback B. journalctl reads --since in local time unless the stamp says UTC.
  • /healthz fails, stack-copy.sh check shows anything not on NEW, verify-new.sh prints any DIFFER, or check-launch.mjs chief prints CHECK3_FAIL: rollback B.

console-check.sh FAIL is a finding for Mos, not an automatic rollback. The bus and the launches don't depend on the Console's new routes. Mos decides between a rollback B and a Console-only fix forward. The same goes for a step 9 failure, unless the live talk path is broken.

Rollback

Never start the bus after a restore that did not print RESTORE_OK. Under either copy, a bus that starts on a data root with no bus/bus.sqlite creates a fresh empty store.

A: the migration failed (step 5), units and wrapper still on OLD

  1. Bus and Console stay stopped.
  2. bash $P/restore.sh $D $B. RESTORE_OK. Keep the printed bus.post-attempt-* path for diagnosis.
  3. bash $RS/verify-live.sh, MATCH × 3.
  4. Start the bus, then the Console, on OLD. stack-copy.sh check shows OLD.
  5. Report the refusal code. Nobody retries the migration until it is diagnosed.

B: after the start on NEW

  1. Stop the Console, then the bus; both inactive.
  2. bash $P/restore.sh $D $B. RESTORE_OK. The post-attempt store stays at the printed path.
  3. Repeat steps 6 and 7 with $OLD in place of $NEW.
  4. bash $RS/verify-live.sh, MATCH × 3. A DIFFER means stop and diff against the saved files before starting anything.
  5. Start the bus, then the Console. stack-copy.sh check shows OLD.

OLD never opens the 3g file: step 2 moves it aside before step 5 starts the bus. Anything the bus recorded after the step 8 start, at the least the chief launch, is in the post-attempt store only. Mos and Jason decide what happens to it. Nobody strips columns or edits evidence in place. No Save ran, so Jason's project repo has nothing to reconcile.

Notes

  • Land-work gap. Since de352dfaf the canonical checkout's scripts/land-work/read.mjs imports the 3g SCHEMA_VERSION and refuses against the 3f bus. Until step 5, land-work refuses on the live store and any landing uses conductor-apply. After a rollback B the gap reopens.
  • Keep the copies: OLD, NEW and the earlier ones. Nothing prunes copies.
  • Keep $B, its rehearsal-* copies and any bus.post-attempt-* directory until Mos says otherwise. They hold live bus data, stay 0700 and nobody but Mos opens them. Deleting them is a data deletion and goes to Jason.
  • Record for BUILD-LOG: step 1 stamp, backup sums, rehearsal output, the migrate exit and last line, the step 8 output, the chief and pm manifest paths and prompt digests, Jason's browser check, and the downtime from step 2 to step 8.
  • After the window, still not in it: Jason's mosaic name for any instance he wants named, and a first Save from #/docs if he wants one. Row 143 smoke points 1-2 stay with Jason after 143 lands.

What Sage ran for this plan

Nothing live was stopped, started, installed, migrated or re-pinned. No live store file was opened.

  • P1: stack-copy.sh copy $NEW, rc 0. The git archive diff against the copy, rc 0, no output. NEW_COPY_HAS_141_148. grep git.merge.protected over NEW's roles: none.
  • P2: capture-rollback.sh to $RS: no drop-in directories, MATCH × 3.
  • P4: business-check.mjs. OLD: roles 6, projects 1, arbiters 2, chief no, pm no, LOAD_OK. NEW: the same counts, chief yes, pm no, LOAD_OK.
  • verify-new.sh now: DIFFER × 5, rc 1. With NEW set to OLD: MATCH × 5.
  • check-launch.mjs against the live chief (r2665bdaad973) and pm (r0271f4ae385b) runs. With $OLD: chief CHECK3_FAIL on exactly the two row 141 checks (untyped [], no untyped line), everything else PASS; pm CHECK3_PASS. With $NEW: the sources check fails as well, for both. write_record INFO prints present.
  • console-check.sh $OLD against the live Console, GETs and one refused loopback POST: 5 × FAIL, CONSOLE_FAIL. OLD has no markdown-preview.js (404), no edit in /api/config, no project routes (404), and its generic preview refusal has no sent field.
  • console-check.sh $NEW against a stub Console: NEW's startServer on a free loopback port with stub project reads and a recording projectSave (fixture/console-stub.mjs). 5 × PASS, CONSOLE_PASS, and the stub recorded no save or send call.

On fixture data roots under $P/fixture, made fresh at 3f by the OLD copy's store.mjs (fixture/t.sh, output in fixture/t.out):

  • stopped-check.sh fails on a store held open by a process and on a writer.lock, and passes once both are gone.
  • backup.sh refuses a held store (no OUT made), a locked store, a tampered rollback set and an existing OUT. BACKUP_OK on two stores, each with a leftover WAL and SHM from a killed holder.
  • rehearse.sh: REHEARSAL_PASS twice, migrate 43 ms on the fixture, on a backup that has a WAL.
  • A stand-in step 5: rc 0, bus schema 3g ready, BACKUP_INTACT; then NEW opens it at 3g and OLD refuses it.
  • restore.sh refuses a backup from another data root and a locked store; then RESTORE_OK. The restored store opens at 3f under OLD and refuses under NEW; the post-attempt directory is kept.
  • verify-open.mjs refuses ~/.mosaic-dev, rc 4, before opening anything.