row 198 r3: drop --board for every op, reason by target (sage-2837)

install_stack runs webui-service.sh install on activate as well as rollback,
and that install refuses --board. A re-pin to a pre-198 copy would therefore
fail the same way a rollback did. console_for_target now drops the pair for
every op. The reason follows the target's cli.mjs: a target that parses
--board is pre-198 and the 7331 board is retired; one that does not runs the
board in the Console.

M13 covers a kept --board for a parsing target on activate --plan and on
rollback --plan. The webui README rollback note covers any move to a pre-198
copy. BUILD.md's Ruled section is updated to match.

UNVERIFIED: bash -n only; no node or tests run (Sage's gate).
This commit is contained in:
jason.woltje committed 2026-10-22 12:15:32 -05:00
1 parent 435b7b5597
commit 2a758a5bac
4 files changed
+49 -31

No files matched your search

+22 -16
View File
@@ -11,29 +11,34 @@ No test suite, mutant, or node process has run for this commit. Sage's gate (sag
"no node of any kind and no https git; no test runs until my CLEAR." This commit is local.
It must not be pushed until the gate runs green and Sage opens a gap for the push.
## Ruled: rollback drops `--board` (Sage sage-2834, ruling a)
## Ruled: `--board` is dropped for every op (Sage sage-2834 ruling a, sage-2837)
Design item 8's "rollback keeps the flag" part is withdrawn. `webui-service.sh install` has no
`--board` branch (exit 4), and `c3b60e513` removed the flag from its parse_flags, so a kept flag
failed the rollback at install-console. Ruling (a): a rollback always drops `--board`, with its
own plan line: `drop --board ...; the 7331 board is retired, so the rolled-back Console runs
without the board`. A rollback to a pre-198 copy has no board until the next re-pin (README).
Design item 8's "keep the flag where the target parses it" part is withdrawn. `webui-service.sh
install` has no `--board` branch (exit 4), and `c3b60e513` removed the flag from its parse_flags,
so a kept flag fails at install-console. `install_stack` (stack-release.sh:565) runs that install on
activate as well as rollback, so a re-pin to a pre-198 copy has the same failure. sage-2837 makes
the rule per target, not per op.
Code: `console_for_target` drops the pair on rollback whether or not the target parses it; an
activate still keeps it where the target parses it. `DROP_BOARD_WHY` carries the reason to both
print sites (plan and steps).
Code: `console_for_target` drops every `--board` pair from `BASE_CONSOLE`, for any op. The reason
follows the target's `cli.mjs`:
- `parses_flag` true (pre-198 target): `drop --board <url>; the 7331 board is retired, so this
Console runs without the board`.
- `parses_flag` false (post-198 target): `drop --board <url>; the target Console does not parse it,
so the board runs in the Console`.
`DROP_BOARD_WHY` carries the reason to both print sites (plan and steps).
M13 now asserts the drop line, that the rollback `Console:` install line carries no `--board`,
and that the plan leaves the state unchanged.
Tests: M12 unchanged (post-198 target, activate --plan). M13 now covers a kept `--board` for a target
that parses it, on both `activate --plan` to a pre-198 copy and `rollback --plan`. Each asserts the
drop line with the retired-board reason and no `--board` on the `Console:` install line, and that the
plan leaves state unchanged. The README rollback note covers any move to a pre-198 copy.
## What changed in the source
- `scripts/stack-release.sh` (B2, design item 8): `console_for_target` drops a `--board` pair from
`BASE_CONSOLE` unless the target's `cli.mjs` parses `--board`. `parses_flag` matches
`flag === '--board'` and ignores lines that contain `throw` (N2: the target names `--board` only
in a refusal). A dropped pair sets `DROP_BOARD`, printed as `drop $DROP_BOARD; the target Console
does not parse it, so the board runs in the Console` in the plan and the activate path. Init
`DROP_BOARD=""` at the globals. `bash -n` passes; not run.
in a refusal). A dropped pair sets `DROP_BOARD` and `DROP_BOARD_WHY` (reason by target, see the
Ruled section), printed in the plan and the steps. Init at the globals. `bash -n` passes; not run.
- `packages/control-board/src/board.mjs` (B4): `createBoard` takes `isPidAlive` and passes it to
`scanAsync`, so the stale-pid refusal can be tested with an injected liveness check.
- `packages/control-board/src/cli.mjs`: `serve` throws RETIRED, exit 2 (`ConfigError` path).
@@ -116,7 +121,7 @@ Each mutant runs on a scratch copy with the new tests. Not run yet.
| M10 | `/api/reply` and `/api/seen` branches move below the read-only refusal | `board-inprocess.test.mjs` positive control (200, exec once) |
| M11 | `COPY_PATHS` drops `packages/control-board/src` or `tools/tmux` | `pinned-copy.test.mjs` "serve runs from a copy": `/api/board` 200 |
| M12 | `console_for_target` keeps `--board` for a target that does not parse it | `stack-release.test.mjs` M12 |
| M13 | rollback keeps a recorded `--board` for a target that parses it | `stack-release.test.mjs` M13: rollback drop line and no `--board` in the `Console:` line |
| M13 | a recorded `--board` is kept for a target that parses it (activate or rollback) | `stack-release.test.mjs` M13 (activate and rollback): drop line and no `--board` in the `Console:` line |
| M14 | a board read blocks the loop (spawnSync or a blocking exec) | `board-inprocess.test.mjs` "healthz within 200 ms" catches only a pending read that blocks; see the gap below |
| M15 | `index()` joins a scan that started before the last write | `board.test.mjs` M15 (event log) |
@@ -162,6 +167,7 @@ Still in the README, on purpose: `packages/webui/README.md` line 826 runs the co
- control-board, webui and cli suites; `scripts/test-*.sh`; the mutants M1-M15 on a scratch copy.
All wait for Sage's CLEAR.
- The rollback drop (sage-2834 ruling a) is implemented and documented; M13 has its expectation.
- The per-target drop (sage-2834 ruling a, sage-2837) is implemented and documented; M13 has its
expectation for activate and rollback.
- M14: Sage accepted the gap as recorded (sage-2834). The mutant run is the check.
- Push: not done. Sage's gaps govern it.
+20 -8
View File
@@ -851,10 +851,10 @@ test('T44 row 180 item 2: --plan lists tester-port-invalid and tester-port-targe
});
});
// Row 198 (design item 8, B2; sage-2834 ruling a): an activate keeps --board only where the target's
// cli.mjs parses it. A target that names the flag only in its refusal drops it. A rollback always drops
// it, because webui-service.sh install refuses the flag. The plan names each drop. setCli rewrites a
// shared copy's cli.mjs and restores it after.
// Row 198 (design item 8, B2; sage-2834 ruling a; sage-2837): --board is dropped for every op, because
// webui-service.sh install refuses the flag. The reason follows the target's cli.mjs: a target that
// parses --board is pre-198 (7331 retired); a target that names it only in its refusal runs the board
// in the Console. The plan names each drop. setCli rewrites a shared copy's cli.mjs and restores it after.
const BOARD='--board http://127.0.0.1:9';
const REFUSES_BOARD=`// fixture Console, never run\n// serve accepts '--tester-port'\nif (flag === '--board') throw new Error('--board retired: the board runs in the Console (row 198)');\n`;
const PARSES_BOARD=`// fixture Console, never run\n// serve accepts '--tester-port'\nelse if (flag === '--board') board = value;\n`;
@@ -871,9 +871,21 @@ test('row 198 item 8 (M12): a re-pin plan drops a --board the target Console doe
assert.deepEqual(fp(e),before);assert.deepEqual(startsStops(e,n),[]);assert.equal(logLines(e).length,logs);
});
test('row 198 item 8 (M13): a rollback drops the pointer\'s --board even for a copy that parses it, and its install line has none',async(t)=>{
// Sage sage-2834 ruling (a): webui-service.sh install refuses --board, so a rollback always drops it and
// names the drop with the rollback reason. The activation runs without --board; the unit then carries it,
test('row 198 item 8 (M13): an activate to a pre-198 copy that parses --board drops the pointer\'s --board with the retired-board reason, and its install line has none',async(t)=>{
// Sage sage-2837: a target that parses --board is pre-198, and webui-service.sh install still refuses the flag.
const e=await mk(t);await seed(e);setCli(t,C1,PARSES_BOARD);withBoard(e);
const before=fp(e),n=calls(e).length,logs=logLines(e).length;
const r=await run(e,'activate',C1,'acme','--plan');
assert.equal(r.code,0,r.out+r.err);assert.equal(r.err,'');
const lines=r.out.split('\n');
assert.ok(lines.includes(`drop ${BOARD}; the 7331 board is retired, so this Console runs without the board`),r.out);
assert.doesNotMatch(lines.find((l)=>l.startsWith('Console: ')),/--board/);
assert.deepEqual(fp(e),before);assert.deepEqual(startsStops(e,n),[]);assert.equal(logLines(e).length,logs);
});
test('row 198 item 8 (M13, rollback): a rollback drops the unit\'s --board even for a copy that parses it, and its install line has none',async(t)=>{
// Sage sage-2834 ruling (a): webui-service.sh install refuses --board, so a rollback drops it and
// names the drop with the retired-board reason. The activation runs without --board; the unit then carries it,
// and the rollback target B parses it, which must not keep the flag.
const e=await mk(t);setCli(t,B,PARSES_BOARD);
await seed(e);
@@ -882,7 +894,7 @@ test('row 198 item 8 (M13): a rollback drops the pointer\'s --board even for a c
const before=fp(e);
r=await run(e,'rollback','acme','--plan');assert.equal(r.code,0,r.out+r.err);
const lines=r.out.split('\n');
assert.ok(lines.includes(`drop ${BOARD}; the 7331 board is retired, so the rolled-back Console runs without the board`),r.out);
assert.ok(lines.includes(`drop ${BOARD}; the 7331 board is retired, so this Console runs without the board`),r.out);
assert.doesNotMatch(lines.find((l)=>l.startsWith('Console: ')),/--board/);
assert.deepEqual(fp(e),before);
});
+1 -1
View File
@@ -940,7 +940,7 @@ say-so, not a test result. Keep #1507 open pending that ruling.
Rollback requires only stopping the WebUI. The board runs inside it, so there is no separate board to keep running.
A rollback to a pre-198 copy has no board until the next re-pin. The rollback plan drops a recorded `--board` (the 7331 board is retired and the install refuses the flag), and the rolled-back Console runs without a board until a copy that has the in-process board is activated.
A move to a pre-198 copy, by rollback or by activate, has no board until the next re-pin. The plan drops a recorded `--board` (the 7331 board is retired and the install refuses the flag), and that Console runs without a board until a copy that has the in-process board is activated.
Revert the scoped WebUI commit to remove it; there is no data migration to undo.
Seen and reply actions already taken belong to the board and are not rolled back.
+6 -6
View File
@@ -809,18 +809,18 @@ console_for_target() {
local cli="$COPIES/$TO/packages/webui/src/cli.mjs"
local -a without_tester=()
sso_base
# Row 198 (design item 8, Sage sage-2834 ruling a): --board stays only for an activate whose target's
# Console parses it. A rollback always drops it: 7331 is retired and webui-service.sh install refuses
# the flag, so a kept flag would fail the rollback at install-console. Each drop is named in the plan
# and the steps. The check reads the target's own cli.mjs, as --tester-port does.
# Row 198 (design item 8, Sage sage-2834 ruling a, sage-2837): --board is dropped for every op.
# webui-service.sh install refuses the flag, so a kept flag fails at install-console, on a rollback
# and on an activate to a pre-198 copy alike. The reason follows the target: a target that parses
# --board is pre-198 (7331 is retired); one that does not runs the board in the Console. Each drop
# is named in the plan and the steps. The check reads the target's own cli.mjs, as --tester-port does.
DROP_BOARD="" DROP_BOARD_WHY=""
local -a kept=()
for ((i = 0; i < ${#BASE_CONSOLE[@]}; i++)); do
if [ "${BASE_CONSOLE[i]}" != --board ]; then kept+=("${BASE_CONSOLE[i]}"); continue; fi
i=$((i + 1))
if [ "$OP" != rollback ] && parses_flag "$cli" --board; then kept+=(--board "${BASE_CONSOLE[i]:-}"); continue; fi
DROP_BOARD="--board ${BASE_CONSOLE[i]:-}"
if [ "$OP" = rollback ]; then DROP_BOARD_WHY="the 7331 board is retired, so the rolled-back Console runs without the board"
if parses_flag "$cli" --board; then DROP_BOARD_WHY="the 7331 board is retired, so this Console runs without the board"
else DROP_BOARD_WHY="the target Console does not parse it, so the board runs in the Console"; fi
done
BASE_CONSOLE=("${kept[@]}")