Files

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 --send puts --send into the run's Console flags after --sso and before the tester flags, where webui-service.sh renders it. It refuses send-needs-sso unless those flags hold --sso, given or carried. The plan line is send: on; --send was given, or send: carried when the pointer already holds it. check_target lets it name the pointer's copy only when the pointer holds no --send, so a second --send on the same copy refuses already-active.
  • activate --no-sso refuses sso-not-on when 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_target drops --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 prints sso: off; Console flags from <set>/<file>; restores <flags> and a send line, send: dropped; --no-sso drops --send (F39) or send: off; --no-sso was given and the pointer holds no --send. The steps log gets drop --send; --no-sso was given and turn off --sso; the Console flags come from <set>/<file>.
  • Usage errors: --send with --no-send; --no-sso with --sso, --send, a proof pair or --no-proof; either flag on any verb but activate; either flag under --units external. --no-sso --no-send is allowed, since the brief doesn't forbid it and both drop the same flag.
  • The set read that sso_base did inline for a rollback to a Console without --sso moved into set_flags, which both paths call. Its three messages are unchanged, and T57 still pins them.
  • from_log has 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 --sso took the flags from the set and kept the pointer's --send. From an --sso --send pointer, which this row's --send can now make, that gave a Console with --send and no --sso, which F39 b forbids. send_for_target now drops --send whenever the run drops the pointer's --sso, so it is still the one place that drops it. The plan line reads send: dropped; the run drops --sso, and --send needs it (F39), and the steps log gets drop --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 --send pointer, turns --sso on (which carries --send), then rolls back to B, whose Console has no --sso. A carried --send that ends without --sso breaks 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_PLAN and the round 3 named-instance case), since --send no 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-flags with an ok line in the log names no activation. The walk reads the log through mapfile. 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 --send from B onto C1 with a tester port: plan, pointer, unit and steps. --send beside a carried --sso re-pins C1 once; a second --send refuses already-active; C carries it.
  • T2: --send without --sso refuses on another copy and on the pointer's copy, also when the pointer carries --send alone; --plan lists the refusal.
  • T2 (sage-2315 B1): --sso --send onto 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 --send pointer, --no-sso on the same copy gives back the pre---sso ExecStart line byte for byte, without --send. A second case walks past a --no-send re-pin that ran with --sso and keeps the pointer's tester port (7334), which differs from the set's (7333). A third (sage-2305 a) turns --sso on, then re-pins twice with --send and --no-send, so the two newest sets both hold --sso. --no-sso then gives back the arrival's ExecStart line byte for byte.
  • T4 (sage-2305 b): two sets, both edited or written to hold --sso with sums intact, refuse sso-off-flags through refused().
  • 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 through refused(), so nothing stops and nothing else changes.
  • T5: stack-release-external.test.mjs adds --send and --no-sso to 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.