feat(runs): read-only runs and releases reader module (#1545, row 56)
New packages/runs reads run records, result.json, the release pointer
and the activation log without writing, pruning or following a link out
of the data root. mosaic-task.mjs list and show use it, so a malformed
result.json or a run that is a regular file no longer crashes them.
Deliberate deltas are listed in the README.
Rocko built it. Round 1 (ed3c5392) was approved with notes by Darkwing
(27115) and Filbert (27116); round 2 (2727198f, tests and wording only)
was approved by Darkwing (27123) and Filbert (27124). Sage's gate on
c9a25a47 plus the candidate: runs 41/0, queue 148/0, webui 22/0,
conversation 182/0 (181/1 in the full run on the K12 cgroup timing
test under load, 182/0 alone), control-board 124/0, every
scripts/test-*.sh 0 failed (task 98/0 with Docker, 26/0 without).
Co-Authored-By: Claude Opus 5.5 <[email protected]>
This commit is contained in:
Executable
+66
@@ -0,0 +1,66 @@
|
||||
#!/usr/bin/env bash
|
||||
# Row 56 byte-identity check. Runs `mosaic-task.mjs list` and `show` from a
|
||||
# baseline tree and a candidate tree against the same seeded data roots and
|
||||
# compares stdout, stderr and exit codes byte for byte. It also checks that
|
||||
# neither tree changed the data root.
|
||||
# Usage: byte-check.sh BASE_TREE CAND_TREE WORK_DIR (WORK_DIR must not exist)
|
||||
set -euo pipefail
|
||||
BASE="$(cd "$1" && pwd)"; CAND="$(cd "$2" && pwd)"; W="$3"
|
||||
HERE="$(cd "$(dirname "$0")" && pwd)"
|
||||
[ ! -e "$W" ] || { echo "byte-check: $W exists" >&2; exit 4; }
|
||||
mkdir -p "$W/out"; W="$(cd "$W" && pwd)"
|
||||
"$HERE/seed.sh" "$W/data"
|
||||
mkdir -p "$W/empty"
|
||||
conf() { printf '{"configVersion":1,"environment":"development","dataRoot":"%s","execution":{"backend":"docker","provider":"zai","model":"m"}}\n' "$2" > "$W/$1.json"; }
|
||||
conf seeded "$W/data"; conf empty "$W/empty"; conf absent "$W/no-such-root"
|
||||
|
||||
snapshot() { (cd "$W/data" && find . -printf '%p %y %s %T@ %l\n' | sort && find . -type f -print0 | sort -z | xargs -0 sha256sum); }
|
||||
snapshot > "$W/before.txt"
|
||||
|
||||
CASES=(
|
||||
"seeded list"
|
||||
"empty list"
|
||||
"absent list"
|
||||
"seeded show r-20260101T000000Z-aaaaaa"
|
||||
"seeded show r-20260101T000100Z-bbbbbb"
|
||||
"seeded show r-20260101T000200Z-cccccc"
|
||||
"seeded show r-20260101T000300Z-dddddd"
|
||||
"seeded show r-20260101T000400Z-eeeeee"
|
||||
"seeded show r-20260101T000500Z-ffffff"
|
||||
"seeded show r-20260101T000700Z-hhhhhh"
|
||||
"seeded show r-20260101T000800Z-iiiiii"
|
||||
"seeded show r-zz.weird_name-1"
|
||||
"seeded show r-missing"
|
||||
"seeded show ../escape"
|
||||
"seeded show"
|
||||
"absent show r-20260101T000000Z-aaaaaa"
|
||||
)
|
||||
for tree in base cand; do
|
||||
root="$BASE"; [ "$tree" = cand ] && root="$CAND"
|
||||
i=0
|
||||
for c in "${CASES[@]}"; do
|
||||
i=$((i + 1))
|
||||
read -r cfg op arg <<<"$c"
|
||||
o="$W/out/$tree/$(printf '%02d' "$i")"
|
||||
mkdir -p "$o"
|
||||
printf '%s\n' "$c" > "$o/case"
|
||||
rc=0
|
||||
(cd "$root" && env -u NODE_OPTIONS MOSAIC_CONFIG="$W/$cfg.json" node scripts/mosaic-task.mjs "$op" ${arg:+"$arg"} >"$o/stdout" 2>"$o/stderr") || rc=$?
|
||||
printf '%s\n' "$rc" > "$o/exit"
|
||||
[ -f "$o/stdout" ] && [ -f "$o/stderr" ] || { echo "byte-check: case $i ($c) left no output in $o" >&2; exit 4; }
|
||||
done
|
||||
done
|
||||
snapshot > "$W/after.txt"
|
||||
|
||||
status=0
|
||||
if diff -r "$W/out/base" "$W/out/cand" > "$W/diff.txt"; then
|
||||
echo "byte-check: ${#CASES[@]} cases, stdout, stderr and exit identical"
|
||||
else
|
||||
echo "byte-check: DIFFERENCES (see $W/diff.txt)"; status=1
|
||||
fi
|
||||
if cmp -s "$W/before.txt" "$W/after.txt"; then
|
||||
echo "byte-check: data root unchanged"
|
||||
else
|
||||
echo "byte-check: DATA ROOT CHANGED"; status=1
|
||||
fi
|
||||
exit "$status"
|
||||
@@ -0,0 +1,15 @@
|
||||
1afe07062349b097b955f0391bffb839b083492d02644421def6c39cc19ff4a4 agents/rocko/work/queue-56/byte-check.sh
|
||||
09788e902a54207ef5f94de1c8b7f2f31c049fb028fa68847115da83060d90f9 agents/rocko/work/queue-56/delta-check.sh
|
||||
15d575cc45313906114f7edda9379b66af7624d9e894bb98812deb7acf5d87ba agents/rocko/work/queue-56/seed.sh
|
||||
146b236a37bf665979e28f1794c4c4437b6e85feb43b892e8d9d697506fc453f packages/runs/package.json
|
||||
895dd393a43221ffb12a5dc1b76ab61118b5562a5d7acbc50450ebb3ea6388d3 packages/runs/README.md
|
||||
f686212fa10e8541a1d8055fe30d65558a516245276aa53ecefd97bd7ca0a20c packages/runs/src/errors.mjs
|
||||
eb7063a8ee0417699b45f4bfec69c3ffcde8b3b276956ca4fea266ec0abd6b23 packages/runs/src/index.mjs
|
||||
3f9d025f0615a08edd440bb2aa6cea48f21cafa1f680332fffca3c7ee586cc65 packages/runs/src/paths.mjs
|
||||
ba64e50c3a63e693fc313278ff6cf440234649a6719dd9229bce20272a518ec1 packages/runs/src/runs.mjs
|
||||
ae546df81c6da7ef51680e23a12c01f08c5a6dc1b68cdf26c8d4210772149940 packages/runs/src/state.mjs
|
||||
7c941596020b14d8627ccb09b66e0e51195318e86add60eba9d2c4409e6f8dd6 packages/runs/tests/helpers.mjs
|
||||
fbaa092b5c8a1b54bc52c090da71f0da4313b4a8221fd7a2e89404a2a1b04633 packages/runs/tests/runs.test.mjs
|
||||
d0f33cb945cb5960847cd9e9af05f5a85731cb68bc5131efb084a7ecea81591f packages/runs/tests/state.test.mjs
|
||||
be5d1bdf95340e6c4a697531a225f08b40901ad1b570da7371efe7994e3e1413 packages/runs/tests/task-cli.test.mjs
|
||||
4873187609897c643e62acf7f1d074fa3c03369fe6702b9b0c7776138e776401 scripts/mosaic-task.mjs
|
||||
Executable
+65
@@ -0,0 +1,65 @@
|
||||
#!/usr/bin/env bash
|
||||
# Row 56 deliberate deltas. Outside the seeded data root of byte-check.sh,
|
||||
# list and show change on purpose where a record links out of the data
|
||||
# root, a document isn't a JSON object, a run is a regular file, or the
|
||||
# runs directory can't be read. This prints the baseline and candidate
|
||||
# output for each such case (first stderr line only) so the change is on
|
||||
# record. It asserts nothing; the package tests assert the new behaviour.
|
||||
# Usage: delta-check.sh BASE_TREE CAND_TREE WORK_DIR (WORK_DIR must not exist)
|
||||
set -euo pipefail
|
||||
BASE="$(cd "$1" && pwd)"; CAND="$(cd "$2" && pwd)"; W="$3"
|
||||
[ ! -e "$W" ] || { echo "delta-check: $W exists" >&2; exit 4; }
|
||||
mkdir -p "$W"; W="$(cd "$W" && pwd)"
|
||||
A=r-20260101T000000Z-aaaaaa
|
||||
RESULT='{"runVersion":1,"taskId":"t-out","status":"succeeded","request":"r","response":"s","provider":"p","model":"m","startedAt":"a","finishedAt":"b","durationMs":1,"exitCode":0,"signal":null}'
|
||||
|
||||
root() { # name -> creates W/name/{data,outside}, writes config, echoes data root
|
||||
mkdir -p "$W/$1/data" "$W/$1/outside"
|
||||
printf '{"configVersion":1,"environment":"development","dataRoot":"%s","execution":{"backend":"docker","provider":"zai","model":"m"}}\n' "$W/$1/data" > "$W/$1/config.json"
|
||||
echo "$W/$1/data"
|
||||
}
|
||||
run_case() { # name op [arg]
|
||||
local name="$1"; shift
|
||||
for tree in base cand; do
|
||||
local dir="$BASE"; [ "$tree" = cand ] && dir="$CAND"
|
||||
local rc=0 out err
|
||||
out="$(cd "$dir" && env MOSAIC_CONFIG="$W/$name/config.json" node scripts/mosaic-task.mjs "$@" 2>"$W/$name/$tree.err")" || rc=$?
|
||||
err="$(head -1 "$W/$name/$tree.err")"
|
||||
printf ' %s: exit %s\n' "$tree" "$rc"
|
||||
[ -z "$out" ] || printf '%s\n' "$out" | sed 's/^/ out| /'
|
||||
[ -z "$err" ] || printf ' err| %s\n' "$err"
|
||||
done
|
||||
}
|
||||
hdr() { printf '\n== %s\n' "$*"; }
|
||||
|
||||
D="$(root run-link)"; mkdir -p "$D/runs"; printf '%s\n' "$RESULT" > "$W/run-link/outside/result.json"; ln -s "$W/run-link/outside" "$D/runs/$A"
|
||||
hdr "run directory linked out of the data root: list"; run_case run-link list
|
||||
hdr "run directory linked out of the data root: show"; run_case run-link show "$A"
|
||||
|
||||
D="$(root doc-link)"; mkdir -p "$D/runs/$A"; printf '%s\n' "$RESULT" > "$W/doc-link/outside/result.json"; ln -s "$W/doc-link/outside/result.json" "$D/runs/$A/result.json"
|
||||
hdr "result.json linked out of the data root: list"; run_case doc-link list
|
||||
hdr "result.json linked out of the data root: show"; run_case doc-link show "$A"
|
||||
|
||||
D="$(root runs-link)"; mkdir -p "$W/runs-link/outside/$A"; printf '%s\n' "$RESULT" > "$W/runs-link/outside/$A/result.json"; ln -s "$W/runs-link/outside" "$D/runs"
|
||||
hdr "runs directory linked out of the data root: list"; run_case runs-link list
|
||||
hdr "runs directory linked out of the data root: show"; run_case runs-link show "$A"
|
||||
|
||||
D="$(root non-object)"; mkdir -p "$D/runs/$A"; printf '5\n' > "$D/runs/$A/result.json"; printf '[]\n' > "$D/runs/$A/task.json"
|
||||
hdr "result.json is 5 and task.json is []: list"; run_case non-object list
|
||||
hdr "result.json is 5 and task.json is []: show"; run_case non-object show "$A"
|
||||
|
||||
D="$(root run-file)"; mkdir -p "$D/runs"; printf 'x\n' > "$D/runs/$A"
|
||||
hdr "run is a regular file: show"; run_case run-file show "$A"
|
||||
|
||||
D="$(root loop)"; mkdir -p "$D/runs"; ln -s r-b "$D/runs/$A"; ln -s "$A" "$D/runs/r-b"
|
||||
hdr "run is a link loop: show"; run_case loop show "$A"
|
||||
|
||||
if [ "$(id -u)" != 0 ]; then
|
||||
D="$(root unreadable)"; mkdir -p "$D/runs/$A"; chmod 000 "$D/runs"
|
||||
hdr "runs directory unreadable: list"; run_case unreadable list
|
||||
hdr "runs directory unreadable: show"; run_case unreadable show "$A"
|
||||
chmod 755 "$D/runs"
|
||||
D="$(root run-unreadable)"; mkdir -p "$D/runs/$A"; printf '%s\n' "$RESULT" > "$D/runs/$A/result.json"; chmod 000 "$D/runs/$A"
|
||||
hdr "run directory unreadable: show"; run_case run-unreadable show "$A"
|
||||
chmod 755 "$D/runs/$A"
|
||||
fi
|
||||
Executable
+48
@@ -0,0 +1,48 @@
|
||||
#!/usr/bin/env bash
|
||||
# Seeds a data root for the row 56 byte-identity check. Usage: seed.sh DIR
|
||||
# DIR must not exist. Writes run records only under DIR.
|
||||
set -euo pipefail
|
||||
D="$1"
|
||||
[ ! -e "$D" ] || { echo "seed: $D exists" >&2; exit 4; }
|
||||
R="$D/runs"
|
||||
mkdir -p "$R" "$D/state"
|
||||
run() { mkdir -p "$R/$1"; }
|
||||
put() { printf '%s\n' "$3" > "$R/$1/$2"; }
|
||||
|
||||
A=r-20260101T000000Z-aaaaaa
|
||||
run $A
|
||||
put $A task.json '{"taskVersion":1,"id":"t-full","prompt":"say hi","mission":"m.json"}'
|
||||
put $A mission.json '{"missionVersion":1,"id":"m-full","objective":"Exercise every show line"}'
|
||||
put $A result.json '{"runVersion":1,"runId":"'$A'","taskId":"t-full","missionId":"m-full","status":"succeeded","reason":null,"request":"say \"hi\"\nplease","response":"hi","expectedExact":"hi","retriedFrom":"r-20251231T000000Z-000000","workspace":"ws1","tools":["read","ls"],"session":"s1","sessionForkFrom":null,"exitCode":0,"signal":null,"provider":"zai","model":"m","startedAt":"2026-01-01T00:00:00.000Z","finishedAt":"2026-01-01T00:00:01.000Z","durationMs":1000}'
|
||||
: > "$R/$A/stderr.txt"
|
||||
mkdir "$R/$A/workspace"
|
||||
|
||||
B=r-20260101T000100Z-bbbbbb
|
||||
run $B
|
||||
put $B task.json '{"taskVersion":1,"id":"t-fail","prompt":"x"}'
|
||||
put $B result.json '{"runVersion":1,"runId":"'$B'","taskId":"t-fail","missionId":null,"status":"failed","reason":"expect-mismatch","request":"x","response":"y","expectedExact":null,"workspace":null,"tools":null,"session":null,"sessionForkFrom":null,"exitCode":null,"signal":"SIGKILL","provider":"zai","model":"m","startedAt":"2026-01-01T00:01:00.000Z","finishedAt":"2026-01-01T00:01:02.000Z","durationMs":2000}'
|
||||
|
||||
C=r-20260101T000200Z-cccccc
|
||||
run $C
|
||||
put $C task.json '{"taskVersion":1,"id":"t-incomplete","prompt":"x"}'
|
||||
|
||||
E=r-20260101T000300Z-dddddd
|
||||
run $E
|
||||
put $E result.json '{"status": "succeeded", truncated'
|
||||
|
||||
N=r-20260101T000400Z-eeeeee
|
||||
run $N
|
||||
put $N result.json 'null'
|
||||
|
||||
F=r-20260101T000500Z-ffffff
|
||||
run $F
|
||||
put $F mission.json '{not json'
|
||||
put $F result.json '{"runVersion":1,"runId":"'$F'","taskId":"a-task-id-longer-than-eighteen","missionId":"m","status":"succeeded-with-a-long-status","reason":null,"request":"","response":"","workspace":":run","tools":[],"session":"named-session","exitCode":0,"signal":null,"provider":"p","model":"m","startedAt":"s","finishedAt":"f","durationMs":0}'
|
||||
|
||||
printf 'not a run directory\n' > "$R/r-20260101T000600Z-gggggg"
|
||||
ln -s "$A" "$R/r-20260101T000700Z-hhhhhh"
|
||||
ln -s "r-does-not-exist" "$R/r-20260101T000800Z-iiiiii"
|
||||
run r-zz.weird_name-1
|
||||
mkdir "$R/x-not-a-run"
|
||||
printf '{"at":"2026-01-01T00:00:00Z","event":"pruned","runId":"r-old"}\n' > "$R/.pruned.log"
|
||||
printf 'notes\n' > "$R/notes.txt"
|
||||
Reference in New Issue
Block a user