feat(retention): run-record pruning - keep newest N, dry-run default (#32)
- mosaic-task.mjs prune [--keep=N] [--yes]: default keep 50; without --yes lists candidates without deleting - only r-* directories under the runs root; symlinks skipped; sessions/workspaces/state/config untouched (asserted by suite sentinels) - append-only receipt runs/.pruned.log records every pruned id - test-task.sh: +8 retention cases (dry-run no-delete, keep-N, newest kept, receipt, isolation, invalid keep, empty no-op) Also: suite hardening - prune section scopes its config per-command (no export/unset leaking into later sections); duplicated check() removed; latest_reason hoisted to helpers; status colors now green OK / red FAIL (terminal-only, NO_COLOR-aware) per owner UX feedback. Closes #32
This commit is contained in:
+31
-10
@@ -42,10 +42,6 @@ latest_reason() {
|
||||
[ -n "$latest" ] && node -e 'try{const r=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));console.log(r.reason??"")}catch{console.log("")}' "$latest/result.json" 2>/dev/null
|
||||
}
|
||||
|
||||
check() {
|
||||
if [ "$2" = "0" ]; then PASS=$((PASS+1)); echo "${C_OK}OK${C_RESET} $1"; else FAIL=$((FAIL+1)); echo "${C_FAIL}FAIL${C_RESET} $1"; fi
|
||||
}
|
||||
|
||||
CONFIG="$SANDBOX/config.json"
|
||||
DATA_ROOT="$SANDBOX/data"
|
||||
mkdir -p "$DATA_ROOT"
|
||||
@@ -108,6 +104,37 @@ $TASK validate "$SANDBOX/ok.json" >/dev/null 2>&1
|
||||
M2=$(stat -c %Y "$SANDBOX/ok.json")
|
||||
[ "$M1" = "$M2" ] && check "validation does not modify the task file" 0 || check "validation does not modify the task file" 1
|
||||
|
||||
# ---------- retention: prune (deterministic, no Docker) ----------
|
||||
mkdir -p "$SANDBOX/data"
|
||||
cat > "$SANDBOX/prune-config.json" <<EOF
|
||||
{"configVersion":1,"environment":"development","dataRoot":"$SANDBOX/data","execution":{"backend":"docker","provider":"zai","model":"m"}}
|
||||
EOF
|
||||
for i in 1 2 3 4 5; do
|
||||
D="$SANDBOX/data/runs/r-20260903T0100_0${i}Z-suite00$i"
|
||||
mkdir -p "$D"
|
||||
printf '{"runVersion":1,"runId":"r-20260903T0100_0%sZ-suite00%s","taskId":"t","status":"succeeded"}' "$i" "$i" > "$D/result.json"
|
||||
done
|
||||
mkdir -p "$SANDBOX/data/sessions/sentinel" "$SANDBOX/data/workspaces/sentinel"
|
||||
|
||||
expect_exit "prune dry-run exits 0" 0 -- env MOSAIC_CONFIG="$SANDBOX/prune-config.json" node scripts/mosaic-task.mjs prune
|
||||
[ "$(ls "$SANDBOX/data/runs" | grep -c '^r-')" -eq 5 ] \
|
||||
&& check "dry-run deleted nothing" 0 || check "dry-run deleted nothing" 1
|
||||
|
||||
expect_exit "prune --keep=2 --yes removes oldest" 0 -- env MOSAIC_CONFIG="$SANDBOX/prune-config.json" node scripts/mosaic-task.mjs prune --keep=2 --yes
|
||||
[ "$(ls "$SANDBOX/data/runs" | grep -c '^r-')" -eq 2 ] \
|
||||
&& check "kept exactly 2 newest runs" 0 || check "kept exactly 2 newest runs" 1
|
||||
NEWEST="r-20260903T0100_05Z-suite005"
|
||||
[ -d "$SANDBOX/data/runs/$NEWEST" ] \
|
||||
&& check "newest run kept, oldest pruned" 0 || check "newest run kept, oldest pruned" 1
|
||||
[ -f "$SANDBOX/data/runs/.pruned.log" ] \
|
||||
&& [ "$(grep -c 'pruned' "$SANDBOX/data/runs/.pruned.log")" -eq 3 ] \
|
||||
&& check "append-only receipt written (3 entries)" 0 \
|
||||
|| check "append-only receipt written (3 entries)" 1
|
||||
[ -d "$SANDBOX/data/sessions/sentinel" ] && [ -d "$SANDBOX/data/workspaces/sentinel" ] \
|
||||
&& check "sessions/workspaces untouched by prune" 0 \
|
||||
|| check "sessions/workspaces untouched by prune" 1
|
||||
expect_exit "prune with invalid keep exits 4" 4 -- env MOSAIC_CONFIG="$SANDBOX/prune-config.json" node scripts/mosaic-task.mjs prune --keep=0 --yes
|
||||
|
||||
# ---------- adapter seam: deterministic mock cases (Docker, no provider) ----------
|
||||
if docker info >/dev/null 2>&1; then
|
||||
good_task "$SANDBOX/ok.json"
|
||||
@@ -244,12 +271,6 @@ dump_latest_run() {
|
||||
fi
|
||||
}
|
||||
|
||||
latest_reason() {
|
||||
local latest
|
||||
latest="$(ls -dt "$SANDBOX/data/runs"/r-* 2>/dev/null | head -1)"
|
||||
[ -n "$latest" ] && node -e 'try{const r=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));console.log(r.reason??"")}catch{console.log("")}' "$latest/result.json" 2>/dev/null
|
||||
}
|
||||
|
||||
if docker info >/dev/null 2>&1; then
|
||||
RUNS1=$(ls "$DATA_ROOT/runs" 2>/dev/null | wc -l)
|
||||
if scripts/run-task.sh run "$SANDBOX/ok.json" >/dev/null 2>&1; then
|
||||
|
||||
Reference in New Issue
Block a user