docs(row253): store repair patch without context whitespace
This commit is contained in:
1 parent
144a7bf7c7
commit
b64aa316ab
2 files changed
+42
-201
No files matched your search
@@ -40,3 +40,5 @@ N1: the fake reads its mode with the Bash file-read builtin form. N2: fixture cl
|
|||||||
## Proposed next gate
|
## Proposed next gate
|
||||||
|
|
||||||
On Sage CLEAR after static approval: both touched files whole, M1-M39 with passing focused bases and byte-identical restoration, CLI whole, WebUI whole and the ten shell files. Previous completed results are historical only because product blobs changed. Use a gXXXXXX TMPDIR under /mnt/storage/scratch/tmp, pipefail and tee, a busy check before every launch, and log/wrapper names that do not match the busy detector. Stop and report according to the new grant.
|
On Sage CLEAR after static approval: both touched files whole, M1-M39 with passing focused bases and byte-identical restoration, CLI whole, WebUI whole and the ten shell files. Previous completed results are historical only because product blobs changed. Use a gXXXXXX TMPDIR under /mnt/storage/scratch/tmp, pipefail and tee, a busy check before every launch, and log/wrapper names that do not match the busy detector. Stop and report according to the new grant.
|
||||||
|
|
||||||
|
REPAIR.diff uses zero context so blank context lines in the stored patch do not trigger whitespace checks on the record itself. The full matching snapshots provide surrounding context.
|
||||||
@@ -1,21 +1,12 @@
|
|||||||
--- a/scripts/case2-unit.sh
|
--- a/scripts/case2-unit.sh
|
||||||
+++ b/scripts/case2-unit.sh
|
+++ b/scripts/case2-unit.sh
|
||||||
@@ -16,7 +16,10 @@
|
@@ -19 +19,4 @@
|
||||||
# a unit file into place. Root only stats files under <home> and reads bounded
|
|
||||||
# stdout from a config/target query run as the stack user through sudo. Root
|
|
||||||
# never imports copy code. This adds sudo and GNU timeout to the installer
|
|
||||||
-# dependencies. It renders the default instance only.
|
-# dependencies. It renders the default instance only.
|
||||||
+# dependencies. The inner query supervisor runs after the uid drop: 5 s inside,
|
+# dependencies. The inner query supervisor runs after the uid drop: 5 s inside,
|
||||||
+# 7 s on root's wait. Fixed system Bash, setsid, sleep, timeout and head are
|
+# 7 s on root's wait. Fixed system Bash, setsid, sleep, timeout and head are
|
||||||
+# resolved and root-validated. Only the config query uses this supervisor.
|
+# resolved and root-validated. Only the config query uses this supervisor.
|
||||||
+# It renders the default instance only.
|
+# It renders the default instance only.
|
||||||
#
|
@@ -153,0 +157,113 @@
|
||||||
# The library profile takes exactly these serve flags, each once:
|
|
||||||
# --port N (1..1023) --business ID (the unit's) --public-host NAME
|
|
||||||
@@ -151,18 +154,141 @@
|
|
||||||
fi
|
|
||||||
|
|
||||||
# Row253: bounded unprivileged metadata, parsed by root without running copy code.
|
|
||||||
+# row253-supervisor-begin
|
+# row253-supervisor-begin
|
||||||
+IFS= read -r -d '' TESTER_SUPERVISOR <<'MOSAIC_TESTER_SUPERVISOR' || :
|
+IFS= read -r -d '' TESTER_SUPERVISOR <<'MOSAIC_TESTER_SUPERVISOR' || :
|
||||||
+set +m
|
+set +m
|
||||||
@@ -129,7 +120,7 @@
|
|||||||
+ *) echo inner-command ;;
|
+ *) echo inner-command ;;
|
||||||
+ esac
|
+ esac
|
||||||
+}
|
+}
|
||||||
tester_capture() (
|
@@ -155,5 +271,11 @@
|
||||||
- local seconds="$1" maximum="$2" file child="" rc=0; shift 2
|
- local seconds="$1" maximum="$2" file child="" rc=0; shift 2
|
||||||
- file="$(mktemp)" || exit 1
|
- file="$(mktemp)" || exit 1
|
||||||
- trap 'if [ -n "$child" ]; then kill -KILL -- "-$child" 2>/dev/null || :; wait "$child" 2>/dev/null || :; fi; rm -f -- "$file"' EXIT
|
- trap 'if [ -n "$child" ]; then kill -KILL -- "-$child" 2>/dev/null || :; wait "$child" 2>/dev/null || :; fi; rm -f -- "$file"' EXIT
|
||||||
@@ -146,46 +137,25 @@
|
|||||||
+ maximum="$1"; head_bin="$2"; shift 2
|
+ maximum="$1"; head_bin="$2"; shift 2
|
||||||
+ "$@" 2>/dev/null | "$head_bin" -c "$((maximum + 1))"
|
+ "$@" 2>/dev/null | "$head_bin" -c "$((maximum + 1))"
|
||||||
+ ' -- "$maximum" "$TS_HEAD" sudo -u "$user" -H env -i "HOME=$home" "PATH=$path" "$TS_BASH" -c "$TESTER_SUPERVISOR" -- "$inner" "$TS_SETSID" "$TS_SLEEP" "$@" > "$file" 2> "$notice" &
|
+ ' -- "$maximum" "$TS_HEAD" sudo -u "$user" -H env -i "HOME=$home" "PATH=$path" "$TS_BASH" -c "$TESTER_SUPERVISOR" -- "$inner" "$TS_SETSID" "$TS_SLEEP" "$@" > "$file" 2> "$notice" &
|
||||||
child=$!
|
@@ -163 +285,4 @@
|
||||||
wait "$child" 2>/dev/null || rc=$?
|
|
||||||
kill -KILL -- "-$child" 2>/dev/null || :
|
|
||||||
- [ "$rc" = 0 ] && [ "$(wc -c < "$file")" -le "$maximum" ] || exit 1
|
- [ "$rc" = 0 ] && [ "$(wc -c < "$file")" -le "$maximum" ] || exit 1
|
||||||
+ # Only GNU timeout and the trusted head can write this root-side channel.
|
+ # Only GNU timeout and the trusted head can write this root-side channel.
|
||||||
+ if [ "$rc" = 137 ] && [[ "$(<"$notice")" == *': sending signal KILL to command '* ]]; then exit 82; fi
|
+ if [ "$rc" = 137 ] && [[ "$(<"$notice")" == *': sending signal KILL to command '* ]]; then exit 82; fi
|
||||||
+ [ "$(wc -c < "$file")" -le "$maximum" ] || exit 83
|
+ [ "$(wc -c < "$file")" -le "$maximum" ] || exit 83
|
||||||
+ [ "$rc" = 0 ] || { case "$rc" in 81|85|86|87|88) exit "$rc" ;; *) exit 87 ;; esac; }
|
+ [ "$rc" = 0 ] || { case "$rc" in 81|85|86|87|88) exit "$rc" ;; *) exit 87 ;; esac; }
|
||||||
cat -- "$file"
|
@@ -165,0 +291 @@
|
||||||
)
|
|
||||||
+
|
+
|
||||||
TESTER_QUERY='import {readFileSync} from "node:fs";import {pathToFileURL} from "node:url";const m=await import(pathToFileURL(process.argv[1]).href);const s=m.loadSystem();console.log(JSON.stringify({dataRoot:s.dataRoot,socket:readFileSync(process.argv[2],"utf8").includes("MOSAIC_TESTER_SOCKET_V1")}));'
|
@@ -259 +385 @@
|
||||||
tester_facts() {
|
|
||||||
local LC_ALL=C pattern='^\{"dataRoot":"([A-Za-z0-9/._@+-]+)","socket":(true|false)\}$'
|
|
||||||
@@ -256,7 +382,7 @@
|
|
||||||
if [ "$PROFILE" = factory ]; then
|
|
||||||
if [ "$tester_socket" = 1 ]; then
|
|
||||||
local query
|
|
||||||
- query="$(tester_capture 5 4096 sudo -u "$USER_NAME" -H env -i "HOME=$HOME_DIR" "PATH=$UNIT_PATH" "$NODE" --input-type=module -e "$TESTER_QUERY" "$COPY/packages/cli/src/config.mjs" "$COPY/packages/webui/src/cli.mjs")" || die "tester config query failed"
|
- query="$(tester_capture 5 4096 sudo -u "$USER_NAME" -H env -i "HOME=$HOME_DIR" "PATH=$UNIT_PATH" "$NODE" --input-type=module -e "$TESTER_QUERY" "$COPY/packages/cli/src/config.mjs" "$COPY/packages/webui/src/cli.mjs")" || die "tester config query failed"
|
||||||
+ query="$(tester_capture 5 7 4096 "$USER_NAME" "$HOME_DIR" "$UNIT_PATH" "$NODE" --input-type=module -e "$TESTER_QUERY" "$COPY/packages/cli/src/config.mjs" "$COPY/packages/webui/src/cli.mjs")" || die "tester config query $(tester_capture_reason "$?")"
|
+ query="$(tester_capture 5 7 4096 "$USER_NAME" "$HOME_DIR" "$UNIT_PATH" "$NODE" --input-type=module -e "$TESTER_QUERY" "$COPY/packages/cli/src/config.mjs" "$COPY/packages/webui/src/cli.mjs")" || die "tester config query $(tester_capture_reason "$?")"
|
||||||
tester_facts "$query" || die "tester config query has invalid facts"
|
|
||||||
[ "$TS_MARKED" = true ] || die "the target does not support --tester-socket"
|
|
||||||
tester_location "$TS_DATA" "$HOME_DIR" "$USER_UID" "" || die "tester path refuses $TS_REASON"
|
|
||||||
--- a/scripts/case1-accept.sh
|
--- a/scripts/case1-accept.sh
|
||||||
+++ b/scripts/case1-accept.sh
|
+++ b/scripts/case1-accept.sh
|
||||||
@@ -16,6 +16,10 @@
|
@@ -18,0 +19,4 @@
|
||||||
# opens claude-session.env. A bounded config/target query imports the copy only as the stack
|
|
||||||
# user, with sudo and an empty environment. Root parses its two facts in bash. P10 uses the
|
|
||||||
# fixed system Python, also as the stack user, for a read-only SO_PEERCRED observation.
|
|
||||||
+# The config/probe supervisor is fixed root-validated system Bash plus setsid
|
+# The config/probe supervisor is fixed root-validated system Bash plus setsid
|
||||||
+# and sleep, after the uid drop: query inner/outer 5/7 s, probe 2/4 s. Root
|
+# and sleep, after the uid drop: query inner/outer 5/7 s, probe 2/4 s. Root
|
||||||
+# independently bounds its sudo wait. Legacy AS_USER talk and diagnostics below
|
+# independently bounds its sudo wait. Legacy AS_USER talk and diagnostics below
|
||||||
+# retain their old behavior and are NOT covered by this supervisor (F5 follow-up).
|
+# retain their old behavior and are NOT covered by this supervisor (F5 follow-up).
|
||||||
#
|
@@ -110,0 +115,113 @@
|
||||||
# One line per check, `<id> <path> <mark> <evidence>`, then `count <mark> N` for each mark and a
|
|
||||||
# result line. F checks are `pass` or `FAIL`. P checks are `closed`, `accepted <ruling>` or
|
|
||||||
@@ -108,18 +112,141 @@
|
|
||||||
trap 'rm -rf -- "$WORK"' EXIT
|
|
||||||
|
|
||||||
# Row253: bounded unprivileged metadata, parsed by root without running copy code.
|
|
||||||
+# row253-supervisor-begin
|
+# row253-supervisor-begin
|
||||||
+IFS= read -r -d '' TESTER_SUPERVISOR <<'MOSAIC_TESTER_SUPERVISOR' || :
|
+IFS= read -r -d '' TESTER_SUPERVISOR <<'MOSAIC_TESTER_SUPERVISOR' || :
|
||||||
+set +m
|
+set +m
|
||||||
@@ -299,7 +269,7 @@
|
|||||||
+ *) echo inner-command ;;
|
+ *) echo inner-command ;;
|
||||||
+ esac
|
+ esac
|
||||||
+}
|
+}
|
||||||
tester_capture() (
|
@@ -112,5 +229,11 @@
|
||||||
- local seconds="$1" maximum="$2" file child="" rc=0; shift 2
|
- local seconds="$1" maximum="$2" file child="" rc=0; shift 2
|
||||||
- file="$(mktemp)" || exit 1
|
- file="$(mktemp)" || exit 1
|
||||||
- trap 'if [ -n "$child" ]; then kill -KILL -- "-$child" 2>/dev/null || :; wait "$child" 2>/dev/null || :; fi; rm -f -- "$file"' EXIT
|
- trap 'if [ -n "$child" ]; then kill -KILL -- "-$child" 2>/dev/null || :; wait "$child" 2>/dev/null || :; fi; rm -f -- "$file"' EXIT
|
||||||
@@ -316,53 +286,30 @@
|
|||||||
+ maximum="$1"; head_bin="$2"; shift 2
|
+ maximum="$1"; head_bin="$2"; shift 2
|
||||||
+ "$@" 2>/dev/null | "$head_bin" -c "$((maximum + 1))"
|
+ "$@" 2>/dev/null | "$head_bin" -c "$((maximum + 1))"
|
||||||
+ ' -- "$maximum" "$TS_HEAD" sudo -u "$user" -H env -i "HOME=$home" "PATH=$path" "$TS_BASH" -c "$TESTER_SUPERVISOR" -- "$inner" "$TS_SETSID" "$TS_SLEEP" "$@" > "$file" 2> "$notice" &
|
+ ' -- "$maximum" "$TS_HEAD" sudo -u "$user" -H env -i "HOME=$home" "PATH=$path" "$TS_BASH" -c "$TESTER_SUPERVISOR" -- "$inner" "$TS_SETSID" "$TS_SLEEP" "$@" > "$file" 2> "$notice" &
|
||||||
child=$!
|
@@ -120 +243,4 @@
|
||||||
wait "$child" 2>/dev/null || rc=$?
|
|
||||||
kill -KILL -- "-$child" 2>/dev/null || :
|
|
||||||
- [ "$rc" = 0 ] && [ "$(wc -c < "$file")" -le "$maximum" ] || exit 1
|
- [ "$rc" = 0 ] && [ "$(wc -c < "$file")" -le "$maximum" ] || exit 1
|
||||||
+ # Only GNU timeout and the trusted head can write this root-side channel.
|
+ # Only GNU timeout and the trusted head can write this root-side channel.
|
||||||
+ if [ "$rc" = 137 ] && [[ "$(<"$notice")" == *': sending signal KILL to command '* ]]; then exit 82; fi
|
+ if [ "$rc" = 137 ] && [[ "$(<"$notice")" == *': sending signal KILL to command '* ]]; then exit 82; fi
|
||||||
+ [ "$(wc -c < "$file")" -le "$maximum" ] || exit 83
|
+ [ "$(wc -c < "$file")" -le "$maximum" ] || exit 83
|
||||||
+ [ "$rc" = 0 ] || { case "$rc" in 81|85|86|87|88) exit "$rc" ;; *) exit 87 ;; esac; }
|
+ [ "$rc" = 0 ] || { case "$rc" in 81|85|86|87|88) exit "$rc" ;; *) exit 87 ;; esac; }
|
||||||
cat -- "$file"
|
@@ -122,0 +249 @@
|
||||||
)
|
|
||||||
+
|
+
|
||||||
TESTER_QUERY='import {readFileSync} from "node:fs";import {pathToFileURL} from "node:url";const m=await import(pathToFileURL(process.argv[1]).href);const s=m.loadSystem();console.log(JSON.stringify({dataRoot:s.dataRoot,socket:readFileSync(process.argv[2],"utf8").includes("MOSAIC_TESTER_SOCKET_V1")}));'
|
@@ -196 +323 @@
|
||||||
tester_facts() {
|
|
||||||
local LC_ALL=C pattern='^\{"dataRoot":"([A-Za-z0-9/._@+-]+)","socket":(true|false)\}$'
|
|
||||||
@@ -193,7 +320,7 @@
|
|
||||||
MOSAIC_UID="$(uid_of "$S_USER")"
|
|
||||||
[[ "$MOSAIC_UID" =~ ^[1-9][0-9]*$ ]] || refuse "stack account has no non-root uid"
|
|
||||||
[ "$(db passwd "$S_USER" | cut -d: -f6)" = "$S_HOME" ] || refuse "saved home differs from account home"
|
|
||||||
-QUERY="$(tester_capture 5 4096 sudo -u "$S_USER" -H env -i "HOME=$S_HOME" "PATH=$(dirname "$NODE"):/usr/local/bin:/usr/bin:/bin" "$NODE" --input-type=module -e "$TESTER_QUERY" "$COPY/packages/cli/src/config.mjs" "$COPY/packages/webui/src/cli.mjs")" || refuse "tester config query failed"
|
-QUERY="$(tester_capture 5 4096 sudo -u "$S_USER" -H env -i "HOME=$S_HOME" "PATH=$(dirname "$NODE"):/usr/local/bin:/usr/bin:/bin" "$NODE" --input-type=module -e "$TESTER_QUERY" "$COPY/packages/cli/src/config.mjs" "$COPY/packages/webui/src/cli.mjs")" || refuse "tester config query failed"
|
||||||
+QUERY="$(tester_capture 5 7 4096 "$S_USER" "$S_HOME" "$(dirname "$NODE"):/usr/local/bin:/usr/bin:/bin" "$NODE" --input-type=module -e "$TESTER_QUERY" "$COPY/packages/cli/src/config.mjs" "$COPY/packages/webui/src/cli.mjs")" || refuse "tester config query $(tester_capture_reason "$?")"
|
+QUERY="$(tester_capture 5 7 4096 "$S_USER" "$S_HOME" "$(dirname "$NODE"):/usr/local/bin:/usr/bin:/bin" "$NODE" --input-type=module -e "$TESTER_QUERY" "$COPY/packages/cli/src/config.mjs" "$COPY/packages/webui/src/cli.mjs")" || refuse "tester config query $(tester_capture_reason "$?")"
|
||||||
tester_facts "$QUERY" || refuse "tester config query has invalid facts"
|
@@ -536 +663 @@
|
||||||
[ "$TS_MARKED" = true ] || refuse "target does not support --tester-socket"
|
|
||||||
tester_location "$TS_DATA" "$S_HOME" "$MOSAIC_UID" "$ROOT" observe || refuse "tester path refuses $TS_REASON"
|
|
||||||
@@ -533,11 +660,11 @@
|
|
||||||
before="$(tester_snapshot)" || why=metadata
|
|
||||||
python="$(tester_python)" || why=interpreter
|
|
||||||
if [ -z "$why" ]; then
|
|
||||||
- peer="$(tester_capture 3 128 sudo -u "$S_USER" -H env -i "HOME=$S_HOME" "PATH=$(dirname "$NODE"):/usr/local/bin:/usr/bin:/bin" "$python" -I -S -c "$TESTER_PEER" "$TESTER_PATH")" || why=probe
|
- peer="$(tester_capture 3 128 sudo -u "$S_USER" -H env -i "HOME=$S_HOME" "PATH=$(dirname "$NODE"):/usr/local/bin:/usr/bin:/bin" "$python" -I -S -c "$TESTER_PEER" "$TESTER_PATH")" || why=probe
|
||||||
+ peer="$(tester_capture 2 4 128 "$S_USER" "$S_HOME" "$(dirname "$NODE"):/usr/local/bin:/usr/bin:/bin" "$python" -I -S -c "$TESTER_PEER" "$TESTER_PATH")" || why="probe-$(tester_capture_reason "$?")"
|
+ peer="$(tester_capture 2 4 128 "$S_USER" "$S_HOME" "$(dirname "$NODE"):/usr/local/bin:/usr/bin:/bin" "$python" -I -S -c "$TESTER_PEER" "$TESTER_PATH")" || why="probe-$(tester_capture_reason "$?")"
|
||||||
if [[ "$peer" =~ $pattern ]]; then
|
@@ -540 +667 @@
|
||||||
pid="${BASH_REMATCH[1]}" uid="${BASH_REMATCH[2]}" gid="${BASH_REMATCH[3]}"
|
|
||||||
[ "$pid" = "$CON_PID" ] && [ "$uid" = "$MOSAIC_UID" ] && [ "$gid" -le 2147483647 ] || why=peer
|
|
||||||
- else why=probe; fi
|
- else why=probe; fi
|
||||||
+ else [ -n "$why" ] || why=probe; fi
|
+ else [ -n "$why" ] || why=probe; fi
|
||||||
after="$(tester_snapshot)" || why=metadata
|
|
||||||
[ "$before" = "$after" ] || why=changed
|
|
||||||
fi
|
|
||||||
--- a/packages/cli/tests/case-sudo-fixture.mjs
|
--- a/packages/cli/tests/case-sudo-fixture.mjs
|
||||||
+++ b/packages/cli/tests/case-sudo-fixture.mjs
|
+++ b/packages/cli/tests/case-sudo-fixture.mjs
|
||||||
@@ -1,22 +1,34 @@
|
@@ -3,0 +4,3 @@
|
||||||
// Shared fake process model for the two Case host-script tests. No real sudo.
|
|
||||||
// Call case_sudo_run RECORD_DIR COMMAND... after the caller's argv checks.
|
|
||||||
// Missing session-mode preserves the original exec-style fake.
|
|
||||||
+import { spawn } from 'node:child_process';
|
+import { spawn } from 'node:child_process';
|
||||||
+import { readFileSync, readdirSync } from 'node:fs';
|
+import { readFileSync, readdirSync } from 'node:fs';
|
||||||
+
|
+
|
||||||
export const CASE_SUDO_SESSION = String.raw`
|
@@ -4,0 +8,13 @@
|
||||||
+case_sudo_entry() {
|
+case_sudo_entry() {
|
||||||
+ local record="$1" here sid; shift
|
+ local record="$1" here sid; shift
|
||||||
+ here="$BASHPID"
|
+ here="$BASHPID"
|
||||||
@@ -376,17 +323,13 @@
|
|||||||
+ fi
|
+ fi
|
||||||
+ exec "$@"
|
+ exec "$@"
|
||||||
+}
|
+}
|
||||||
case_sudo_run() {
|
@@ -6,2 +22,3 @@
|
||||||
- local record="$1" child="" rc=0; shift
|
- local record="$1" child="" rc=0; shift
|
||||||
- if [ "$(cat "$record/session-mode" 2>/dev/null)" != hop ]; then exec "$@"; fi
|
- if [ "$(cat "$record/session-mode" 2>/dev/null)" != hop ]; then exec "$@"; fi
|
||||||
+ local record="$1" child="" rc=0 mode=exec; shift
|
+ local record="$1" child="" rc=0 mode=exec; shift
|
||||||
+ [ ! -f "$record/session-mode" ] || mode="$(<"$record/session-mode")"
|
+ [ ! -f "$record/session-mode" ] || mode="$(<"$record/session-mode")"
|
||||||
+ if [ "$mode" != hop ]; then case_sudo_entry "$record" "$@"; return "$?"; fi
|
+ if [ "$mode" != hop ]; then case_sudo_entry "$record" "$@"; return "$?"; fi
|
||||||
# The fake stays as parent, just as sudo does on its pty path. KILL cannot
|
@@ -13,7 +30,2 @@
|
||||||
# be caught or forwarded. The child creates the separate terminal session.
|
|
||||||
trap '[ -z "$child" ] || kill -TERM -- "-$child" 2>/dev/null || :' TERM
|
|
||||||
trap '[ -z "$child" ] || kill -HUP -- "-$child" 2>/dev/null || :' HUP
|
|
||||||
trap '[ -z "$child" ] || kill -INT -- "-$child" 2>/dev/null || :' INT
|
|
||||||
- /usr/bin/setsid /bin/bash -c '
|
- /usr/bin/setsid /bin/bash -c '
|
||||||
- record="$1"; shift
|
- record="$1"; shift
|
||||||
- here="$BASHPID"
|
- here="$BASHPID"
|
||||||
@@ -396,24 +339,12 @@
|
|||||||
- ' -- "$record" "$@" &
|
- ' -- "$record" "$@" &
|
||||||
+ export -f case_sudo_entry
|
+ export -f case_sudo_entry
|
||||||
+ /usr/bin/setsid /bin/bash -c 'case_sudo_entry "$@"' -- "$record" "$@" &
|
+ /usr/bin/setsid /bin/bash -c 'case_sudo_entry "$@"' -- "$record" "$@" &
|
||||||
child=$!
|
@@ -31 +43,3 @@
|
||||||
# An interrupted wait returns while the child still runs; keep waiting.
|
|
||||||
while :; do
|
|
||||||
@@ -28,7 +40,9 @@
|
|
||||||
`;
|
|
||||||
|
|
||||||
export function liveRecordedSessions(recordFile, readFile, procDirs) {
|
|
||||||
- const sessions = new Set(readFile(recordFile).trim().split('\n').filter(Boolean).map(line=>Number(line.trim().split(/\s+/)[1])));
|
- const sessions = new Set(readFile(recordFile).trim().split('\n').filter(Boolean).map(line=>Number(line.trim().split(/\s+/)[1])));
|
||||||
+ // Exec fake entries share the test runner's session. Only self-led sessions
|
+ // Exec fake entries share the test runner's session. Only self-led sessions
|
||||||
+ // are isolated and safe to scan here; exec coverage also checks exact PIDs.
|
+ // are isolated and safe to scan here; exec coverage also checks exact PIDs.
|
||||||
+ const sessions = new Set(readFile(recordFile).trim().split('\n').filter(Boolean).map(line=>line.trim().split(/\s+/).map(Number)).filter(([pid,sid])=>pid===sid).map(([,sid])=>sid));
|
+ const sessions = new Set(readFile(recordFile).trim().split('\n').filter(Boolean).map(line=>line.trim().split(/\s+/).map(Number)).filter(([pid,sid])=>pid===sid).map(([,sid])=>sid));
|
||||||
const live=[];
|
@@ -41,0 +56,87 @@
|
||||||
for(const entry of procDirs()){
|
|
||||||
if(!/^\d+$/.test(entry))continue;
|
|
||||||
@@ -39,3 +53,90 @@
|
|
||||||
}
|
|
||||||
return live;
|
|
||||||
}
|
|
||||||
+
|
+
|
||||||
+export function recordedPids(file) {
|
+export function recordedPids(file) {
|
||||||
+ try{return readFileSync(file,'utf8').trim().split('\n').filter(Boolean).map(line=>line.trim().split(/\s+/).map(Number));}
|
+ try{return readFileSync(file,'utf8').trim().split('\n').filter(Boolean).map(line=>line.trim().split(/\s+/).map(Number));}
|
||||||
@@ -503,38 +434,23 @@
|
|||||||
+}
|
+}
|
||||||
--- a/packages/cli/tests/case2-unit.test.mjs
|
--- a/packages/cli/tests/case2-unit.test.mjs
|
||||||
+++ b/packages/cli/tests/case2-unit.test.mjs
|
+++ b/packages/cli/tests/case2-unit.test.mjs
|
||||||
@@ -13,7 +13,7 @@
|
@@ -16 +16 @@
|
||||||
import { businessDoc, writeJson } from "../../business/tests/helpers.mjs";
|
|
||||||
import { binds, prepareRun, sandboxArgv, sandboxProblem } from "../../seat/src/sandbox.mjs";
|
|
||||||
import { tmp } from "./helpers.mjs";
|
|
||||||
-import { CASE_SUDO_SESSION, liveRecordedSessions } from "./case-sudo-fixture.mjs";
|
-import { CASE_SUDO_SESSION, liveRecordedSessions } from "./case-sudo-fixture.mjs";
|
||||||
+import { CASE_SUDO_SESSION, recordedLivePids, killRecordedPids, fixtureGuard, cleanupFixture, pollFixtureGone, blockingQuerySource } from "./case-sudo-fixture.mjs";
|
+import { CASE_SUDO_SESSION, recordedLivePids, killRecordedPids, fixtureGuard, cleanupFixture, pollFixtureGone, blockingQuerySource } from "./case-sudo-fixture.mjs";
|
||||||
|
@@ -96 +96 @@
|
||||||
const REPO = fileURLToPath(new URL("../../../", import.meta.url));
|
|
||||||
const UNIT = join(REPO, "scripts", "case2-unit.sh");
|
|
||||||
@@ -93,7 +93,7 @@
|
|
||||||
echo "$BASHPID" >>'${root}/query-pids'; wait; exit
|
|
||||||
fi
|
|
||||||
if [ -f '${root}/query-orphan' ]; then
|
|
||||||
- (trap '' TERM; exec sleep 60) </dev/null >/dev/null 2>&1 &
|
- (trap '' TERM; exec sleep 60) </dev/null >/dev/null 2>&1 &
|
||||||
+ (trap '' TERM; here="$BASHPID"; read -r sid < <(/bin/ps -o sid= -p "$here"); printf '%s %s\\n' "$here" "$sid" >'${root}/query-owned'; exec sleep 60) </dev/null >/dev/null 2>&1 &
|
+ (trap '' TERM; here="$BASHPID"; read -r sid < <(/bin/ps -o sid= -p "$here"); printf '%s %s\\n' "$here" "$sid" >'${root}/query-owned'; exec sleep 60) </dev/null >/dev/null 2>&1 &
|
||||||
echo "$!" >'${root}/query-pids'
|
@@ -189,2 +189,4 @@
|
||||||
cat '${root}/query-output'; exit 0
|
|
||||||
fi
|
|
||||||
@@ -186,41 +186,62 @@
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
-// Draft counterexample for DESIGN-C4. Written after sage-2307, not run.
|
-// Draft counterexample for DESIGN-C4. Written after sage-2307, not run.
|
||||||
-test('row253 C4 query cleanup handles both exec and sudo session-hop fakes', async t=>{
|
-test('row253 C4 query cleanup handles both exec and sudo session-hop fakes', async t=>{
|
||||||
+// C4 cases run through both sudo process models. Exec shares the runner's
|
+// C4 cases run through both sudo process models. Exec shares the runner's
|
||||||
+// session, so its session-wide check would be vacuous; exact recorded PIDs
|
+// session, so its session-wide check would be vacuous; exact recorded PIDs
|
||||||
+// are checked in both modes. Hop additionally isolates the fake session.
|
+// are checked in both modes. Hop additionally isolates the fake session.
|
||||||
+for(const kind of ['sleep','signals','member']) test(`row253 C4 ${kind} query has exact inner reason and cleans all members`,async t=>{
|
+for(const kind of ['sleep','signals','member']) test(`row253 C4 ${kind} query has exact inner reason and cleans all members`,async t=>{
|
||||||
for(const mode of ['exec','hop']){
|
@@ -192 +194 @@
|
||||||
- const h=hub(t),pids=join(h.root,'session-pids'),queryPids=join(h.root,'query-pids'),copy=join(copyDir(h),'packages/cli/src/config.mjs');
|
- const h=hub(t),pids=join(h.root,'session-pids'),queryPids=join(h.root,'query-pids'),copy=join(copyDir(h),'packages/cli/src/config.mjs');
|
||||||
+ const h=hub(t),sessions=join(h.root,'session-pids'),queries=join(h.root,'query-pids'),files=[sessions,queries];
|
+ const h=hub(t),sessions=join(h.root,'session-pids'),queries=join(h.root,'query-pids'),files=[sessions,queries];
|
||||||
writeFileSync(join(h.root,'session-mode'),mode);
|
@@ -194,21 +196,10 @@
|
||||||
- writeFileSync(pids,'');
|
- writeFileSync(pids,'');
|
||||||
- writeFileSync(copy,`import fs from 'node:fs';process.on('SIGTERM',()=>{});process.on('SIGHUP',()=>{});fs.appendFileSync(${JSON.stringify(queryPids)},process.pid+'\\n');const s=fs.readFileSync('/proc/self/stat','utf8');const sid=Number(s.slice(s.lastIndexOf(') ')+2).split(' ')[3]);if(sid===process.pid)fs.appendFileSync(${JSON.stringify(pids)},process.pid+' '+sid+'\\n');export function loadSystem(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0);}`);
|
- writeFileSync(copy,`import fs from 'node:fs';process.on('SIGTERM',()=>{});process.on('SIGHUP',()=>{});fs.appendFileSync(${JSON.stringify(queryPids)},process.pid+'\\n');const s=fs.readFileSync('/proc/self/stat','utf8');const sid=Number(s.slice(s.lastIndexOf(') ')+2).split(' ')[3]);if(sid===process.pid)fs.appendFileSync(${JSON.stringify(pids)},process.pid+' '+sid+'\\n');export function loadSystem(){Atomics.wait(new Int32Array(new SharedArrayBuffer(4)),0,0);}`);
|
||||||
- t.after(()=>{
|
- t.after(()=>{
|
||||||
@@ -566,7 +482,7 @@
|
|||||||
+ assert.deepEqual(await pollFixtureGone(files),[],mode);
|
+ assert.deepEqual(await pollFixtureGone(files),[],mode);
|
||||||
+ if(kind==='member')assert.ok(read(proof).includes('('+member.split('/').at(-1)+') '),'member comm with newline and closing parenthesis was observed');
|
+ if(kind==='member')assert.ok(read(proof).includes('('+member.split('/').at(-1)+') '),'member comm with newline and closing parenthesis was observed');
|
||||||
+ assert.equal(existsSync(h.log),false,mode);await stopGuard();
|
+ assert.equal(existsSync(h.log),false,mode);await stopGuard();
|
||||||
}
|
@@ -215,0 +207,30 @@
|
||||||
+});
|
+});
|
||||||
+
|
+
|
||||||
+test('row253 C4 overflow is output-limit in both sudo modes',async t=>{
|
+test('row253 C4 overflow is output-limit in both sudo modes',async t=>{
|
||||||
@@ -597,77 +513,36 @@
|
|||||||
+ const extract=s=>s.split('# row253-supervisor-begin\n')[1]?.split('# row253-supervisor-end')[0];
|
+ const extract=s=>s.split('# row253-supervisor-begin\n')[1]?.split('# row253-supervisor-end')[0];
|
||||||
+ const a=extract(read(UNIT)),b=extract(read(join(REPO,'scripts/case1-accept.sh')));
|
+ const a=extract(read(UNIT)),b=extract(read(join(REPO,'scripts/case1-accept.sh')));
|
||||||
+ assert.ok(a?.length>100);assert.equal(a,b);
|
+ assert.ok(a?.length>100);assert.equal(a,b);
|
||||||
});
|
@@ -222,2 +243,2 @@
|
||||||
|
|
||||||
test('row253 query reaps its group even when a child closes stdout before the query returns',async t=>{
|
|
||||||
const h=hub(t);writeFileSync(join(h.root,'query-orphan'),'');
|
|
||||||
writeFileSync(join(h.root,'query-output'),JSON.stringify({dataRoot:h.dataRoot,socket:true}));
|
|
||||||
let pid;
|
|
||||||
- t.after(()=>{if(pid)try{process.kill(pid,'SIGKILL');}catch{}});
|
- t.after(()=>{if(pid)try{process.kill(pid,'SIGKILL');}catch{}});
|
||||||
- const r=unit(h,{profile:'factory'});
|
- const r=unit(h,{profile:'factory'});
|
||||||
+ const capture=cleanupFixture(t,[join(h.root,'query-owned')]);
|
+ const capture=cleanupFixture(t,[join(h.root,'query-owned')]);
|
||||||
+ const r=unit(h,{profile:'factory'});capture();
|
+ const r=unit(h,{profile:'factory'});capture();
|
||||||
pid=Number(read(join(h.root,'query-pids')).trim());
|
@@ -235,2 +256,2 @@
|
||||||
assert.equal(r.code,0,r.out+r.err);
|
|
||||||
let state='gone';
|
|
||||||
@@ -232,8 +253,8 @@
|
|
||||||
assert.ok(state==='gone'||state==='Z',`query descendant ${pid} still ${state}`);
|
|
||||||
});
|
|
||||||
|
|
||||||
-test('row253 config parser is strict and output capture is bounded',()=>{
|
-test('row253 config parser is strict and output capture is bounded',()=>{
|
||||||
- const source=read(UNIT), body=source.slice(source.indexOf('tester_capture() ('),source.indexOf('tester_location() {'));
|
- const source=read(UNIT), body=source.slice(source.indexOf('tester_capture() ('),source.indexOf('tester_location() {'));
|
||||||
+test('row253 config parser is strict',()=>{
|
+test('row253 config parser is strict',()=>{
|
||||||
+ const source=read(UNIT), body=source.slice(source.indexOf('tester_facts() {'),source.indexOf('tester_location() {'));
|
+ const source=read(UNIT), body=source.slice(source.indexOf('tester_facts() {'),source.indexOf('tester_location() {'));
|
||||||
const valid=spawnSync('/bin/bash',['-c',body+'\ntester_facts "$1" && printf "%s %s" "$TS_DATA" "$TS_MARKED"','--','{"dataRoot":"/srv/mosaic/data","socket":true}'],{encoding:'utf8'});
|
@@ -244,2 +264,0 @@
|
||||||
assert.equal(valid.status,0);assert.equal(valid.stdout,'/srv/mosaic/data true');
|
|
||||||
const facts=['{"dataRoot":"/srv/mosaic/data","socket":true,"extra":1}','{"dataRoot":4,"socket":true}','{"dataRoot":"/srv/mosaic/data","socket":"true"}','{"dataRoot":"/srv/mosaic/data","socket":true}\nP10 forged closed'];
|
|
||||||
@@ -241,8 +262,6 @@
|
|
||||||
const r=spawnSync('/bin/bash',['-c',body+'\ntester_facts "$1"','--',fact]);
|
|
||||||
assert.notEqual(r.status,0,fact);
|
|
||||||
}
|
|
||||||
- const r=spawnSync('/bin/bash',['-c',body+'\ntester_capture 1 32 /usr/bin/head -c 100 /dev/zero']);
|
- const r=spawnSync('/bin/bash',['-c',body+'\ntester_capture 1 32 /usr/bin/head -c 100 /dev/zero']);
|
||||||
- assert.notEqual(r.status,0);assert.equal(r.stdout.length,0);
|
- assert.notEqual(r.status,0);assert.equal(r.stdout.length,0);
|
||||||
});
|
|
||||||
|
|
||||||
function refuses(r, pattern) {
|
|
||||||
--- a/packages/cli/tests/case1-accept.test.mjs
|
--- a/packages/cli/tests/case1-accept.test.mjs
|
||||||
+++ b/packages/cli/tests/case1-accept.test.mjs
|
+++ b/packages/cli/tests/case1-accept.test.mjs
|
||||||
@@ -11,7 +11,7 @@
|
@@ -14 +14 @@
|
||||||
import { dirname, join } from "node:path";
|
|
||||||
import { fileURLToPath } from "node:url";
|
|
||||||
import { tmp } from "./helpers.mjs";
|
|
||||||
-import { CASE_SUDO_SESSION } from "./case-sudo-fixture.mjs";
|
-import { CASE_SUDO_SESSION } from "./case-sudo-fixture.mjs";
|
||||||
+import { CASE_SUDO_SESSION, recordedLivePids, killRecordedPids, fixtureGuard, cleanupFixture, pollFixtureGone, blockingQuerySource } from "./case-sudo-fixture.mjs";
|
+import { CASE_SUDO_SESSION, recordedLivePids, killRecordedPids, fixtureGuard, cleanupFixture, pollFixtureGone, blockingQuerySource } from "./case-sudo-fixture.mjs";
|
||||||
|
@@ -90 +90,2 @@
|
||||||
const REPO = fileURLToPath(new URL("../../../", import.meta.url));
|
|
||||||
const SCRIPT = join(REPO, "scripts", "case1-accept.sh");
|
|
||||||
@@ -87,7 +87,8 @@
|
|
||||||
cmd="$1"; shift
|
|
||||||
args=(); for a in "$@"; do if [[ "$a" == /* ]] && [ -e "$ROOT_DIR$a" ]; then args+=("$ROOT_DIR$a"); else args+=("$a"); fi; done
|
|
||||||
export HOME="$ROOT_DIR/srv/mosaic" PATH="$np$PATH"
|
|
||||||
-case_sudo_run "$HC" "$ROOT_DIR$cmd" "\${args[@]}"`,
|
-case_sudo_run "$HC" "$ROOT_DIR$cmd" "\${args[@]}"`,
|
||||||
+[ ! -e "$ROOT_DIR$cmd" ] || cmd="$ROOT_DIR$cmd"
|
+[ ! -e "$ROOT_DIR$cmd" ] || cmd="$ROOT_DIR$cmd"
|
||||||
+case_sudo_run "$HC" "$cmd" "\${args[@]}"`,
|
+case_sudo_run "$HC" "$cmd" "\${args[@]}"`,
|
||||||
};
|
@@ -100 +101,5 @@
|
||||||
|
|
||||||
// Executables inside the tree, run by the fake sudo or by the script itself. Node marks a run outside
|
|
||||||
@@ -97,7 +98,11 @@
|
|
||||||
exec "$REAL_NODE" "$@"`;
|
|
||||||
// The real Python payload runs against a mock socket object, never an OS listener.
|
|
||||||
const PYTHON_STUB = `[ -f "$HC/peer-output" ] && { cat "$HC/peer-output"; exit "$(cat "$HC/peer-status")"; }
|
|
||||||
-[ -f "$HC/peer-hang" ] && { echo "$BASHPID" > "$HC/peer-pid"; exec sleep 60; }
|
-[ -f "$HC/peer-hang" ] && { echo "$BASHPID" > "$HC/peer-pid"; exec sleep 60; }
|
||||||
+[ -f "$HC/peer-hang" ] && {
|
+[ -f "$HC/peer-hang" ] && {
|
||||||
+ here="$BASHPID"; read -r sid < <(/bin/ps -o sid= -p "$here")
|
+ here="$BASHPID"; read -r sid < <(/bin/ps -o sid= -p "$here")
|
||||||
+ echo "$here" > "$HC/peer-pid"; printf '%s %s\\n' "$here" "$sid" >> "$HC/query-pids"
|
+ echo "$here" > "$HC/peer-pid"; printf '%s %s\\n' "$here" "$sid" >> "$HC/query-pids"
|
||||||
+ trap '' TERM HUP; exec sleep 60
|
+ trap '' TERM HUP; exec sleep 60
|
||||||
+}
|
+}
|
||||||
if [ -f "$HC/peer-change" ]; then cat "$HC/peer-change" > "$HC/stat-socket"; fi
|
@@ -303,0 +309,58 @@
|
||||||
if [ -f "$HC/peer-pid-change" ]; then cat "$HC/peer-pid-change" > "$HC/show-mosaic-webui.service"; fi
|
|
||||||
exec /usr/bin/python3 -I -S -c '
|
|
||||||
@@ -301,6 +306,64 @@
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
+test('row253 C4 acceptance query inner reason holds under both sudo models',async t=>{
|
+test('row253 C4 acceptance query inner reason holds under both sudo models',async t=>{
|
||||||
+ for(const mode of ['exec','hop']){
|
+ for(const mode of ['exec','hop']){
|
||||||
+ const h=host(t),files=[join(h.hc,'session-pids'),join(h.hc,'query-pids')];
|
+ const h=host(t),files=[join(h.hc,'session-pids'),join(h.hc,'query-pids')];
|
||||||
@@ -726,47 +601,23 @@
|
|||||||
+ }
|
+ }
|
||||||
+});
|
+});
|
||||||
+
|
+
|
||||||
// One fault per F check; each makes that check, and only that one, fail.
|
|
||||||
const F_FAULTS = {
|
|
||||||
F1: (h) => proc(h.root, 100, { ppid: 1, uid: 0, exe: NODE, comm: "node" }),
|
|
||||||
--- a/packages/webui/README.md
|
--- a/packages/webui/README.md
|
||||||
+++ b/packages/webui/README.md
|
+++ b/packages/webui/README.md
|
||||||
@@ -125,9 +125,9 @@
|
@@ -128 +128 @@
|
||||||
With `--instance NAME` (row 190) every verb works on
|
|
||||||
`mosaic-webui-NAME.service`, which sets NAME's `MOSAIC_CONFIG`; install
|
|
||||||
checks NAME's `mosaic-bus-NAME@` units for the copy. render and install then
|
|
||||||
-need `--port`, and 7330, 7332 or 7333 as `--port` or `--tester-port` exits 4
|
-need `--port`, and 7330, 7332 or 7333 as `--port` or `--tester-port` exits 4
|
||||||
+need `--port`, and 7330, 7332 or 7333 as the main or legacy tester port exits 4
|
+need `--port`, and 7330, 7332 or 7333 as the main or legacy tester port exits 4
|
||||||
with port-reserved. For every instance, install refuses a `--port` or
|
@@ -130 +130 @@
|
||||||
-`--tester-port` that another installed Console unit in the unit directory
|
-`--tester-port` that another installed Console unit in the unit directory
|
||||||
+legacy tester port that another installed Console unit in the unit directory
|
+legacy tester port that another installed Console unit in the unit directory
|
||||||
already takes. That refusal is port-claimed, exit 1, and writes nothing. A
|
@@ -144 +144 @@
|
||||||
unit with no `--port` holds 7332. See
|
|
||||||
[TOOLS.md, Stack instances](../../docs/TOOLS.md#stack-instances---instance-name-row-190).
|
|
||||||
@@ -141,7 +141,7 @@
|
|
||||||
|
|
||||||
## Sign-in in the Console (Q175 phase 1, #1690)
|
|
||||||
|
|
||||||
-`serve --sso [--send] [--tester-port N]` makes the Console an OpenID Connect
|
-`serve --sso [--send] [--tester-port N]` makes the Console an OpenID Connect
|
||||||
+`serve --sso [--send] [--tester-socket]` makes the Console an OpenID Connect
|
+`serve --sso [--send] [--tester-socket]` makes the Console an OpenID Connect
|
||||||
relying party. It reads `console.sso` from the system config (`publicOrigin`
|
@@ -176 +176 @@
|
||||||
and one or more providers, each with its issuer, client id, a client secret
|
|
||||||
slot, the signing algorithms it accepts and the subjects allowed in). The
|
|
||||||
@@ -173,7 +173,7 @@
|
|
||||||
- The page shows Sign out only under `--sso`. It posts to `/auth/logout`, which
|
|
||||||
ends the session and goes to the provider's end-session endpoint when it has
|
|
||||||
one, or to `/auth/signed-out`.
|
|
||||||
-- With `--tester-port`, the tester listener refuses any request carrying an
|
-- With `--tester-port`, the tester listener refuses any request carrying an
|
||||||
+- With `--tester-socket`, the tester listener refuses any request carrying an
|
+- With `--tester-socket`, the tester listener refuses any request carrying an
|
||||||
`Authorization` header, a `__Host-mosaic_session` or `__Host-mosaic_login`
|
|
||||||
cookie, or an `x-authentik-*` header (403 `tester listener takes no
|
|
||||||
sign-in`). It never serves `/auth/*`.
|
|
||||||
--- a/docs/plans/DEFERRED.md
|
--- a/docs/plans/DEFERRED.md
|
||||||
+++ b/docs/plans/DEFERRED.md
|
+++ b/docs/plans/DEFERRED.md
|
||||||
@@ -270,20 +270,6 @@
|
@@ -273,14 +272,0 @@
|
||||||
(`:1538`, `:1689`, `:1691`). For a later row that edits
|
|
||||||
`stack-release.sh`. Webby N1 on row 238 r1 (REVIEW-r1.md, 433a95292,
|
|
||||||
#1744). (2026-10-21)
|
|
||||||
-- **Gate runs mount the host's pi auth file.** `compose.yaml:43` mounts
|
-- **Gate runs mount the host's pi auth file.** `compose.yaml:43` mounts
|
||||||
- `${PI_AUTH_FILE:-/home/jwoltje/.pi/agent/auth.json}` read-only into
|
- `${PI_AUTH_FILE:-/home/jwoltje/.pi/agent/auth.json}` read-only into
|
||||||
- the agent container. No suite sets `PI_AUTH_FILE`, so the
|
- the agent container. No suite sets `PI_AUTH_FILE`, so the
|
||||||
@@ -781,13 +632,7 @@
|
|||||||
- the lane's `cleanEnv`, and a test that no gate step mounts the host
|
- the lane's `cleanEnv`, and a test that no gate step mounts the host
|
||||||
- default. It needs its own row. Webby N4 on the row 197 proof driver
|
- default. It needs its own row. Webby N4 on the row 197 proof driver
|
||||||
- (bba870ee9), traced by Herb (herb-0051). (2026-10-21)
|
- (bba870ee9), traced by Herb (herb-0051). (2026-10-21)
|
||||||
- **Lane image prep inspects a base that BuildKit never tagged.**
|
@@ -362,0 +349,34 @@
|
||||||
(N7 of the row 197 proof.) After the Compose build,
|
|
||||||
`scripts/testing/lane-image.mjs:41` (`prepareImage`) runs
|
|
||||||
@@ -360,6 +346,40 @@
|
|
||||||
do nothing yet. The row that adds the Console library routes decides
|
|
||||||
whether the tester listener gets `library.sync`. Duckworth's view is
|
|
||||||
that it shouldn't, because sync is a human write. (2026-10-21)
|
|
||||||
+- **Console library routes: four library codes still map to 502.**
|
+- **Console library routes: four library codes still map to 502.**
|
||||||
+ (Dewey N2 on the row 254 manifest, #1759 comment 30173.) Row 254
|
+ (Dewey N2 on the row 254 manifest, #1759 comment 30173.) Row 254
|
||||||
+ gave six library codes a STATUS entry in `packages/webui/src/bus.mjs`.
|
+ gave six library codes a STATUS entry in `packages/webui/src/bus.mjs`.
|
||||||
@@ -822,13 +667,7 @@
|
|||||||
+ credential and a real `release.sh activate`, and records both results on
|
+ credential and a real `release.sh activate`, and records both results on
|
||||||
+ the re-pin row. Morgana's wording (row 244 BUILD.md), Webby N2 on row 244
|
+ the re-pin row. Morgana's wording (row 244 BUILD.md), Webby N2 on row 244
|
||||||
+ r1 (#1757). (2026-10-21)
|
+ r1 (#1757). (2026-10-21)
|
||||||
|
@@ -427,0 +448,14 @@
|
||||||
## Queue
|
|
||||||
|
|
||||||
@@ -425,3 +445,17 @@
|
|
||||||
at a time (Filbert F2; Darkwing and Filbert T1 on #1522;
|
|
||||||
flows.test.mjs).
|
|
||||||
The frozen-cgroup test (Filbert F1) stays open above.
|
|
||||||
+- Gate runs mount the host's pi auth file (Webby N4 on the row 197 proof
|
+- Gate runs mount the host's pi auth file (Webby N4 on the row 197 proof
|
||||||
+ driver, 059577886): closed by row 244 (#1757), landing e69260a3c. The
|
+ driver, 059577886): closed by row 244 (#1757), landing e69260a3c. The
|
||||||
+ suites and the lane's `cleanEnv` export a 0600 fixture `PI_AUTH_FILE`,
|
+ suites and the lane's `cleanEnv` export a 0600 fixture `PI_AUTH_FILE`,
|
||||||
|
|||||||
Reference in new issue
Block a user