commit - 18e7ebf97d7549bb8c88fc683e1b6e36576be347
commit + 4150f8458ba83e3fd4eb3a9523584218c33c84d6
blob - 8ff2bc5682349f3e4f3d4140bc2508efb4caf9b8
blob + 6358664c6fd13c6651214d93911aaddb968ba44b
--- regress/agent/run.sh
+++ regress/agent/run.sh
rc=$?
}
+run_fugu_resume() {
+ sid=$1
+ prompt=$2
+ ( cd "$dir/epoch-work" && \
+ FUGU_CONF=$dir/fugu.conf FUGU_LIBEXEC=$dir/libexec/fugu \
+ FUGU_CA_FILE=$dir/cert.pem FUGU_RETRY_BASE_MS=50 \
+ "$fugu" -r "$sid" -p "$prompt" </dev/null ) \
+ >"$dir/out" 2>"$dir/errout"
+ rc=$?
+}
+
request_section() {
awk -v name="$1" '
$0 == "--- " name " ---" { found = 1; next }
ok "agent loop: provider traffic stays credential-brokered" $?
stop_stub
+# A compacted System epoch survives process exit. Later Subagent requests
+# use its pinned base and policy-selected source snapshots, append the current
+# fixed read-only guidance, and omit the Lead-only Compaction notes. Provider
+# routing remains invocation configuration: the resumed child uses a named
+# route that did not exist when the Session was created.
+rm -rf "$HOME/.fugu"
+rm -rf "$dir/epoch-work"
+mkdir -p "$HOME/.fugu" "$dir/epoch-work"
+printf 'SUBAGENT-EPOCH-PERSONAL-OLD\n' > "$HOME/.fugu/FUGU.md"
+printf 'SUBAGENT-EPOCH-PROJECT-OLD\n' > "$dir/epoch-work/FUGU.md"
+start_stub OK SUMMARY
+cat > "$dir/fugu.conf" <<EOF
+model "SUBAGENT-EPOCH-SEED-LEAD"
+provider "anthropic"
+api_host "127.0.0.1"
+api_port "$stubport"
+api_path "/epoch-seed"
+api_key "$KEY"
+system "SUBAGENT-EPOCH-BASE-OLD"
+subagent_model "SUBAGENT-EPOCH-ROUTE-OLD"
+auto_compact no
+EOF
+printf 'seed the Subagent epoch\n/compact\n/quit\n' > "$dir/epoch-input"
+( cd "$dir/epoch-work" && FUGU_CONF=$dir/fugu.conf \
+ FUGU_LIBEXEC=$dir/libexec/fugu FUGU_CA_FILE=$dir/cert.pem \
+ FUGU_RETRY_BASE_MS=50 "$fugu" <"$dir/epoch-input" ) \
+ >"$dir/out" 2>"$dir/errout"
+rc=$?
+ok "resumed agent epoch: compacted seed process exits cleanly" \
+ $([ "$rc" -eq 0 ]; echo $?)
+journal=$(ls "$sessions"/*.ndjson 2>/dev/null | head -1)
+epoch_id=${journal##*/}
+epoch_id=${epoch_id%.ndjson}
+grep -q '"t":"system-epoch".*"reason":"compact"' "$journal" 2>/dev/null
+ok "resumed agent epoch: seed records a compacted System epoch" $?
+stop_stub
+
+printf 'SUBAGENT-EPOCH-PERSONAL-NEW\n' > "$HOME/.fugu/FUGU.md"
+printf 'SUBAGENT-EPOCH-PROJECT-NEW\n' > "$dir/epoch-work/FUGU.md"
+start_stub AGENTCALL AGENTREPORT AGENTSYNTH
+cat > "$dir/fugu.conf" <<EOF
+model "SUBAGENT-EPOCH-LIVE-LEAD"
+provider "anthropic"
+api_host "127.0.0.1"
+api_port "$stubport"
+api_path "/epoch-live-lead"
+api_key "$KEY"
+system "SUBAGENT-EPOCH-BASE-NEW"
+auto_compact no
+provider "epoch-child" {
+ type "anthropic"
+ model "SUBAGENT-EPOCH-LIVE-CHILD"
+ api_host "127.0.0.1"
+ api_port "$stubport"
+ api_path "/epoch-live-child"
+ api_key "$KEY"
+}
+subagent_provider "epoch-child"
+subagent_model "SUBAGENT-EPOCH-LIVE-CHILD"
+EOF
+run_fugu_resume "$epoch_id" "delegate from the full resumed epoch"
+ok "resumed agent epoch: full-policy child Turn succeeds" \
+ $([ "$rc" -eq 0 ]; echo $?)
+request_section AGENTCALL "$dir/epoch-full-lead"
+request_section AGENTREPORT "$dir/epoch-full-child"
+grep -q 'COMPACT-NOTES-MARKER' "$dir/epoch-full-lead"
+ok "resumed agent epoch: Compaction notes remain in the Lead baseline" $?
+grep -q 'SUBAGENT-EPOCH-BASE-OLD' "$dir/epoch-full-child" && \
+ grep -q 'SUBAGENT-EPOCH-PERSONAL-OLD' "$dir/epoch-full-child" && \
+ grep -q 'SUBAGENT-EPOCH-PROJECT-OLD' "$dir/epoch-full-child"
+ok "resumed agent epoch: full child uses pinned base and snapshots" $?
+grep -q 'You are an ephemeral read-only subagent' \
+ "$dir/epoch-full-child" && \
+ grep -q 'Do not address the Owner directly' "$dir/epoch-full-child"
+ok "resumed agent epoch: full child retains current read-only guidance" $?
+grep -Eq 'COMPACT-NOTES-MARKER|SUBAGENT-EPOCH-BASE-NEW|SUBAGENT-EPOCH-PERSONAL-NEW|SUBAGENT-EPOCH-PROJECT-NEW' \
+ "$dir/epoch-full-child" 2>/dev/null
+ok "resumed agent epoch: full child omits notes and current source bytes" \
+ $([ $? -ne 0 ]; echo $?)
+grep -q '^POST /epoch-live-lead HTTP/1.1' "$dir/epoch-full-lead" && \
+ grep -q '"model":"SUBAGENT-EPOCH-LIVE-LEAD"' \
+ "$dir/epoch-full-lead" && \
+ grep -q '^POST /epoch-live-child HTTP/1.1' "$dir/epoch-full-child" && \
+ grep -q '"model":"SUBAGENT-EPOCH-LIVE-CHILD"' \
+ "$dir/epoch-full-child"
+ok "resumed agent epoch: current Lead and named child routes stay live" $?
+stop_stub
+
+start_stub AGENTCALL AGENTREPORT AGENTSYNTH
+cat > "$dir/fugu.conf" <<EOF
+model "SUBAGENT-EPOCH-LIVE-LEAD-RESTRICTED"
+provider "anthropic"
+api_host "127.0.0.1"
+api_port "$stubport"
+api_path "/epoch-live-lead-restricted"
+api_key "$KEY"
+system "SUBAGENT-EPOCH-BASE-NEW"
+project_context no
+auto_compact no
+provider "epoch-child" {
+ type "anthropic"
+ model "SUBAGENT-EPOCH-LIVE-CHILD-RESTRICTED"
+ api_host "127.0.0.1"
+ api_port "$stubport"
+ api_path "/epoch-live-child-restricted"
+ api_key "$KEY"
+}
+subagent_provider "epoch-child"
+subagent_model "SUBAGENT-EPOCH-LIVE-CHILD-RESTRICTED"
+EOF
+run_fugu_resume "$epoch_id" "delegate from the restricted resumed epoch"
+ok "resumed agent epoch: restricted-policy child Turn succeeds" \
+ $([ "$rc" -eq 0 ]; echo $?)
+request_section AGENTCALL "$dir/epoch-restricted-lead"
+request_section AGENTREPORT "$dir/epoch-restricted-child"
+grep -q 'SUBAGENT-EPOCH-BASE-OLD' "$dir/epoch-restricted-child" && \
+ grep -q 'You are an ephemeral read-only subagent' \
+ "$dir/epoch-restricted-child" && \
+ grep -q 'Do not address the Owner directly' \
+ "$dir/epoch-restricted-child"
+ok "resumed agent epoch: restricted child keeps pinned base and guidance" $?
+grep -Eq 'COMPACT-NOTES-MARKER|SUBAGENT-EPOCH-PERSONAL-OLD|SUBAGENT-EPOCH-PROJECT-OLD|SUBAGENT-EPOCH-BASE-NEW|SUBAGENT-EPOCH-PERSONAL-NEW|SUBAGENT-EPOCH-PROJECT-NEW' \
+ "$dir/epoch-restricted-child" 2>/dev/null
+ok "resumed agent epoch: restricted child suppresses all external snapshots" \
+ $([ $? -ne 0 ]; echo $?)
+grep -q '^POST /epoch-live-child-restricted HTTP/1.1' \
+ "$dir/epoch-restricted-child" && \
+ grep -q '"model":"SUBAGENT-EPOCH-LIVE-CHILD-RESTRICTED"' \
+ "$dir/epoch-restricted-child"
+ok "resumed agent epoch: repeated resume uses current restricted route" $?
+stop_stub
+
# An escaped glob metacharacter is a literal future path, not an unmatched
# pattern. It is absent when the coordinator snapshots protect, then the
# provider stub creates the literal-star filename before the child starts.
blob - 03682d6dfcd86351c3dddda856bdc83bd82a6852
blob + 47e2da6ed0602070c76831e63f4c35816433f8be
--- regress/turn/run.sh
+++ regress/turn/run.sh
' "$dir/reqlog" > "$request_entry_file"
}
+replace_epoch_variant() { # $1 = field, $2 = exact bytes, $3 = Journal
+ epoch_field=$1
+ epoch_value=$2
+ epoch_journal=$3
+ epoch_hex=$(printf '%s' "$epoch_value" |
+ hexdump -ve '1/1 "%.2x"') || return 1
+ LC_ALL=C sed -E \
+ "s#\"$epoch_field\":\"[0-9a-f]*\"#\"$epoch_field\":\"$epoch_hex\"#" \
+ "$epoch_journal" > "$epoch_journal.new" || return 1
+ grep -q "\"$epoch_field\":\"$epoch_hex\"" \
+ "$epoch_journal.new" || return 1
+ mv "$epoch_journal.new" "$epoch_journal"
+}
+
sessions=$HOME/.fugu/sessions
# --- 0: startup replies are per-Role, empty, and exactly once (I10/I13) ---
"$dir/out" "$dir/errout" 2>/dev/null | awk '{ n += $1 } END { print n + 0 }')
ok "System epoch selection: restricted resume reports one notice" \
$([ "$n" -eq 1 ]; echo $?)
+
+# The same invocation-scoped presentation event reaches line and curses
+# frontends without becoming a Turn. Curses may repaint a transcript row, so
+# its raw terminal stream proves presence while the line sink pins one emit.
+printf '/quit\n' > "$dir/lin"
+( cd "$dir/epoch-work" && FUGU_CONF=$dir/fugu.conf \
+ FUGU_LIBEXEC=$dir/libexec/fugu FUGU_CA_FILE=$dir/cert.pem \
+ "$fugu" -c <"$dir/lin" >"$dir/out" 2>"$dir/errout" )
+rc=$?
+n=$(grep -h -c 'project context disabled for resumed session' \
+ "$dir/out" "$dir/errout" 2>/dev/null | awk '{ n += $1 } END { print n + 0 }')
+ok "System epoch selection: restricted line resume reports one notice" \
+ $([ "$rc" -eq 0 ] && [ "$n" -eq 1 ]; echo $?)
+if [ -x "$obj/ptydrive" ]; then
+ printf '/quit\r' | TERM=vt100 HOME=$HOME \
+ FUGU_CONF=$dir/fugu.conf FUGU_LIBEXEC=$dir/libexec/fugu \
+ FUGU_CA_FILE=$dir/cert.pem FUGU_RETRY_BASE_MS=50 \
+ "$obj/ptydrive" "$fugu" -c >"$dir/cursesout" 2>&1
+ rc=$?
+ grep -a -q 'project context disabled for resumed session' \
+ "$dir/cursesout"
+ notice_rc=$?
+ ok "System epoch selection: restricted curses resume shows its notice" \
+ $([ "$rc" -eq 0 ] && [ "$notice_rc" -eq 0 ]; echo $?)
+fi
n=$(grep -c '"t":"system-epoch".*"reason":"start"' \
"$sessions"/*.ndjson 2>/dev/null)
ok "System epoch selection: resumes do not rebuild the epoch" \
[ "$migration_line" -lt "$turn_line" ]; echo $?)
stop_stub
+# A second resume must reuse the additive migration epoch exactly rather than
+# rebuilding it from a changed invocation or appending another migration.
+start_stub "OK"
+cat > "$dir/fugu.conf" <<EOF
+model "claude-sonnet-4-6"
+provider "anthropic"
+api_host "127.0.0.1"
+api_port "$stubport"
+api_path "/v1/messages"
+api_key "$KEY"
+system "MIGRATION-CHANGED"
+auto_compact no
+EOF
+run_fugu -r "$legacy_id" -p "resume migrated epoch"
+ok "System epoch migration: second resume succeeds" \
+ $([ "$rc" -eq 0 ]; echo $?)
+grep -q 'MIGRATION-BASE' "$dir/reqout" && \
+ grep -q 'LEGACY-PERSONAL' "$dir/reqout" && \
+ grep -q 'LEGACY-PROJECT' "$dir/reqout" && \
+ ! grep -q 'MIGRATION-CHANGED' "$dir/reqout"
+ok "System epoch migration: second resume reuses exact stored bytes" $?
+n=$(grep -c '"t":"system-epoch".*"reason":"migration"' \
+ "$sessions/$legacy_id.ndjson")
+ok "System epoch migration: complete migration is not duplicated" \
+ $([ "$n" -eq 1 ]; echo $?)
+stop_stub
+
# --- 23b: automatic Compaction preflight ---------------------------
# With the minimal five-tool catalogue below, the conservative estimate for
# System "S" plus User "strict boundary" is exactly 463 tokens. Equality at
$([ "$n" -eq 1 ]; echo $?)
stop_stub
+# Stored complete variants remain authoritative across composition drift.
+# Reactive Compaction preserves their exact prefixes while replacing only the
+# summary suffix, and later resumes may toggle between both stored variants.
+rm -rf "$HOME/.fugu"
+start_stub "OK"
+cat > "$dir/fugu.conf" <<EOF
+model "claude-sonnet-4-6"
+provider "anthropic"
+api_host "127.0.0.1"
+api_port "$stubport"
+api_path "/v1/messages"
+api_key "$KEY"
+system "CURRENT-COMPOSER-BASE"
+project_context no
+auto_compact no
+EOF
+run_fugu -p "authoritative drift seed"
+ok "System epoch composition drift: seed Turn succeeds" \
+ $([ "$rc" -eq 0 ]; echo $?)
+journal=$(ls "$sessions"/*.ndjson 2>/dev/null | head -1)
+epoch_id=${journal##*/}
+epoch_id=${epoch_id%.ndjson}
+replace_epoch_variant full "DRIFT-FULL-V0" "$journal" && \
+ replace_epoch_variant restricted "DRIFT-RESTRICTED-V0" "$journal"
+ok "System epoch composition drift: exact stored variants are pinned" $?
+stop_stub
+
+start_stub "OAI_OVERFLOW OAI_SUMMARY OAI_OK"
+cat > "$dir/fugu.conf" <<EOF
+model "unknown-window-fixture"
+provider "openai"
+api_host "127.0.0.1"
+api_port "$stubport"
+api_path "/v1/chat/completions"
+api_key "$KEY"
+system "CHANGED-COMPOSER-BASE"
+compact_keep_tokens 1
+auto_compact yes
+EOF
+run_fugu -r "$epoch_id" -p "recover with authoritative epoch"
+ok "System epoch composition drift: overflow recovery succeeds" \
+ $([ "$rc" -eq 0 ]; echo $?)
+request_log_entry 1 "$dir/drift-overflow"
+grep -q 'DRIFT-FULL-V0' "$dir/drift-overflow" && \
+ ! grep -Eq 'CURRENT-COMPOSER-BASE|CHANGED-COMPOSER-BASE' \
+ "$dir/drift-overflow"
+ok "System epoch composition drift: rejected request uses stored full bytes" $?
+request_log_entry 3 "$dir/drift-retry"
+grep -q 'DRIFT-FULL-V0' "$dir/drift-retry" && \
+ grep -q 'OPENAI-SUMMARY-MARKER' "$dir/drift-retry" && \
+ ! grep -Eq 'CURRENT-COMPOSER-BASE|CHANGED-COMPOSER-BASE' \
+ "$dir/drift-retry"
+ok "System epoch composition drift: Compaction preserves the exact prefix" $?
+grep -q '"t":"system-epoch".*"reason":"compact"' "$journal"
+ok "System epoch composition drift: replacement commits with its Turn" $?
+stop_stub
+
+# /context is derived from the selected stored baseline, not the invocation's
+# current system directive. With Projection and live route unchanged, its
+# complete report is byte-stable across those configuration edits.
+start_stub "OAI_OK"
+printf '/context\n/quit\n' > "$dir/lin"
+cat > "$dir/fugu.conf" <<EOF
+model "unknown-window-fixture"
+provider "openai"
+api_host "127.0.0.1"
+api_port "$stubport"
+api_path "/v1/chat/completions"
+api_key "$KEY"
+system "CURRENT-SYSTEM-A"
+auto_compact no
+EOF
+run_fugu_in "$dir/lin" -r "$epoch_id"
+context_a_rc=$rc
+cp "$dir/out" "$dir/drift-context-a"
+cat > "$dir/fugu.conf" <<EOF
+model "unknown-window-fixture"
+provider "openai"
+api_host "127.0.0.1"
+api_port "$stubport"
+api_path "/v1/chat/completions"
+api_key "$KEY"
+system "A-MUCH-LONGER-CURRENT-SYSTEM-B"
+auto_compact no
+EOF
+run_fugu_in "$dir/lin" -r "$epoch_id"
+context_b_rc=$rc
+[ "$context_a_rc" -eq 0 ] && [ "$context_b_rc" -eq 0 ] && \
+ grep -q 'context estimate' "$dir/drift-context-a" && \
+ grep -q 'context estimate' "$dir/out" && \
+ cmp -s "$dir/drift-context-a" "$dir/out"
+ok "System epoch composition drift: /context ignores current composition" $?
+stop_stub
+
+start_stub "OAI_OK"
+cat > "$dir/fugu.conf" <<EOF
+model "unknown-window-fixture"
+provider "openai"
+api_host "127.0.0.1"
+api_port "$stubport"
+api_path "/v1/chat/completions"
+api_key "$KEY"
+system "CHANGED-COMPOSER-BASE"
+project_context no
+auto_compact no
+EOF
+run_fugu -r "$epoch_id" -p "select restricted drift variant"
+ok "System epoch composition drift: restricted resume succeeds" \
+ $([ "$rc" -eq 0 ]; echo $?)
+grep -q 'DRIFT-RESTRICTED-V0' "$dir/reqout" && \
+ grep -q 'OPENAI-SUMMARY-MARKER' "$dir/reqout" && \
+ ! grep -Eq 'DRIFT-FULL-V0|CURRENT-COMPOSER-BASE|CHANGED-COMPOSER-BASE' \
+ "$dir/reqout"
+ok "System epoch composition drift: restricted bytes remain authoritative" $?
+stop_stub
+
+start_stub "OAI_OK"
+cat > "$dir/fugu.conf" <<EOF
+model "unknown-window-fixture"
+provider "openai"
+api_host "127.0.0.1"
+api_port "$stubport"
+api_path "/v1/chat/completions"
+api_key "$KEY"
+system "CHANGED-COMPOSER-BASE"
+project_context yes
+auto_compact no
+EOF
+run_fugu -r "$epoch_id" -p "toggle back to full drift variant"
+ok "System epoch composition drift: toggle-back resume succeeds" \
+ $([ "$rc" -eq 0 ]; echo $?)
+grep -q 'DRIFT-FULL-V0' "$dir/reqout" && \
+ grep -q 'OPENAI-SUMMARY-MARKER' "$dir/reqout" && \
+ ! grep -Eq 'DRIFT-RESTRICTED-V0|CURRENT-COMPOSER-BASE|CHANGED-COMPOSER-BASE' \
+ "$dir/reqout"
+ok "System epoch composition drift: toggle-back selects stored full bytes" $?
+stop_stub
+
# --- 24: /compact summarizes and replaces the context (behavior.md 3)
rm -rf "$HOME/.fugu"
start_stub "OK SUMMARY OK"