feat(sessions): sessionForkFrom - branch conversations from a common ancestor (#33)
- task schema: optional sessionForkFrom (source session name); requires session target; self-fork rejected - runner: resolves source newest .jsonl (fail 4 if none/outside dataRoot); passes MOSAIC_SESSION_FORK + MOSAIC_SESSION_DIR; result records lineage - pi adapter: --fork <source> --session-dir <target> when forking; ephemeral default unchanged; plain session resume unchanged - compose passthrough; RELEASE -> 0.0.7 (adapter changed) - suite +9 cases (58 total): plumbing via mock stderr, validation negatives, live fork - child recalls ancestor code word, ancestor session file untouched Closes #33
This commit is contained in:
@@ -191,6 +191,23 @@ EOF
|
||||
expect_exit "retry of missing run exits 4" 4 -- \
|
||||
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" node scripts/mosaic-task.mjs retry r-missing
|
||||
|
||||
# session fork plumbing (M11): fork source + target dir delivered
|
||||
printf '{"taskVersion":1,"id":"t-forkplumb","prompt":"x","session":"fork-child","sessionForkFrom":"base"}' > "$SANDBOX/forkplumb.json"
|
||||
mkdir -p "$SANDBOX/data/sessions/base"
|
||||
printf '{}' > "$SANDBOX/data/sessions/base/20260903T000000-plumb.jsonl"
|
||||
expect_exit "fork task runs via mock" 0 -- \
|
||||
env MOSAIC_CONFIG="$SANDBOX/mock-adapters.json" MOSAIC_MOCK_RESPONSE=MOCKED \
|
||||
scripts/run-task.sh run "$SANDBOX/forkplumb.json"
|
||||
FL="$(ls -dt "$SANDBOX/data/runs"/r-* | head -1)"
|
||||
grep -q '^MOSAIC_SESSION_FORK=/var/lib/mosaic/sessions/base/20260903T000000-plumb.jsonl$' "$FL/stderr.txt" 2>/dev/null \
|
||||
&& grep -q '^MOSAIC_SESSION_DIR=/var/lib/mosaic/sessions/fork-child$' "$FL/stderr.txt" 2>/dev/null \
|
||||
&& check "fork source + target delivered to adapter" 0 \
|
||||
|| check "fork source + target delivered to adapter" 1
|
||||
printf '{"taskVersion":1,"id":"t-f2","prompt":"x","sessionForkFrom":"base"}' > "$SANDBOX/noforktarget.json"
|
||||
expect_exit "fork without session target exits 2" 2 -- $TASK validate "$SANDBOX/noforktarget.json"
|
||||
printf '{"taskVersion":1,"id":"t-f3","prompt":"x","session":"base","sessionForkFrom":"base"}' > "$SANDBOX/selfork.json"
|
||||
expect_exit "self-fork exits 2" 2 -- $TASK validate "$SANDBOX/selfork.json"
|
||||
|
||||
# capability policy (M9): least-privilege intersection
|
||||
POL="$SANDBOX/data/workspaces"; mkdir -p "$POL"
|
||||
pol_run() { # missionTools(ABSENT|json) taskTools(ABSENT|json) -> stderr MOSAIC_TOOLS value
|
||||
@@ -305,6 +322,21 @@ process.exit(r.status === "succeeded" && r.response === "MOSAIC_HELLO_OK" && r.e
|
||||
|
||||
COUNT=$($TASK list | wc -l)
|
||||
[ "$COUNT" -ge 2 ] && check "list shows both runs" 0 || check "list shows both runs" 1
|
||||
|
||||
# session fork (M11): teach in base, fork into child, child recalls;
|
||||
# ancestor file count must be unchanged by the fork
|
||||
printf '{"taskVersion":1,"id":"t-fork-base","prompt":"Remember this code word for later: mosaico. Reply with exactly: REMEMBERED","session":"suite-base","expectExact":"REMEMBERED","timeoutSeconds":180}' > "$SANDBOX/fb.json"
|
||||
expect_exit "fork base: teach succeeds" 0 -- scripts/run-task.sh run "$SANDBOX/fb.json"
|
||||
BASECOUNT=$(ls "$SANDBOX/data/sessions/suite-base" | wc -l)
|
||||
printf '{"taskVersion":1,"id":"t-fork-child","prompt":"What code word did I ask you to remember? Reply with only the code word.","session":"suite-child","sessionForkFrom":"suite-base","timeoutSeconds":180}' > "$SANDBOX/fc.json"
|
||||
expect_exit "fork child recalls ancestor context" 0 -- scripts/run-task.sh run "$SANDBOX/fc.json"
|
||||
FR="$(ls -dt "$DATA_ROOT/runs"/r-* | head -1)"
|
||||
node -e 'const r=JSON.parse(require("fs").readFileSync(process.argv[1],"utf8"));process.exit(String(r.response).toLowerCase().includes("mosaico")?0:1)' "$FR/result.json" \
|
||||
&& check "forked child recalled ancestor code word" 0 || check "forked child recalled ancestor code word" 1
|
||||
[ "$(ls "$SANDBOX/data/sessions/suite-base" | wc -l)" -eq "$BASECOUNT" ] \
|
||||
&& check "ancestor session untouched by fork" 0 || check "ancestor session untouched by fork" 1
|
||||
[ "$(ls "$SANDBOX/data/sessions/suite-child" | wc -l)" -ge 1 ] \
|
||||
&& check "child session dir has its own branch file" 0 || check "child session dir has its own branch file" 1
|
||||
else
|
||||
echo "skip live task cases (docker unavailable)"
|
||||
fi
|
||||
|
||||
Reference in New Issue
Block a user