Files
jason.woltje b650146479 row 195: scripts/case1-accept.sh, root's Case 1 acceptance (F1-F11, P1-P11), its test, TOOLS.md entry (#1693)
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.
2026-10-21 04:58:17 -05:00

27 KiB

Row 195 design: the Case 1 install on 10.1.1.53 (r7)

Darkwing, 2026-10-21. Brief: docs/plans/2026-10-20_hub-own-uid.md, blob 45be7f09af, section "Brief: the Case 1 install on 10.1.1.53 (row 195)". Gitea #1693. Reviewer Duckworth, gate owner Sage. Branch darkwing/row195-r7 on c515273db. r6 (b7239bb66 on darkwing/row195, approved duckworth-0055) is the base, carried unchanged as 7398b7dd7. The r6 text is in that commit and in b7239bb66.

Changes from r6

Q187 (mos-1108) made install and upgrade product commands, and mos-1118 split r6 into the installer rows (docs/plans/2026-10-20_installer-rows.md). That brief says row 195 "shrinks to the factory profile's values and the Case 1 acceptance". So r7 is mostly a cut. What r6 designed now ships elsewhere:

r6 piece Now State
stack-release.sh --units external: confirm, revert, gate, rollback --to, external adopt and status; stack-copy.sh check --units external and bundle copies row 232, I1 landed
case1-repin.sh (A1-A5, A2f, R1-R4) scripts/stack-upgrade.sh, row 233, I3 landed
the host steps 2a, 2a', 2a'' and the window's root steps docs/guides/host-run-sheet.md sections 1-10, row 233 landed
case1-hostcheck.sh, case1-pins.sh, mosaic-hostcheck.service (the drift stop, r6 B2) row 240, I4 landed c515273db
the unit render, --profile factory, --pm, --claude, the factory serve flags, the precheck row 230, C2-13a, I2 landed
the install as mosaic: bootstrap, business file, copy row 226, C2-13b, I2, scripts/case2-install.sh assigned to Gosalyn, briefed, not started
docs/guides/case1-install.md the host run sheet, plus section 6 of this file

Other changes:

  • Claude moves under /opt (sage-1522, run sheet section 3). --claude is /opt/mosaic-stack/claude-<pin>/node_modules/@anthropic-ai/claude-code/bin/claude.exe, root's, and root installs and upgrades it. r6's /srv/mosaic/.local/bin/claude is gone. F6 reads the new path.
  • The args file is /etc/mosaic-stack/dev.args (run sheet section 7), not case1-unit.args. It holds --copy too, and stack-upgrade.sh rewrites only that line, after confirm passes.
  • Both units are WantedBy=multi-user.target and start at boot (Q189, mos-1122). r6's "the Console has no [Install], root starts it by hand after a reboot" no longer holds.
  • The business id is stated, with the DEFERRED 240 N1 reasoning (sage-1809, sage-1813). Section 7.
  • The window can't run until row 226 lands. Sage added 226 to row 195's after list at rev 2718 (sage-1813). Section 6 lists what row 195 needs from 226's install.
  • The acceptance runs as root through ops-01, not as localadmin. aa-status, sudo -l -U, systemd-run --uid and the hostcheck all need root. It gains four functional checks for items the run sheet marks "precheck line missing" (F8-F11) and one host line (P11). Section 5.
  • The Discord row in section 2 changed. The precheck's "no Discord binding" line is C2-13b's and is library-only, so for the factory it's F11 in the acceptance.

What row 195 still builds: scripts/case1-accept.sh and its test (section 5). The rest of this file is inputs, the window order and the record the reviewer checks.

0. What I checked on the host

Read only, as localadmin over ssh, 2026-10-20 between 20:40Z and 21:51Z. Nothing in r7 touched .53 (sage-1809). The window's first step re-reads these, since they can drift in a day: unattended-upgrades runs daily.

Item 10.1.1.53 on 2026-10-20 Needed
OS Ubuntu 24.04.5, kernel 6.8.0-124, x86_64, 12 cores, 94 GiB, 217 GiB free on / fits
mosaic user, group, /srv/mosaic absent run sheet section 1
node /usr/local/bin/node v22.22.2, root-owned 24 or later, under /opt (run sheet section 2)
claude not on localadmin's PATH under /opt (run sheet section 3)
bwrap absent; apt candidate bubblewrap 0.9.0-1ubuntu0.3 run sheet section 4
user namespaces kernel.apparmor_restrict_unprivileged_userns = 1, apparmor 4.0.1, no bwrap profile loaded run sheet section 4
profiles that name bwrap none (grep -rlw bwrap /etc/apparmor.d exits 1) hostcheck item 3
fs.protected_hardlinks 1 hostcheck item 6
net.ipv4.ip_unprivileged_port_start 1024 run sheet section 5
listeners below 1024 22 (sshd) and 53 (the resolver); 732 is free the Console's port
ports 7330-7333 free 7333 is the tester port
localadmin NOPASSWD sudo (#423 step 1, mos-1066, mos-1077) ops-01's root path
jarvis groups sudo, docker, lxd, adm #423 step 4 removes them (P5)
unattended-upgrades on, with the security pocket, which carries bubblewrap the drift stop (row 240)
journald SplitMode=uid the PM check reads launches list, not the journal (row 232)

1. The factory profile's values

Instance dev, row 190's default (mos-1118 choice 4). ~ is mosaic's home.

Input Value Who gives it
user mosaic, primary group mosaic with no other member fixed
home /srv/mosaic, 0700 fixed
config dir /srv/mosaic/.config/mosaic-dev row 190
data root /srv/mosaic/.mosaic-dev, marker {"instanceVersion":1,"name":"dev"} row 190
business mosaic-stack, file <configDir>/businesses/mosaic-stack.json fixed (section 7)
profile factory for case2-install.sh, case2-unit.sh and case2-precheck.sh fixed
roles the factory set from roles/, with a chief that has a harness var the go names the set (section 2)
human id Jason's, not tester (broker.mjs:321) the go
model credential <configDir>/secrets/claude-session.env, mosaic 0600, one line CLAUDE_CODE_OAUTH_TOKEN=... Jason places it (section 3)
token slot path none section 2
node /opt/mosaic-stack/node-<v>/bin/node, <v> 24 or later the go names <v> and its sha256
claude /opt/mosaic-stack/claude-<pin>/node_modules/@anthropic-ai/claude-code/bin/claude.exe, <pin> 2.1.304 unless the go names a newer one the go
PM on (--pm) fixed
Console --port 732 on 127.0.0.1, with the unit's CAP_NET_BIND_SERVICE fixed
tester --tester-port 7333 on 127.0.0.1 (P10) fixed
board none fixed
release a git bundle of refs/heads/refactor and the 40-hex commit (run sheet section 6) the go names both and the bundle's sha256

The args file. /etc/mosaic-stack/dev.args, root:root 0644, one argument per line (run sheet section 7):

--profile
factory
--user
mosaic
--home
/srv/mosaic
--business
mosaic-stack
--copy
<40-hex commit>
--node
/opt/mosaic-stack/node-<v>/bin/node
--claude
/opt/mosaic-stack/claude-<pin>/node_modules/@anthropic-ai/claude-code/bin/claude.exe
--pm
--
--port
732
--business
mosaic-stack
--tester-port
7333

case2-unit.sh takes exactly these before -- (scripts/case2-unit.sh:54), and stack-upgrade.sh refuses args-unreadable on anything else (scripts/stack-upgrade.sh:126-148). The factory profile refuses the preview trio, --send and the proof pair (case2-unit.sh:169). .53 has no proxy, and Jason reaches the Console with ssh -L (sage-1451 Q3). Changing a serve flag is a re-install with its own go, not an upgrade.

Why the port is 732 (r6, mos-1101 item 5). Role sessions share the host's network namespace (packages/seat/src/sandbox.mjs:67), so while the Console is down a session could bind a port above the floor and answer in its place. Below 1024 only the Console unit can bind, through its capability. The tester port can't go below 1024 (packages/webui/src/cli.mjs:33), so P10 records it.

PATH for mosaic's steps. sudo -u mosaic -H resets PATH to sudo's secure_path, which finds /usr/local/bin/node, v22. So every mosaic step runs as sudo -u mosaic -H env PATH=/opt/mosaic-stack/node-<v>/bin:/usr/local/bin:/usr/bin:/bin ..., the list case2-unit.sh renders as the units' PATH. stack-upgrade.sh builds it from the args file's --node.

2. Where Case 1 differs from Case 2

Case 2 (library) Case 1 (factory) Reason
business file with the library mapping mosaic-stack with no library key. The precheck refuses a library project under factory (profile-library-business, case2-precheck.sh:127-128) the factory has no books repo
bean, chet, persona vars and files, specialists the factory roles from roles/, with a chief that has a harness var the go names the set. I haven't read Jason's live business file and won't. The acceptance needs only the chief (F3); a chief with no harness var answers no-route. The human adds roles later with instances.set, no new row
token slot library-gitea none the factory's per-agent git identity is row 199's credential store (Megavolt, #1697). Until it lands, git work stays on dragon-lin (Q182)
bus.tester off on; the precheck refuses it off under factory (case2-precheck.sh:135) the acceptance and the Q173 test system need the tester
persona modes and digests, library.sync, library.status skipped no persona files, no library (C2-13b brief, factory profile)
ZAI_API_KEY in claude-session.env not checked; the file holds CLAUDE_CODE_OAUTH_TOKEN no instance has provider zai (run sheet section 9)
no Discord binding (C2-13b, library only) none either. F11 checks that <dataRoot>/discord/ holds no binding the factory's connector stays on dragon-lin. Two connectors on one bot token would split its events
the claude marker prompt runs, since the chief's harness is claude-code
serve flags: preview trio, --send, proof pair --port 732, --business mosaic-stack, --tester-port 7333 no proxy on .53
bus: no --pm, no MOSAIC_CLAUDE_BIN --pm, and Environment=MOSAIC_CLAUDE_BIN=<--claude> the PM dispatches the factory's work; without the path the bus finds no claude on the unit's PATH
Console proof file (run sheet section 9, .54) none no proof pair

All of these are inputs to the landed scripts. Row 230 implemented the four factory items r6 asked for (serve flags, --pm, --claude, row 190's tokens), and nothing in them hardcodes a Case 1 value.

3. Model access

Q185: Case 1 uses the account the factory uses today, the Claude subscription behind dragon-lin's claude-session.env. No script copies that file and none reads its value. In the window, Jason, or the person he names under the Q173 secrets rule, makes a credential for .53 from that account and writes it to /srv/mosaic/.config/mosaic-dev/secrets/claude-session.env as mosaic, mode 0600 (run sheet section 9). Nobody prints it. The check is stat -c '%U %a %F' PATH only (F8).

The bus unit reads it through EnvironmentFile=- (case2-unit.sh:243). The leading - means a missing file doesn't stop the bus, so the chief's first launch would fail instead. The window places the file before the units start (section 6, W9), and the precheck's claude marker prompt then proves it works without printing it.

Quota: the factory on dragon-lin and on .53 share one subscription. Q185's "use existing" accepts that. The pi harness stays off on .53 until P19 #383 settles (mos-0865). The business file names claude only.

4. Upgrade, rollback and backup on .53

All three are scripts/stack-upgrade.sh (row 233), run as root by ops-01 from /root/mosaic-stack at the reviewed commit. Usage, exits and the step log are in docs/TOOLS.md, "Root's upgrade driver", and run sheet section 6.

Action Command Who Go
upgrade stack-upgrade.sh upgrade --instance dev --bundle /srv/mosaic/incoming/<sha>.bundle --sha <sha> after run sheet section 6's per-upgrade lines ops-01 Mos per run; --migrate also needs Jason present (decision 96)
rollback stack-upgrade.sh rollback --instance dev --to <copy> [--restore <ACT> [--events N]] ops-01 Mos per run. Mos or Sage picks <copy> from stack-release.sh status mosaic-stack --units external, read as mosaic. The plan refuses unless it is the previous distinct copy
backup of the store A3's backup_store into <dataRoot>/backups/stack/<ACT>, inside every upgrade the driver the upgrade's
rehearsal stack-release.sh rehearse against the log's newest backup, before anything stops, on --rehearse-port, default 7339, which section 0 found free the driver the upgrade's
backup of the VM a PBS backup of VM 1008 before the install window and before any --migrate ops-01 the window's go (Q182, Q183: agents keep PBS)

Three .53 specifics:

  1. The first upgrade after install needs --no-rehearsal in its go. The install's adopt takes no backup, so the log records none, the rehearsal exits 3 (no-backup), and the driver goes on only with the flag (stack-upgrade.sh:323-324). Every later upgrade refuses the flag (rehearsal-required).
  2. Exit 5 (args-failed): the new stack is live and confirmed, but dev.args still names the old copy. ops-01 moves the file the result line names into place with mv -f, and nothing else (run sheet section 6).
  3. Exit 2 leaves both units stopped and the activation open. ops-01 sends Mos the exit and the last lines of /var/log/mosaic-stack/upgrade-dev.log, and Jason or Mos settles it with confirm or revert as on dragon-lin. A unit that doesn't stop within --timeout ends here on purpose (A2f's stale-start, duckworth-0055 N1, now in run sheet section 6).

The store backup doesn't hold the config dir or the credential. PBS backups of VM 1008 hold the whole disk, the credential included (P4). A restore swaps only the store and leaves claude-session.env in place.

Re-install, for a serve flag or role change, is not an upgrade. It needs its own go and runs: stop the Console, then the bus; rewrite dev.args with the same --copy; case2-unit.sh with it; both units start again from the render. Row 230 N1 (webby-0046) holds here: a re-render with the units running leaves each on its old copy, so ops-01 stops both first. stack-upgrade.sh already stops both before it renders.

5. Acceptance: scripts/case1-accept.sh

New in this row. ops-01 runs it as root on .53, from /root/mosaic-stack:

case1-accept.sh --business mosaic-stack --args /etc/mosaic-stack/dev.args --claude-pin <pin> --node-pin <v>

It reads the user, home, node, claude and ports from the args file and refuses (exit 2) when that file fails stack-upgrade.sh's rules. Every check inside the instance runs as sudo -u mosaic -H env PATH=<node dir>:/usr/local/bin:/usr/bin:/bin. It changes nothing on the host except what F3's message writes to the store. It prints one line per check, <id> <path> <mark> <evidence>, then a count of each mark. Marks: closed, accepted Q182, accepted Q183, accepted mos-1096, accepted mos-1101, and unnamed, which means "not named in Q182, Q183 or a Mos ruling". It exits 1 when any F check fails and 0 otherwise. A P check never changes the exit. Each unnamed line goes to Mos, who takes it to Jason (R8 N1). Duckworth reads the whole output.

It prints no value from claude-session.env and never opens it.

Functional checks. Any failure fails the row:

Id Check How
F1 the units run as mosaic for [email protected] and mosaic-webui.service: active, User=mosaic, and the uid in /proc/<MainPID>/status is mosaic's
F2 the Console answers on 127.0.0.1:732 only curl -fsS http://127.0.0.1:732/healthz succeeds, and ss -Hltnp shows 732 bound to 127.0.0.1 only, by the Console's MainPID. Jason reaches it with ssh -L 7332:127.0.0.1:732 [email protected]. A LAN listener waits for Q175 SSO (sage-1451 Q3)
F3 one tester message gets a reply as mosaic: mosaic talk chief <marker> --business mosaic-stack --tester --wait 300. A stored reply to the marked message passes. The inbox shows decisions only, so it isn't used
F4 the precheck passes case2-precheck.sh --business mosaic-stack --profile factory as mosaic exits 0
F5 a role session runs under bwrap with the profile while F3's reply is pending: a /usr/bin/bwrap process owned by mosaic has a claude child. Its /proc/<pid>/attr/current equals bwrap (enforce) and the child's equals bwrap//&unpriv_bwrap (enforce), exact strings after the trailing newline is cut
F6 claude is the pin the args file's --claude path sits under /opt/mosaic-stack/claude-<pin>/, and its --version as mosaic prints <pin> (Claude Code)
F7 the PM run is live launches list --json as mosaic has exactly one live entry for the PM's instance (launch.by), the check confirm runs
F8 the credential's file stat -c '%U %a %F' on claude-session.env reads mosaic 600 regular file, and it isn't a symlink. Run sheet section 9 marks this "precheck line missing"
F9 the autoupdater is off /etc/claude-code/managed-settings.json is root:root 0644 and its env.DISABLE_AUTOUPDATER is "1". Run sheet section 3 marks this "precheck line missing"
F10 node is the pin the args file's --node is /opt/mosaic-stack/node-<v>/bin/node with <v> the --node-pin, and its --version prints <v>. Run sheet section 2 marks this "precheck line missing"
F11 no Discord binding /srv/mosaic/.mosaic-dev/discord/ is absent or holds no *.json (section 2)

Host paths. These report and never fail the row:

Id Path Mark Evidence
P1 mosaic has sudo closed only when sudo -l -U mosaic prints "is not allowed to run sudo"; any other output is unnamed the sudo -l result
P2 mosaic has supplementary groups or a key closed when id -G mosaic is one entry and ~mosaic/.ssh/authorized_keys is absent or empty id, file state
P3 localadmin NOPASSWD sudo, reached with the dragon-lin key accepted Q183 sudo -l -U localadmin
P4 root on pve over VM 1008, and PBS backups that hold /srv/mosaic with the credential accepted Q182 and Q183 a fixed line; the script can't see pve
P5 jarvis, which runs Hermes, reaches /srv/mosaic through sudo, docker or lxd closed when jarvis is in none of the three; unnamed otherwise. #423 step 4 removes all four groups before the install (mos-1091 4c) id jarvis
P6 other members of sudo, docker, lxd closed when sudo holds only localadmin and docker and lxd are empty; each other member is its own unnamed line getent group lines
P7 a file under /etc/sudoers.d naming mosaic closed when none file names
P8 the Console or tester reachable from the LAN closed when 732 and 7333 are bound to 127.0.0.1 only, each by the Console's MainPID, and nothing listens on 7331 listener table
P9 a user namespace through /usr/bin/bwrap accepted mos-1096 only while all eight hold, unnamed otherwise: hostcheck items 1 to 6, read from /usr/local/lib/mosaic-stack/hostcheck.sh --check's own ok N lines (item 7 is P10's floor); sudo -u jarvis /usr/bin/bwrap --version fails with "Permission denied"; getent group mosaic lists no member and no other account has its gid. F5 adds the label of a real role session the hostcheck lines and each item
P10 a session binds the tester port 7333 while the Console is down accepted mos-1101 while the port floor is above 732 and 7333 is held by the Console's MainPID the floor, the owner pid
P11 the boot check runs before the bus closed when mosaic-hostcheck.service is enabled and systemctl show -p Before on it names mosaic-bus@<business>.service for the args file's business; unnamed otherwise (section 7) the Before= value

P9 reads hostcheck's output, which root's own script prints, by matching ^ok [1-6] lines. It runs --check only, never --enforce, so the acceptance can't close bwrap.

Tests, in packages/cli/tests/case1-accept.test.mjs. The script takes a test seam, --root DIR, for /proc, /etc, /srv/mosaic and getent, and refuses it when run as root, as the hostcheck does. Fake systemctl, curl, ss, sudo, bwrap, sysctl, stat, claude, node and hostcheck.sh on PATH. Nothing reaches .53 or dragon-lin's live state, and no test reads a real credential. Cases:

  • every check passing exits 0, with the counts matching the lines;
  • each F check failing alone exits 1 with its line;
  • F5 fails on unpriv_bwrap (enforce), bwrap//&unpriv_bwrap (complain) and a trailing space;
  • F6 fails on a stub claude.exe that prints nothing and on a path outside claude-<pin>;
  • F8 fails on mode 0640, on another owner and on a symlink, and the stand-in credential's value never appears in the output;
  • F11 fails on one *.json under discord/;
  • each P check in each of its marks: P1 with output that grants nothing but lacks the refusal text (unnamed), P9 unnamed once for each item missing, P10 unnamed with the floor at 700 and with 7333 held by another pid, P11 unnamed with Before= naming another business;
  • every P check unnamed still exits 0;
  • a bad args file refuses with exit 2 before any check;
  • every in-instance call goes through sudo -u mosaic -H env PATH=<node dir>:..., read from the fake sudo's recorded argv;
  • the hostcheck fake records --check and never --enforce.

Mutants, each killed by a named case:

  1. F1 passes on a MainPID owned by root.
  2. F2 accepts a listener on 0.0.0.0.
  3. F3 passes without a reply.
  4. F5 matches a substring.
  5. F6 skips the --version check.
  6. F8 skips the owner check.
  7. F11 ignores discord/.
  8. P1 closes on any output.
  9. P5 maps to closed with jarvis in docker.
  10. P9 drops the jarvis item.
  11. P11 accepts any Before=.
  12. A P check's unnamed sets the exit to 1.

6. The .53 window

Nothing in this row touches .53 (sage-1809). Every root step below is ops-01's, over ssh [email protected] with sudo, and waits on a Jason decision (mos-1086). The window needs:

  • row 226 landed;
  • Jason's or Mos's go naming the commit, the bundle's sha256, the node and claude pins, the role set and Jason's human id;
  • a backup first;
  • a STATE report after.
# Step Runs as Source
W0 re-read section 0's items; stop on any change and report it localadmin, read only section 0
W1 PBS backup of VM 1008 ops-01 on pve section 4
W2 the stack's user and home root run sheet 1
W3 node under /opt root run sheet 2
W4 claude under /opt, managed settings root run sheet 3
W5 bubblewrap, the profile, the statoverride, the no-new-privs test, the drift stop; hostcheck.sh --check exits 0 with item 7 at 1023 root run sheet 4
W6 the port floor (read) root run sheet 5
W7 the bundle, root's checkout at <sha>, mosaic's clone root, then sudo -u mosaic -H run sheet 6
W8 case2-install.sh --profile factory with section 1's inputs: bootstrap, the business file, roles, human id, the copy sudo -u mosaic -H env PATH=... row 226
W9 place claude-session.env Jason, or who he names run sheet 9, section 3
W10 write dev.args (section 1); case2-unit.sh from it, which enables both units and starts the bus, then the Console root run sheet 7, 10
W11 case2-precheck.sh --business mosaic-stack --profile factory exits 0; hostcheck.sh --check exits 0 with item 7 at 732 mosaic; root row 230, 240
W12 stack-release.sh adopt mosaic-stack --units external, unless row 226's install does it sudo -u mosaic -H row 232
W13 case1-accept.sh (section 5); the output to Duckworth, then Mos confirms root this row
W14 STATE report to Mos ops-01 brief

Install rollback, any step before W13 passes. Stop and disable both units, remove their files and dev.args, then the run sheet's rollbacks for sections 4, 3 and 2 in that order. Removing the user and home (userdel -r mosaic) needs its own Mos go, since it deletes the home. W1's backup covers the rest.

What row 195 needs from row 226. Row 226 is assigned to Gosalyn, briefed and not started, so I'm listing what the window needs from its design, not designing it:

  1. --profile factory writes the business file with bus.tester on and no library key, and the roles the go names.
  2. It writes no token slot and no persona files (its brief says so).
  3. It runs before the unit render, since the copy must exist for case2-unit.sh --copy, and it starts no unit.
  4. Who writes dev.args. The C2-13b brief says the install doesn't write unit files, and the run sheet says "I2's install writes it". mosaic can't write /etc/mosaic-stack. So either root's part of I2 writes it, or ops-01 writes it from section 1 at W10. Either works for row 195. The file's content is fixed above.
  5. Whether it runs adopt. If it doesn't, W12 does.

I sent these to Gosalyn on 2026-10-21 (gosalyn-0085 acknowledged), and the run sheet takes whichever answer 226 gives.

7. The business id

.53's business id is mosaic-stack (sage-1813). It appears in:

  • the args file, twice;
  • the bus unit [email protected];
  • the business file name;
  • case1-accept.sh --business.

The landed boot unit packages/cli/systemd/mosaic-hostcheck.service has [email protected]. So at boot the drift stop runs before .53's bus, and DEFERRED 240 N1 (da3051ccc: After=mosaic-hostcheck.service in mosaic-bus.service.in) doesn't bind this row.

If a go ever names another id for .53, the window stops before W8, and the N1 fix lands first as its own reviewed row. P11 is the check on the host. It reads unnamed when the boot unit's Before= doesn't name the installed bus.

DEFERRED 240 N2 (the apt hook goes in before the first --check) is a run sheet order note. This row doesn't edit the run sheet, so it stays deferred.

8. Rulings this design rests on

  • sage-1451: Q1 system units; Q3 the Console on 127.0.0.1 with ssh -L; Q4 a rebase goes to a new branch name (#426(a), mos-1086).
  • mos-1091, mos-1096, mos-1098, mos-1101: the bwrap path, its profile and P9; the port squat and P10; .53 runs the PM. Their r6 sections stand, and rows 232, 233, 240 and 230 implemented them.
  • mos-1108 (Q187) and mos-1118: the installer rows and the scope cut. mos-1122 (Q189): both units start at boot.
  • sage-1522: claude under /opt, root-owned.
  • sage-1809: no step on .53, root steps named and left to ops-01, the business id stated.
  • sage-1813: 226 in the after list (rev 2718); the id is mosaic-stack and 240 N1 doesn't apply.
  • Q185: the factory's model account. Q173: credentials placed by Jason or his named person.

9. Manifest (planned)

  • scripts/case1-accept.sh (new), mode 100755.
  • packages/cli/tests/case1-accept.test.mjs (new). Section 5's cases and mutants.
  • docs/TOOLS.md: a case1-accept.sh entry with usage, marks and exits, beside the hostcheck entry.
  • agents/darkwing/work/queue-195/BUILD.md (new): run environment, the mutant results, deviations.
  • No change to case2-*.sh, stack-upgrade.sh, stack-release.sh, case1-hostcheck.sh or the run sheet. Two follow-ups stay out of this row:
    • row 230 r2 N1. The "still on copy" line reads the id from the unit file. On .53 neither the install nor stack-upgrade.sh reaches that line, since both start from stopped units.
    • DEFERRED 240 N2.
  • No change to ~/.mosaic-dev, ~/.config/mosaic-dev, dragon-lin's units or ~/.mosaic.

Gate: Duckworth approves this design, then the manifest; the cli suite and every scripts/test-*.sh green on Sage's gate; section 5's twelve mutants killed in a slot Sage grants.