7.8 KiB
Row 273 (#1778): activate --send and --no-sso
Darkwing, 2026-10-22. Brief: docs/plans/2026-10-22_activate-send-nosso.md (blob in f0d0b31e9), approved by
Mos in mos-1246 (F39). Base: origin/refactor 0042f713d. Reviewer: Filbert. Gate owner: Sage.
What changed
Only scripts/stack-release.sh and its two node test files.
activate --sendputs--sendinto the run's Console flags after--ssoand before the tester flags, where webui-service.sh renders it. It refusessend-needs-ssounless those flags hold--sso, given or carried. The plan line issend: on; --send was given, orsend: carriedwhen the pointer already holds it.check_targetlets it name the pointer's copy only when the pointer holds no--send, so a second--sendon the same copy refusesalready-active.activate --no-ssorefusessso-not-onwhen the pointer holds no--sso. Otherwise it takes the Console flags from a rollback set (below) and keeps the pointer's tester flags.send_for_targetdrops--send, whether it came from the set or the pointer. It is the only place that does, so one mutant there (M3) shows the drop is tested. The plan printssso: off; Console flags from <set>/<file>; restores <flags>and a send line,send: dropped; --no-sso drops --send (F39)orsend: off; --no-sso was given and the pointer holds no --send. The steps log getsdrop --send; --no-sso was givenandturn off --sso; the Console flags come from <set>/<file>.- Usage errors:
--sendwith--no-send;--no-ssowith--sso,--send, a proof pair or--no-proof; either flag on any verb but activate; either flag under--units external.--no-sso --no-sendis allowed, since the brief doesn't forbid it and both drop the same flag. - The set read that
sso_basedid inline for a rollback to a Console without--ssomoved intoset_flags, which both paths call. Its three messages are unchanged, and T57 still pins them. from_loghas a new mode,sets: the activation of each ok activate and rollback line, newest first, stopping at the adopt line, which took no rollback set.
The consequential choice: which set --no-sso reads
The brief says the flags come from "the rollback set of the activation that turned --sso on", and that
"a set whose Console already ran with --sso" refuses. Each set holds the Console unit from before its
run. So the activation that turned --sso on is the newest one whose set holds a Console without
--sso: every activation after it ran with --sso, and so did every set it left.
no_sso_base walks the log's sets newest first and stops at the first set whose Console has no --sso.
It refuses sso-off-flags at the first set it can't read (missing, failing its SHA256SUMS, or with no
argv the script can parse), since it can't tell whether that set was the one. If every set ran with
--sso, it refuses with the Console in <set>/<file> already ran with --sso, and the log names no earlier activation. A log with no set at all refuses with the log names no activation with a rollback set.
The tradeoff: a later same-copy re-pin that carried --sso (a --no-send, or a re-pin onto a newer
copy) doesn't break --no-sso, which the phase 2 window needs if a re-pin lands between step 5 and a
rollback. The cost is that "a set that ran with --sso refuses" now applies only when no older set
without --sso exists. A tampered set on the walk still refuses, even one newer than the set the walk
would have used. Filbert, please rule on this reading.
Filbert's early look: B1 and N1 (sage-2315)
Filbert accepted the walk. Sage ruled both findings into this row.
- B1: row 200's rollback to a copy whose Console has no
--ssotook the flags from the set and kept the pointer's--send. From an--sso --sendpointer, which this row's--sendcan now make, that gave a Console with--sendand no--sso, which F39 b forbids.send_for_targetnow drops--sendwhenever the run drops the pointer's--sso, so it is still the one place that drops it. The plan line readssend: dropped; the run drops --sso, and --send needs it (F39), and the steps log getsdrop --send; the run drops --sso. Row 200's sso line now names only the tester as following the pointer. - Changed existing assertion, for the review to approve by name: T57 item 3 (sage-1389), "the
plain case", ends with
assert.deepEqual(pointer(g).console,[...noSend(gbase),...pair(f)]). It used to expect'--send'at the end. The case seeds a legacy--sendpointer, turns--ssoon (which carries--send), then rolls back to B, whose Console has no--sso. A carried--sendthat ends without--ssobreaks F39 b whatever its source, so the old pin asserted the wrong outcome (Sage accepted this in sage-2321). Row 200's two sso plan strings also lose "--send and" (ROLLBACK_PLANand the round 3 named-instance case), since--sendno longer follows the pointer on that path. - N1: the walk skipped an ok line with no activation. Skipping it could land on an older set that
isn't the right one, so it now refuses
sso-off-flagswithan ok line in the log names no activation. The walk reads the log throughmapfile. That gives no entry for an empty log, so a log with no set still gets its own message.
test-release.sh
The window plan says test-release.sh "gets cases for both". The brief lists it only as a file to run whole. test-release.sh is release.sh's Docker selftest and doesn't call stack-release.sh, so the cases live in the node tests and test-release.sh runs unchanged.
Tests (T58 in stack-release.test.mjs)
- T1:
--sso --sendfrom B onto C1 with a tester port: plan, pointer, unit and steps.--sendbeside a carried--ssore-pins C1 once; a second--sendrefusesalready-active; C carries it. - T2:
--sendwithout--ssorefuses on another copy and on the pointer's copy, also when the pointer carries--sendalone;--planlists the refusal. - T2 (sage-2315 B1):
--sso --sendonto C1, then a rollback to B, whose Console has no--sso. The pointer and the unit end with neither flag, and the plan and steps name the drop. - T2 and T4 usage: every forbidden pairing and verb, checked against the full usage text.
- T3: from an
--sso --sendpointer,--no-ssoon the same copy gives back the pre---ssoExecStart line byte for byte, without--send. A second case walks past a--no-sendre-pin that ran with--ssoand keeps the pointer's tester port (7334), which differs from the set's (7333). A third (sage-2305 a) turns--ssoon, then re-pins twice with--sendand--no-send, so the two newest sets both hold--sso.--no-ssothen gives back the arrival's ExecStart line byte for byte. - T4 (sage-2305 b): two sets, both edited or written to hold
--ssowith sums intact, refusesso-off-flagsthroughrefused(). - T4:
sso-not-on; an ok line with no activation; a set that fails its sums, ran with--sso, has no argv or is missing; a log with no set. Each runs throughrefused(), so nothing stops and nothing else changes. - T5: stack-release-external.test.mjs adds
--sendand--no-ssoto the row 232 usage list. - T55 test 5 and the T57 usage test match the new usage line.
Mutants
| # | Change | Expected killer |
|---|---|---|
| M1 | set_flags skips the SHA256SUMS comparison when --no-sso is given |
T4 tampered set |
| M2 | no_sso_base skips the sso-not-on refusal |
T4 sso-not-on |
| M3 | the --no-sso branch of send_for_target keeps --send |
T3 |
| M4 | --send skips the send-needs-sso refusal |
T2 |
| M5 | parse_opts drops the --send/--no-send rule |
T2 and T4 usage |
| M6 | a rollback to a Console without --sso keeps --send (B1) |
T2 (sage-2315 B1) |
| M7 | the walk skips an ok line with no activation (N1) | T4 no-activation line |
Runs and results go in the round's notes once Sage clears the slot.