|
@@ -26,7 +26,7 @@ git -C "$REPO" -c user.email=t@t -c user.name=t commit -q --allow-empty -m init
|
|
|
PKT="$TMP/packet.txt"; echo "Do the thing. FINAL REPLY: one line." > "$PKT"
|
|
PKT="$TMP/packet.txt"; echo "Do the thing. FINAL REPLY: one line." > "$PKT"
|
|
|
|
|
|
|
|
# --- syntax + help ------------------------------------------------------------
|
|
# --- syntax + help ------------------------------------------------------------
|
|
|
-for s in ff-spawn.sh ff-collect.sh ff-doctor.sh ff-status.sh; do
|
|
|
|
|
|
|
+for s in ff-spawn.sh ff-collect.sh ff-doctor.sh ff-status.sh ff-run.sh ff-clean.sh ff-import.sh; do
|
|
|
bash -n "$S/$s" 2>/dev/null && ok "syntax $s" || bad "syntax $s"
|
|
bash -n "$S/$s" 2>/dev/null && ok "syntax $s" || bad "syntax $s"
|
|
|
bash "$S/$s" --help 2>/dev/null | grep -q "EXAMPLES" && ok "$s --help has EXAMPLES" || bad "$s --help lacks EXAMPLES"
|
|
bash "$S/$s" --help 2>/dev/null | grep -q "EXAMPLES" && ok "$s --help has EXAMPLES" || bad "$s --help lacks EXAMPLES"
|
|
|
check "$s --help exits 0" 0 bash "$S/$s" --help
|
|
check "$s --help exits 0" 0 bash "$S/$s" --help
|
|
@@ -94,6 +94,11 @@ bash "$S/ff-status.sh" --run r1 --repo "$REPO" 2>/dev/null | jq -e '.lanes[] | s
|
|
|
&& ok "status: dry-run lane state done" || bad "status: lane state wrong"
|
|
&& ok "status: dry-run lane state done" || bad "status: lane state wrong"
|
|
|
[ -f "$HERE/../assets/ff-monitor.html" ] && grep -q "status.json" "$HERE/../assets/ff-monitor.html" \
|
|
[ -f "$HERE/../assets/ff-monitor.html" ] && grep -q "status.json" "$HERE/../assets/ff-monitor.html" \
|
|
|
&& ok "monitor asset present + polls status.json" || bad "monitor asset missing"
|
|
&& ok "monitor asset present + polls status.json" || bad "monitor asset missing"
|
|
|
|
|
+# torn-write guard: an empty/missing-lanes payload is treated as a fetch miss
|
|
|
|
|
+grep -q "torn-write guard" "$HERE/../assets/ff-monitor.html" \
|
|
|
|
|
+ && ok "monitor: empty-lanes torn-write guard present" || bad "monitor: torn-write guard missing"
|
|
|
|
|
+grep -q "d.lanes.length === 0" "$HERE/../assets/ff-monitor.html" \
|
|
|
|
|
+ && ok "monitor: guards on empty lanes array" || bad "monitor: empty-lanes guard logic missing"
|
|
|
|
|
|
|
|
# --- escape guard ------------------------------------------------------------------
|
|
# --- escape guard ------------------------------------------------------------------
|
|
|
check "escape guard: clean main" 0 bash "$S/ff-collect.sh" --check-main-clean --run r1 --repo "$REPO"
|
|
check "escape guard: clean main" 0 bash "$S/ff-collect.sh" --check-main-clean --run r1 --repo "$REPO"
|
|
@@ -107,6 +112,186 @@ bash "$S/ff-doctor.sh" --offline >/dev/null 2>&1; RC=$?
|
|
|
[ "$RC" = 0 ] || [ "$RC" = 10 ] && ok "doctor --offline runs (rc=$RC)" || bad "doctor --offline rc=$RC"
|
|
[ "$RC" = 0 ] || [ "$RC" = 10 ] && ok "doctor --offline runs (rc=$RC)" || bad "doctor --offline rc=$RC"
|
|
|
bash "$S/ff-doctor.sh" --offline 2>/dev/null | grep -qE "^bin-jq ok" && ok "doctor TSV output" || bad "doctor TSV output missing"
|
|
bash "$S/ff-doctor.sh" --offline 2>/dev/null | grep -qE "^bin-jq ok" && ok "doctor TSV output" || bad "doctor TSV output missing"
|
|
|
|
|
|
|
|
|
|
+# --- manifest (feature 1): created on first spawn, append, idempotent -----------
|
|
|
|
|
+M="$REPO/.fleetflow/r1/manifest.json"
|
|
|
|
|
+[ -f "$M" ] && ok "manifest created on first spawn" || bad "manifest not created"
|
|
|
|
|
+jq -e '.run=="r1" and .base=="main" and (.created_by|startswith("ff-spawn/"))' "$M" >/dev/null \
|
|
|
|
|
+ && ok "manifest header fields" || bad "manifest header wrong"
|
|
|
|
|
+# lane 'a' was spawned several times (cache-hit, --force, changed) yet stays 1 entry
|
|
|
|
|
+NA="$(jq '[.packets[]|select(.id=="a")]|length' "$M")"
|
|
|
|
|
+[ "$NA" = "1" ] && ok "manifest packet idempotent (one entry per id)" || bad "manifest has $NA 'a' entries"
|
|
|
|
|
+# every packet carries the Wave-1 fields the brief requires
|
|
|
|
|
+jq -e '.packets[]|select(.id=="a")|has("effort") and has("key") and has("max_turns") and has("worktree")' "$M" >/dev/null \
|
|
|
|
|
+ && ok "manifest packet has effort+key+max_turns+worktree" || bad "manifest packet fields missing"
|
|
|
|
|
+# ff-status surfaces the manifest summary
|
|
|
|
|
+bash "$S/ff-status.sh" --run r1 --repo "$REPO" 2>/dev/null | jq -e '.manifest.packet_count >= 1' >/dev/null \
|
|
|
|
|
+ && ok "status: surfaces manifest.packet_count" || bad "status: manifest summary missing"
|
|
|
|
|
+
|
|
|
|
|
+# --- ff-run.sh (feature 2): whole-run resume + status alias ---------------------
|
|
|
|
|
+check "ff-run: no subcommand -> 2" 2 bash "$S/ff-run.sh"
|
|
|
|
|
+check "ff-run: bad subcommand -> 2" 2 bash "$S/ff-run.sh" frobnicate --run r1 --repo "$REPO"
|
|
|
|
|
+check "ff-run: resume missing run -> 2" 2 bash "$S/ff-run.sh" resume --run nope --repo "$REPO"
|
|
|
|
|
+check "ff-run: resume no manifest -> 2" 2 bash "$S/ff-run.sh" resume --run r1 --repo "$TMP"
|
|
|
|
|
+# fresh run, two DISTINCT packets so replay order is unambiguous
|
|
|
|
|
+PA="$TMP/r2-a.txt"; echo "do A. FINAL REPLY: a" > "$PA"
|
|
|
|
|
+PB="$TMP/r2-b.txt"; echo "do B. FINAL REPLY: b" > "$PB"
|
|
|
|
|
+bash "$S/ff-spawn.sh" --run r2 --id a --brain sonnet --prompt-file "$PA" --repo "$REPO" --dry-run >/dev/null 2>&1
|
|
|
|
|
+bash "$S/ff-spawn.sh" --run r2 --id b --brain sonnet --prompt-file "$PB" --repo "$REPO" --dry-run >/dev/null 2>&1
|
|
|
|
|
+# resume re-spawns both -> both cache-hit -> exit 0; ids return in manifest order
|
|
|
|
|
+RR="$(bash "$S/ff-run.sh" resume --run r2 --repo "$REPO" 2>/dev/null)"; RC=$?
|
|
|
|
|
+[ "$RC" = "0" ] && ok "ff-run: all-cached resume exits 0" || bad "ff-run: resume rc=$RC"
|
|
|
|
|
+printf '%s' "$RR" | jq -e 'length==2 and .[0].id=="a" and .[1].id=="b" and all(.[]; .status=="cached")' >/dev/null \
|
|
|
|
|
+ && ok "ff-run: resume preserves packet order (no reorder drift)" || bad "ff-run: resume order wrong: $RR"
|
|
|
|
|
+# status subcommand == ff-status (compare stable fields; generated_at differs by design)
|
|
|
|
|
+SA="$(bash "$S/ff-run.sh" status --run r2 --repo "$REPO" 2>/dev/null | jq -c '{run,lanes:[.lanes[].id]}')"
|
|
|
|
|
+SB="$(bash "$S/ff-status.sh" --run r2 --repo "$REPO" 2>/dev/null | jq -c '{run,lanes:[.lanes[].id]}')"
|
|
|
|
|
+[ "$SA" = "$SB" ] && ok "ff-run status aliases ff-status" || bad "ff-run status != ff-status"
|
|
|
|
|
+
|
|
|
|
|
+# --- schema fence-strip (feature 3) --------------------------------------------
|
|
|
|
|
+# a result whose text is fenced JSON must still validate (--schema strips fences)
|
|
|
|
|
+jq -nc '{is_error:false,result:"```json\n{\"verdict\":\"ok\"}\n```"}' > "$REPO/.fleetflow/r1/fence.result.json"
|
|
|
|
|
+jq -nc '{type:"result",key:"v2:fence",id:"fence",brain:"sonnet",rc:0,artifact:"x"}' >> "$REPO/.fleetflow/r1/journal.jsonl"
|
|
|
|
|
+FOUT="$(bash "$S/ff-collect.sh" --run r1 --id fence --repo "$REPO" --schema 2>/dev/null)"; FRC=$?
|
|
|
|
|
+[ "$FRC" = "0" ] && ok "collect: fence-strip lets fenced JSON validate" || bad "collect: fence-strip failed rc=$FRC"
|
|
|
|
|
+printf '%s' "$FOUT" | jq -e '.verdict=="ok"' >/dev/null && ok "collect: fence-strip returns inner JSON" || bad "collect: fence-strip output wrong"
|
|
|
|
|
+
|
|
|
|
|
+# --- schema --repair seam (feature 3) ------------------------------------------
|
|
|
|
|
+# bad result + FLEETFLOW_REPAIR_DRYRUN: do_repair saves <id>.invalid.txt and
|
|
|
|
|
+# respawns a <id>-repair lane. The dry-run lane replies "DRYRUN" (not JSON), so
|
|
|
|
|
+# the repair gate fails -> exit 10; we assert the SEAM fired, not a happy path.
|
|
|
|
|
+jq -nc '{is_error:false,result:"this is not json"}' > "$REPO/.fleetflow/r1/rp.result.json"
|
|
|
|
|
+jq -nc '{type:"result",key:"v2:rp",id:"rp",brain:"sonnet",rc:0,artifact:"x"}' >> "$REPO/.fleetflow/r1/journal.jsonl"
|
|
|
|
|
+FLEETFLOW_REPAIR_DRYRUN=1 bash "$S/ff-collect.sh" --run r1 --id rp --repo "$REPO" --schema --repair >/dev/null 2>&1; RPRC=$?
|
|
|
|
|
+[ "$RPRC" = "10" ] && ok "collect: --repair exits 10 when corrected output invalid" || bad "collect: --repair rc=$RPRC (want 10)"
|
|
|
|
|
+[ -f "$REPO/.fleetflow/r1/rp.invalid.txt" ] && ok "collect: --repair saved <id>.invalid.txt" || bad "collect: invalid.txt missing"
|
|
|
|
|
+NREP="$(jq -r 'select(.type=="result" and .id=="rp-repair")|.id' "$REPO/.fleetflow/r1/journal.jsonl" | wc -l | tr -d ' ')"
|
|
|
|
|
+[ "$NREP" -ge 1 ] && ok "collect: --repair respawned rp-repair lane" || bad "collect: no repair lane spawned"
|
|
|
|
|
+grep -q 'corrected JSON' "$REPO/.fleetflow/r1/rp-repair.prompt-src.txt" 2>/dev/null \
|
|
|
|
|
+ && ok "collect: --repair lane got the corrective prompt" || bad "collect: repair prompt missing"
|
|
|
|
|
+
|
|
|
|
|
+# --- FF_VERSION in journal (feature 4) -----------------------------------------
|
|
|
|
|
+grep -q '"v":"1.1.0"' "$REPO/.fleetflow/r1/journal.jsonl" && ok "journal records FF_VERSION 1.1.0" || bad "journal missing FF_VERSION"
|
|
|
|
|
+# every operational script pins the same version (version-skew spine)
|
|
|
|
|
+VS=0
|
|
|
|
|
+for s in ff-spawn.sh ff-collect.sh ff-status.sh ff-doctor.sh ff-run.sh ff-clean.sh ff-import.sh; do
|
|
|
|
|
+ grep -q '^FF_VERSION="1.1.0"$' "$S/$s" || VS=1
|
|
|
|
|
+done
|
|
|
|
|
+[ "$VS" = "0" ] && ok "all scripts pin FF_VERSION=1.1.0" || bad "version skew across scripts"
|
|
|
|
|
+
|
|
|
|
|
+# --- effort lever (feature 5): effort is part of the cache key ------------------
|
|
|
|
|
+EP="$TMP/effort.txt"; echo "effort test. FINAL REPLY: e" > "$EP"
|
|
|
|
|
+check "spawn: effort lane first run -> 0" 0 bash "$S/ff-spawn.sh" --run r3 --id e --brain sonnet --prompt-file "$EP" --repo "$REPO" --dry-run
|
|
|
|
|
+check "spawn: effort lane identical -> cached" 3 bash "$S/ff-spawn.sh" --run r3 --id e --brain sonnet --prompt-file "$EP" --repo "$REPO" --dry-run
|
|
|
|
|
+# changing ONLY the effort must bust the cache (effort is baked into the OPTS key)
|
|
|
|
|
+check "spawn: effort change -> cache miss" 0 bash "$S/ff-spawn.sh" --run r3 --id e --brain sonnet --prompt-file "$EP" --repo "$REPO" --dry-run --effort high
|
|
|
|
|
+jq -e '.packets[]|select(.id=="e")|.effort=="high"' "$REPO/.fleetflow/r3/manifest.json" >/dev/null \
|
|
|
|
|
+ && ok "manifest records effort=high" || bad "manifest effort field wrong"
|
|
|
|
|
+
|
|
|
|
|
+# --- cache/tmp redirect (feature 7) --------------------------------------------
|
|
|
|
|
+CRT="$TMP/ffcache"; CDP="$TMP/cdp.txt"; echo "cache test. FINAL REPLY: c" > "$CDP"
|
|
|
|
|
+FLEETFLOW_CACHE_ROOT="$CRT" bash "$S/ff-spawn.sh" --run r4 --id c --brain sonnet --prompt-file "$CDP" --repo "$REPO" --dry-run >/dev/null 2>&1
|
|
|
|
|
+[ -d "$CRT/r4-c" ] && ok "spawn: cache dir created under FLEETFLOW_CACHE_ROOT" || bad "spawn: cache dir not redirected to FLEETFLOW_CACHE_ROOT"
|
|
|
|
|
+
|
|
|
|
|
+# --- ff-clean.sh (feature 8): autoclean lanes + cache --------------------------
|
|
|
|
|
+check "ff-clean: usage -> 2" 2 bash "$S/ff-clean.sh"
|
|
|
|
|
+check "ff-clean: no such run -> 2" 2 bash "$S/ff-clean.sh" --run ghost --repo "$REPO"
|
|
|
|
|
+# fresh run, cache redirected, three DISTINCT-prompt worktree lanes (distinct so
|
|
|
|
|
+# they don't cache-hit and skip worktree creation)
|
|
|
|
|
+CLEANROOT="$TMP/cleancache"
|
|
|
|
|
+for lid in cleanlane keeplane dirtlane; do
|
|
|
|
|
+ echo "clean-$lid task. FINAL REPLY: $lid" > "$TMP/clean-$lid.txt"
|
|
|
|
|
+ FLEETFLOW_CACHE_ROOT="$CLEANROOT" bash "$S/ff-spawn.sh" --run rc --id "$lid" --brain sonnet \
|
|
|
|
|
+ --prompt-file "$TMP/clean-$lid.txt" --repo "$REPO" --dry-run --worktree >/dev/null 2>&1
|
|
|
|
|
+done
|
|
|
|
|
+# keeplane gets a real commit (must survive every clean); dirtlane gets untracked junk
|
|
|
|
|
+( cd "$REPO/.fleetflow/rc/wt-keeplane" && git -c user.email=t@t -c user.name=t commit -q --allow-empty -m "real work" )
|
|
|
|
|
+echo "junk" > "$REPO/.fleetflow/rc/wt-dirtlane/junk.txt"
|
|
|
|
|
+[ -d "$CLEANROOT/rc-cleanlane" ] && ok "ff-clean: setup created cache dir" || bad "ff-clean: setup cache dir missing"
|
|
|
|
|
+# no --force: cleanlane removed, dirtlane kept (dirty), keeplane kept (1 commit).
|
|
|
|
|
+# pass the SAME FLEETFLOW_CACHE_ROOT to ff-clean so it finds the redirected dirs.
|
|
|
|
|
+CL="$(FLEETFLOW_CACHE_ROOT="$CLEANROOT" bash "$S/ff-clean.sh" --run rc --repo "$REPO" 2>/dev/null)"
|
|
|
|
|
+printf '%s' "$CL" | awk -F'\t' '$1=="cleanlane"&&$2=="removed"{f=1} END{exit !f}' && ok "ff-clean: removes zero-commit clean lane" || bad "ff-clean: clean lane not removed"
|
|
|
|
|
+printf '%s' "$CL" | awk -F'\t' '$1=="dirtlane"&&$2=="kept"&&$3~/dirty/{f=1} END{exit !f}' && ok "ff-clean: keeps dirty zero-commit lane (no --force)" || bad "ff-clean: dirty lane mishandled"
|
|
|
|
|
+printf '%s' "$CL" | awk -F'\t' '$1=="keeplane"&&$2=="kept"&&$3~/1 commits/{f=1} END{exit !f}' && ok "ff-clean: keeps committed lane" || bad "ff-clean: committed lane mishandled"
|
|
|
|
|
+[ -d "$REPO/.fleetflow/rc/wt-cleanlane" ] && bad "ff-clean: clean worktree dir remains" || ok "ff-clean: clean worktree removed"
|
|
|
|
|
+git -C "$REPO" show-ref --verify --quiet refs/heads/fleetflow/rc/keeplane && ok "ff-clean: committed branch preserved" || bad "ff-clean: keeper branch deleted"
|
|
|
|
|
+# --force: dirtlane now removed; keeplane STILL kept (committed lanes are never force-removed)
|
|
|
|
|
+CL2="$(FLEETFLOW_CACHE_ROOT="$CLEANROOT" bash "$S/ff-clean.sh" --run rc --repo "$REPO" --force 2>/dev/null)"
|
|
|
|
|
+printf '%s' "$CL2" | awk -F'\t' '$1=="dirtlane"&&$2=="removed"{f=1} END{exit !f}' && ok "ff-clean: --force removes dirty zero-commit lane" || bad "ff-clean: --force dirty mishandled"
|
|
|
|
|
+printf '%s' "$CL2" | awk -F'\t' '$1=="keeplane"&&$2=="kept"{f=1} END{exit !f}' && ok "ff-clean: --force still keeps committed lane" || bad "ff-clean: --force removed committed lane!"
|
|
|
|
|
+[ -d "$CLEANROOT/rc-cleanlane" ] || [ -d "$CLEANROOT/rc-dirtlane" ] || [ -d "$CLEANROOT/rc-keeplane" ] \
|
|
|
|
|
+ && bad "ff-clean: cache dir remains" || ok "ff-clean: cache dirs removed"
|
|
|
|
|
+
|
|
|
|
|
+# --- state derivation (feature C): last journal record wins -----------------------
|
|
|
|
|
+# a respawn appends "started" AFTER an old "result" -> the lane is running again,
|
|
|
|
|
+# NOT done/failed (the last-result-wins bug this fixes).
|
|
|
|
|
+RD5="$REPO/.fleetflow/r5"; mkdir -p "$RD5"
|
|
|
|
|
+: > "$RD5/z.prompt.txt" # mtime source for elapsed
|
|
|
|
|
+printf '%s\n' \
|
|
|
|
|
+ '{"type":"started","key":"v2:z","id":"z","brain":"sonnet","phase":"build","v":"1.1.0"}' \
|
|
|
|
|
+ '{"type":"result","key":"v2:z","id":"z","brain":"sonnet","rc":0,"artifact":"x"}' \
|
|
|
|
|
+ '{"type":"started","key":"v2:z","id":"z","brain":"sonnet","phase":"build","v":"1.1.0"}' \
|
|
|
|
|
+ > "$RD5/journal.jsonl"
|
|
|
|
|
+bash "$S/ff-status.sh" --run r5 --repo "$REPO" 2>/dev/null \
|
|
|
|
|
+ | jq -e '.lanes[]|select(.id=="z")|.state=="running"' >/dev/null \
|
|
|
|
|
+ && ok "status: respawned lane (started,result,started) is running" || bad "status: respawned lane state wrong"
|
|
|
|
|
+# regression guard: same lane with result-last is still done (common path unchanged)
|
|
|
|
|
+printf '%s\n' \
|
|
|
|
|
+ '{"type":"started","key":"v2:z","id":"z","brain":"sonnet","phase":"build","v":"1.1.0"}' \
|
|
|
|
|
+ '{"type":"result","key":"v2:z","id":"z","brain":"sonnet","rc":0,"artifact":"x"}' \
|
|
|
|
|
+ > "$RD5/journal.jsonl"
|
|
|
|
|
+bash "$S/ff-status.sh" --run r5 --repo "$REPO" 2>/dev/null \
|
|
|
|
|
+ | jq -e '.lanes[]|select(.id=="z")|.state=="done"' >/dev/null \
|
|
|
|
|
+ && ok "status: result-last lane is still done (no regression)" || bad "status: result-last state wrong"
|
|
|
|
|
+
|
|
|
|
|
+# --- ff-import.sh (feature B): native Workflow run import ------------------------
|
|
|
|
|
+# build a synthetic native wf_ dir: journal.jsonl (started/result keyed by
|
|
|
|
|
+# agentId) + two agent transcripts (one string content, one content-array).
|
|
|
|
|
+WFD="$TMP/wf_ab12cd34-ef"; mkdir -p "$WFD"
|
|
|
|
|
+printf '%s\n' \
|
|
|
|
|
+ '{"type":"started","key":"v2:aaaa","agentId":"a01cb5f01fadf5610"}' \
|
|
|
|
|
+ '{"type":"result","key":"v2:aaaa","agentId":"a01cb5f01fadf5610","result":{"verdict":"ok","score":7}}' \
|
|
|
|
|
+ '{"type":"started","key":"v2:bbbb","agentId":"a02deadbeef00000"}' \
|
|
|
|
|
+ > "$WFD/journal.jsonl"
|
|
|
|
|
+jq -nc '{type:"user",message:{role:"user",content:"Refute the claim that X is safe."}}' \
|
|
|
|
|
+ > "$WFD/agent-a01cb5f01fadf5610.jsonl"
|
|
|
|
|
+jq -nc '{type:"user",message:{role:"user",content:[{type:"text",text:"Find any bugs in module Y."}]}}' \
|
|
|
|
|
+ > "$WFD/agent-a02deadbeef00000.jsonl"
|
|
|
|
|
+IMP="$(bash "$S/ff-import.sh" --wf "$WFD" --run imp1 --repo "$REPO" 2>/dev/null)"; IRC=$?
|
|
|
|
|
+[ "$IRC" = "0" ] && ok "ff-import: exits 0 on import" || bad "ff-import: rc=$IRC"
|
|
|
|
|
+printf '%s' "$IMP" | awk -F'\t' '$1=="a01cb5f01fadf5610"&&$2=="imported"{f=1} END{exit !f}' \
|
|
|
|
|
+ && ok "ff-import: completed agent reported imported" || bad "ff-import: imported TSV wrong"
|
|
|
|
|
+printf '%s' "$IMP" | awk -F'\t' '$1=="a02deadbeef00000"&&$2=="incomplete"{f=1} END{exit !f}' \
|
|
|
|
|
+ && ok "ff-import: started-only agent reported incomplete" || bad "ff-import: incomplete TSV wrong"
|
|
|
|
|
+PC="$(printf '%s' "$IMP" | awk -F'\t' '$1=="a01cb5f01fadf5610"{print $3}')"
|
|
|
|
|
+[ -n "$PC" ] && [ "$PC" -gt 0 ] 2>/dev/null && ok "ff-import: prompt_chars > 0" || bad "ff-import: prompt_chars wrong ($PC)"
|
|
|
|
|
+IRD="$REPO/.fleetflow/imp1"
|
|
|
|
|
+[ -f "$IRD/a01cb5f01fadf5610.prompt.txt" ] && ok "ff-import: wrote prompt.txt (completed)" || bad "ff-import: prompt.txt missing"
|
|
|
|
|
+grep -q "Refute the claim" "$IRD/a01cb5f01fadf5610.prompt.txt" && ok "ff-import: prompt extracted (string content)" || bad "ff-import: string-content prompt wrong"
|
|
|
|
|
+grep -q "Find any bugs" "$IRD/a02deadbeef00000.prompt.txt" && ok "ff-import: prompt extracted (content array)" || bad "ff-import: array-content prompt wrong"
|
|
|
|
|
+[ -f "$IRD/a01cb5f01fadf5610.result.json" ] && ok "ff-import: wrote result.json (completed)" || bad "ff-import: result.json missing"
|
|
|
|
|
+[ ! -f "$IRD/a02deadbeef00000.result.json" ] && ok "ff-import: incomplete agent has no result.json" || bad "ff-import: incomplete got result.json"
|
|
|
|
|
+jq -e '.is_error==false and (.result|fromjson|.verdict=="ok" and .score==7)' "$IRD/a01cb5f01fadf5610.result.json" >/dev/null \
|
|
|
|
|
+ && ok "ff-import: result.json wraps native result (tojson)" || bad "ff-import: result.json shape wrong"
|
|
|
|
|
+NJ="$IRD/journal.jsonl"
|
|
|
|
|
+[ "$(jq -r 'select(.type=="result" and .id=="a01cb5f01fadf5610")|.brain' "$NJ")" = "native" ] \
|
|
|
|
|
+ && ok "ff-import: journal result brain=native" || bad "ff-import: journal brain wrong"
|
|
|
|
|
+# phase lives on the started record (same convention as ff-spawn), not the result
|
|
|
|
|
+[ "$(jq -r 'select(.type=="started" and .id=="a01cb5f01fadf5610")|.phase' "$NJ")" = "imported" ] \
|
|
|
|
|
+ && ok "ff-import: journal phase=imported" || bad "ff-import: journal phase wrong"
|
|
|
|
|
+[ -z "$(jq -r 'select(.type=="result" and .id=="a02deadbeef00000")' "$NJ")" ] \
|
|
|
|
|
+ && ok "ff-import: incomplete agent has no result record" || bad "ff-import: incomplete got a result record"
|
|
|
|
|
+jq -e --arg wf "$WFD" '.packets[]|select(.id=="a01cb5f01fadf5610")|.brain=="native" and .imported_from==$wf' "$IRD/manifest.json" >/dev/null \
|
|
|
|
|
+ && ok "ff-import: manifest packet brain=native + imported_from" || bad "ff-import: manifest packet wrong"
|
|
|
|
|
+# nothing to import -> exit 3
|
|
|
|
|
+WFE="$TMP/wf_empty"; mkdir -p "$WFE"; : > "$WFE/journal.jsonl"
|
|
|
|
|
+check "ff-import: empty wf journal -> 3" 3 bash "$S/ff-import.sh" --wf "$WFE" --run imp2 --repo "$REPO"
|
|
|
|
|
+# ff-run resume SKIPS imported native packets (terminal, not replayable)
|
|
|
|
|
+RRN="$(bash "$S/ff-run.sh" resume --run imp1 --repo "$REPO" 2>/dev/null)"; RCRR=$?
|
|
|
|
|
+[ "$RCRR" = "0" ] && ok "ff-run: resume skips native packets (exit 0)" || bad "ff-run: resume on imported run rc=$RCRR"
|
|
|
|
|
+printf '%s' "$RRN" | jq -e 'any(.[]; .id=="a01cb5f01fadf5610" and .status=="imported")' >/dev/null \
|
|
|
|
|
+ && ok "ff-run: native packet reported imported (skipped)" || bad "ff-run: native packet not skipped"
|
|
|
|
|
+
|
|
|
echo "=== $PASS passed, $FAILN failed ==="
|
|
echo "=== $PASS passed, $FAILN failed ==="
|
|
|
[ "$FAILN" = 0 ] || exit 1
|
|
[ "$FAILN" = 0 ] || exit 1
|
|
|
exit 0
|
|
exit 0
|