mirror of
https://github.com/akitaonrails/ai-memory.git
synced 2026-10-02 03:24:46 +08:00
Forward-merge of the nine PRs that landed on main (the 2.0.4 batch: #642 auth stale-bearer, #646/#640 LoginLimiter, #644 Cursor attribution, #650/#647 reindex manifest, #638 MCP routing, #652 CI docs, #645 dev-loop/build) into the 2.1 feature train, so release/2.1 carries every fix before 2.1.0 is cut. Conflicts resolved: - crates/ai-memory-wiki/src/wiki.rs: 2.1's per-page write lock (page_locks, #607) and main's manifested_scopes memo (#650) are independent additions to the same struct/imports/constructor — kept both; imports merged to {HashMap, HashSet}. - CHANGELOG.md: [Unreleased] now carries 2.1's ### Added features above main's ### Changed + ### Fixed (the 2.0.4 fixes), Keep-a-Changelog order, single [2.0.3] section preserved. - crates/ai-memory-llm/tests/extra_headers_on_the_wire.rs (2.1's #606 test) relocated into tests/suite/ and declared in mod.rs to satisfy #645's one-test-binary-per-crate harness convention (caught by the repo_layout guard). fmt, clippy -D warnings, llm harness, and the repo_layout guard all green.
1716 lines
72 KiB
Bash
Executable File
1716 lines
72 KiB
Bash
Executable File
#!/usr/bin/env bash
|
|
# Manual, opt-in acceptance test for managed cross-harness workstreams.
|
|
# This is intentionally not called by CI: the real-harness phase uses the
|
|
# operator's installed CLIs, credentials, model defaults, and native stores.
|
|
set -euo pipefail
|
|
|
|
ROOT=$(cd "$(dirname "${BASH_SOURCE[0]}")/.." && pwd)
|
|
BIN=${AI_MEMORY_ACCEPTANCE_BIN:-"$ROOT/target/debug/ai-memory"}
|
|
KEEP=${AI_MEMORY_ACCEPTANCE_KEEP:-0}
|
|
DETERMINISTIC_ONLY=${AI_MEMORY_ACCEPTANCE_DETERMINISTIC_ONLY:-0}
|
|
HARNESS_WORDS=${AI_MEMORY_ACCEPTANCE_HARNESSES:-"claude codex opencode pi crush omp kimi command-code kiro grok antigravity"}
|
|
TMP=$(mktemp -d "${TMPDIR:-/tmp}/ai-memory-workstream-acceptance.XXXXXX")
|
|
DATA="$TMP/data"
|
|
REPO="$TMP/repo"
|
|
CONFIG="$TMP/config"
|
|
LOGS="$TMP/logs"
|
|
SERVER_PID=""
|
|
|
|
cleanup() {
|
|
local code=$?
|
|
if [ -n "$SERVER_PID" ]; then
|
|
kill "$SERVER_PID" 2>/dev/null || true
|
|
wait "$SERVER_PID" 2>/dev/null || true
|
|
fi
|
|
if [ "$KEEP" = 1 ] || [ "$code" -ne 0 ]; then
|
|
printf 'acceptance artifacts retained at %s\n' "$TMP" >&2
|
|
else
|
|
rm -rf "$TMP"
|
|
fi
|
|
}
|
|
trap cleanup EXIT INT TERM
|
|
|
|
for command in cargo curl diff git jq script sqlite3; do
|
|
command -v "$command" >/dev/null 2>&1 || {
|
|
printf 'missing required command: %s\n' "$command" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
|
|
if [ ! -x "$BIN" ] || [ "${AI_MEMORY_ACCEPTANCE_REBUILD:-1}" = 1 ]; then
|
|
(cd "$ROOT" && cargo build -p ai-memory-cli)
|
|
fi
|
|
|
|
mkdir -p "$DATA" "$REPO" "$CONFIG" "$LOGS"
|
|
git -C "$REPO" init -q
|
|
git -C "$REPO" config user.name "ai-memory acceptance"
|
|
git -C "$REPO" config user.email "acceptance@localhost"
|
|
printf '# Managed workstream acceptance\n' >"$REPO/README.md"
|
|
git -C "$REPO" add README.md
|
|
git -C "$REPO" commit -q --no-gpg-sign -m "acceptance fixture"
|
|
|
|
TOKEN="managed-acceptance-$(date +%s)-$$"
|
|
PORT=${AI_MEMORY_ACCEPTANCE_PORT:-$((52000 + ($$ % 10000)))}
|
|
for _ in $(seq 1 50); do
|
|
if ! curl -sS --max-time 0.1 "http://127.0.0.1:$PORT/" >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
PORT=$((PORT + 1))
|
|
done
|
|
URL="http://127.0.0.1:$PORT"
|
|
export AI_MEMORY_SERVER_URL="$URL"
|
|
export AI_MEMORY_AUTH_TOKEN="$TOKEN"
|
|
export AI_MEMORY_NO_VERSION_CHECK=1
|
|
|
|
"$BIN" --data-dir "$DATA" serve \
|
|
--transport http \
|
|
--bind "127.0.0.1:$PORT" \
|
|
--no-watcher >"$LOGS/server.log" 2>&1 &
|
|
SERVER_PID=$!
|
|
for _ in $(seq 1 100); do
|
|
status=$(curl -sS --max-time 0.2 -o /dev/null -w '%{http_code}' \
|
|
-H "Authorization: Bearer $TOKEN" \
|
|
"$URL/workstream/not-a-uuid/events" 2>/dev/null || true)
|
|
[ "$status" = 400 ] && break
|
|
if ! kill -0 "$SERVER_PID" 2>/dev/null; then
|
|
printf 'ai-memory server exited during startup\n' >&2
|
|
tail -80 "$LOGS/server.log" >&2
|
|
exit 1
|
|
fi
|
|
sleep 0.1
|
|
done
|
|
[ "${status:-}" = 400 ] || {
|
|
printf 'ai-memory server did not become ready at %s\n' "$URL" >&2
|
|
tail -80 "$LOGS/server.log" >&2
|
|
exit 1
|
|
}
|
|
|
|
workstream_hex() {
|
|
local name=$1
|
|
sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT lower(hex(id)) FROM workstreams WHERE name = '$name' ORDER BY selected_at DESC LIMIT 1;"
|
|
}
|
|
|
|
latest_workstream_sequence() {
|
|
local hex=$1
|
|
sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT COALESCE(MAX(sequence), 0) FROM workstream_events WHERE workstream_id = x'$hex';"
|
|
}
|
|
|
|
current_delivery_cursor() {
|
|
local hex=$1
|
|
local agent=$2
|
|
sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT COALESCE((
|
|
SELECT delivery_cursor FROM workstream_native_sessions
|
|
WHERE workstream_id = x'$hex'
|
|
AND agent_kind = '$agent'
|
|
AND is_current = 1
|
|
), 0);"
|
|
}
|
|
|
|
agent_observation_count() {
|
|
local agent=$1
|
|
sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT COUNT(*)
|
|
FROM observations AS o
|
|
JOIN sessions AS s ON s.id = o.session_id
|
|
WHERE s.agent_kind = '$agent';"
|
|
}
|
|
|
|
# Assert the deterministic boundaries of one real or fake managed leg:
|
|
# the harness produced new portable evidence, and any assigned context delta
|
|
# was acknowledged by its hook/launcher delivery path. Adapters with readable
|
|
# transcripts persist an assistant event. Antigravity deliberately does not
|
|
# decode private trajectory protobuf, so its startup hook must instead persist
|
|
# an observation and link the exact native conversation. Model recall is not a
|
|
# protocol oracle: large hook results may be file-backed, and whether a model
|
|
# chooses to read that file must not decide acceptance.
|
|
assert_managed_leg() {
|
|
local hex=$1
|
|
local expected_agent=$2
|
|
local before_sequence=$3
|
|
local before_delivery=$4
|
|
local expect_context=$5
|
|
local log=$6
|
|
local before_observations=${7:-0}
|
|
local native_id delivery sync_after sync_through context_delivered after_observations
|
|
|
|
if [ "$expected_agent" = antigravity-cli ]; then
|
|
native_id=$(sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT native_session_id FROM workstream_native_sessions
|
|
WHERE workstream_id = x'$hex'
|
|
AND agent_kind = '$expected_agent'
|
|
AND is_current = 1;")
|
|
[ -n "$native_id" ] || {
|
|
printf '%s did not link a native conversation\n' "$expected_agent" >&2
|
|
tail -120 "$log" >&2
|
|
return 1
|
|
}
|
|
after_observations=$(agent_observation_count "$expected_agent")
|
|
[ "$after_observations" -gt "$before_observations" ] || {
|
|
printf '%s did not persist a startup-hook observation (before: %s, after: %s)\n' \
|
|
"$expected_agent" "$before_observations" "$after_observations" >&2
|
|
tail -120 "$log" >&2
|
|
return 1
|
|
}
|
|
else
|
|
native_id=$(sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT native_session_id FROM workstream_events
|
|
WHERE workstream_id = x'$hex'
|
|
AND sequence > $before_sequence
|
|
AND agent_kind = '$expected_agent'
|
|
AND role = 'assistant'
|
|
ORDER BY sequence DESC LIMIT 1;")
|
|
[ -n "$native_id" ] || {
|
|
printf '%s did not persist a new assistant event after ledger sequence %s\n' \
|
|
"$expected_agent" "$before_sequence" >&2
|
|
tail -120 "$log" >&2
|
|
return 1
|
|
}
|
|
fi
|
|
|
|
if [ "$expect_context" = 1 ]; then
|
|
if [ "$before_sequence" -le "$before_delivery" ]; then
|
|
printf '%s had no managed context delta to assign (ledger: %s, cursor: %s)\n' \
|
|
"$expected_agent" "$before_sequence" "$before_delivery" >&2
|
|
tail -120 "$log" >&2
|
|
return 1
|
|
fi
|
|
delivery=$(sqlite3 -separator '|' "$DATA/db/memory.sqlite" \
|
|
"SELECT sync_after, sync_through, context_delivered
|
|
FROM managed_runs
|
|
WHERE workstream_id = x'$hex' AND agent_kind = '$expected_agent'
|
|
ORDER BY started_at DESC LIMIT 1;")
|
|
IFS='|' read -r sync_after sync_through context_delivered <<<"$delivery"
|
|
if [ -z "$delivery" ] || [ "${sync_through:-0}" -ne "$before_sequence" ] ||
|
|
[ "${context_delivered:-0}" -ne 1 ]; then
|
|
printf '%s did not acknowledge its assigned managed context delta (state: %s)\n' \
|
|
"$expected_agent" "${delivery:-missing}" >&2
|
|
tail -120 "$log" >&2
|
|
return 1
|
|
fi
|
|
fi
|
|
|
|
printf '%s\n' "$native_id"
|
|
}
|
|
|
|
FAKE="$TMP/fake-harness.sh"
|
|
cat >"$FAKE" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
case "${AI_MEMORY_ACCEPTANCE_FAKE_MODE:-argv}" in
|
|
argv)
|
|
printf '%s\n' "$@" >"$AI_MEMORY_ACCEPTANCE_ARGV_LOG"
|
|
;;
|
|
exit)
|
|
exit "${AI_MEMORY_ACCEPTANCE_EXIT_CODE:-23}"
|
|
;;
|
|
lease)
|
|
: >"$AI_MEMORY_ACCEPTANCE_STARTED"
|
|
sleep "${AI_MEMORY_ACCEPTANCE_SLEEP:-3}"
|
|
;;
|
|
crush)
|
|
printf '%s\n' "$@" >"$AI_MEMORY_ACCEPTANCE_ARGV_LOG"
|
|
printf '%s\n' "$CRUSH_GLOBAL_CONFIG" >"$AI_MEMORY_ACCEPTANCE_CRUSH_ENV_LOG"
|
|
cp "$CRUSH_GLOBAL_CONFIG/crush.json" "$AI_MEMORY_ACCEPTANCE_CRUSH_CONFIG_LOG"
|
|
packet=$(jq -r '.options.global_context_paths[-1]' "$CRUSH_GLOBAL_CONFIG/crush.json")
|
|
cp "$packet" "$AI_MEMORY_ACCEPTANCE_CRUSH_PACKET_LOG"
|
|
;;
|
|
kiro)
|
|
printf '%s\n' "$@" >"$AI_MEMORY_ACCEPTANCE_ARGV_LOG"
|
|
# Honor `--resume-id <id>` (resume); a fresh launch mints its own id
|
|
# because Kiro CLI session ids are server-assigned UUIDs.
|
|
session_id=""
|
|
previous_arg=""
|
|
for arg in "$@"; do
|
|
if [ "$previous_arg" = --resume-id ]; then
|
|
session_id=$arg
|
|
fi
|
|
previous_arg=$arg
|
|
done
|
|
if [ -z "$session_id" ]; then
|
|
if command -v uuidgen >/dev/null 2>&1; then
|
|
session_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
|
elif [ -r /proc/sys/kernel/random/uuid ]; then
|
|
session_id=$(cat /proc/sys/kernel/random/uuid)
|
|
else
|
|
printf 'kiro fake mode needs uuidgen or /proc/sys/kernel/random/uuid\n' >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
# Real layout: flat store, one `<uuid>.json` metadata + `<uuid>.jsonl`
|
|
# event-stream pair per session; discovery must read the metadata cwd.
|
|
store="${KIRO_HOME:?kiro fake mode requires KIRO_HOME}/sessions/cli"
|
|
mkdir -p "$store"
|
|
stream="$store/$session_id.jsonl"
|
|
if [ ! -f "$store/$session_id.json" ]; then
|
|
printf '{"session_id":"%s","cwd":"%s","created_at":"2026-08-01T10:00:00Z","updated_at":"2026-08-01T10:00:00Z","title":"acceptance","session_state":{"version":"v1","conversation_metadata":{}}}\n' \
|
|
"$session_id" "$PWD" >"$store/$session_id.json"
|
|
: >"$stream"
|
|
fi
|
|
sentinel=${AI_MEMORY_ACCEPTANCE_SENTINEL:-AMWS-FAKE-KIRO}
|
|
printf '{"version":"v1","kind":"Prompt","data":{"message_id":"m-%s-u","content":[{"kind":"text","data":"%s"}],"meta":{"timestamp":%s}}}\n' \
|
|
"$(date +%s)" "$sentinel" "$(date +%s)000" >>"$stream"
|
|
printf '{"version":"v1","kind":"AssistantMessage","data":{"message_id":"m-%s-a","content":[{"kind":"text","data":"%s reply"}],"meta":{"timestamp":%s}}}\n' \
|
|
"$(date +%s)" "$sentinel" "$(date +%s)000" >>"$stream"
|
|
;;
|
|
kiro-v3)
|
|
printf '%s\n' "$@" >"$AI_MEMORY_ACCEPTANCE_ARGV_LOG"
|
|
session_id=""
|
|
previous_arg=""
|
|
for arg in "$@"; do
|
|
if [ "$previous_arg" = --resume-id ]; then
|
|
session_id=$arg
|
|
fi
|
|
previous_arg=$arg
|
|
done
|
|
if [ -z "$session_id" ]; then
|
|
if command -v uuidgen >/dev/null 2>&1; then
|
|
session_id="sess_$(uuidgen | tr '[:upper:]' '[:lower:]')"
|
|
elif [ -r /proc/sys/kernel/random/uuid ]; then
|
|
session_id="sess_$(cat /proc/sys/kernel/random/uuid)"
|
|
else
|
|
printf 'kiro v3 fake mode needs uuidgen or /proc/sys/kernel/random/uuid\n' >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
session_dir="${KIRO_HOME:?kiro v3 fake mode requires KIRO_HOME}/sessions/checkout_fixture/$session_id"
|
|
mkdir -p "$session_dir"
|
|
stream="$session_dir/messages.jsonl"
|
|
if [ ! -f "$session_dir/session.json" ]; then
|
|
printf '{"schemaVersion":"1.0.0","dataModelVersion":1,"id":"%s","workspacePaths":["%s"],"createdAt":"2026-08-06T10:00:00Z","lastModifiedAt":"2026-08-06T10:00:00Z","agentMode":"vibe","status":"idle"}\n' \
|
|
"$session_id" "$PWD" >"$session_dir/session.json"
|
|
: >"$stream"
|
|
fi
|
|
sentinel=${AI_MEMORY_ACCEPTANCE_SENTINEL:-AMWS-FAKE-KIRO-V3}
|
|
printf '{"id":"u-%s","timestamp":"2026-08-06T10:00:00Z","payload":{"type":"user","content":"%s"}}\n' \
|
|
"$(date +%s)" "$sentinel" >>"$stream"
|
|
printf '{"id":"a-%s","timestamp":"2026-08-06T10:00:01Z","payload":{"type":"assistant","operationType":"Say","content":"%s reply"}}\n' \
|
|
"$(date +%s)" "$sentinel" >>"$stream"
|
|
;;
|
|
kimi)
|
|
printf '%s\n' "$@" >"$AI_MEMORY_ACCEPTANCE_ARGV_LOG"
|
|
# Honor `--session <id>` (resume); a fresh launch mints its own id
|
|
# because Kimi Code cannot accept a caller-supplied session id.
|
|
session_id=""
|
|
previous_arg=""
|
|
for arg in "$@"; do
|
|
if [ "$previous_arg" = --session ]; then
|
|
session_id=$arg
|
|
fi
|
|
previous_arg=$arg
|
|
done
|
|
if [ -z "$session_id" ]; then
|
|
session_id="session_acceptance-$(date +%s)-$$"
|
|
fi
|
|
# The bucket directory name is intentionally opaque: the real layout
|
|
# hashes the working directory one-way, and discovery must read
|
|
# state.json's current cwd field instead of parsing the bucket name.
|
|
session_dir="${KIMI_CODE_HOME:?kimi fake mode requires KIMI_CODE_HOME}/sessions/wd_fixture_bucket/$session_id"
|
|
mkdir -p "$session_dir/agents/main"
|
|
wire="$session_dir/agents/main/wire.jsonl"
|
|
if [ ! -f "$session_dir/state.json" ]; then
|
|
printf '{"id":"%s","version":2,"cwd":"%s"}\n' \
|
|
"$session_id" "$PWD" >"$session_dir/state.json"
|
|
printf '{"type":"metadata","protocol_version":"1","created_at":%s}\n' \
|
|
"$(date +%s)000" >"$wire"
|
|
fi
|
|
sentinel=${AI_MEMORY_ACCEPTANCE_SENTINEL:-AMWS-FAKE-KIMI}
|
|
printf '{"type":"context.append_message","time":%s,"message":{"role":"user","content":[{"type":"text","text":"%s"}],"toolCalls":[]}}\n' \
|
|
"$(date +%s)000" "$sentinel" >>"$wire"
|
|
printf '{"type":"context.append_message","time":%s,"message":{"role":"assistant","content":[{"type":"text","text":"%s reply"}],"toolCalls":[]}}\n' \
|
|
"$(date +%s)000" "$sentinel" >>"$wire"
|
|
;;
|
|
command-code)
|
|
printf '%s\n' "$@" >"$AI_MEMORY_ACCEPTANCE_ARGV_LOG"
|
|
session_id=""
|
|
previous_arg=""
|
|
for arg in "$@"; do
|
|
if [ "$previous_arg" = --session ]; then
|
|
session_id=$arg
|
|
fi
|
|
previous_arg=$arg
|
|
done
|
|
if [ -z "$session_id" ]; then
|
|
if command -v uuidgen >/dev/null 2>&1; then
|
|
session_id=$(uuidgen | tr '[:upper:]' '[:lower:]')
|
|
elif [ -r /proc/sys/kernel/random/uuid ]; then
|
|
session_id=$(cat /proc/sys/kernel/random/uuid)
|
|
else
|
|
printf 'command-code fake mode needs uuidgen or /proc/sys/kernel/random/uuid\n' >&2
|
|
exit 1
|
|
fi
|
|
fi
|
|
store="$HOME/.commandcode/projects/opaque_fixture_slug"
|
|
mkdir -p "$store"
|
|
stream="$store/$session_id.jsonl"
|
|
if [ ! -f "$stream" ]; then
|
|
printf '{"type":"session","version":3,"id":"%s","timestamp":"2026-08-07T17:00:00Z","cwd":"%s"}\n' \
|
|
"$session_id" "$PWD" >"$stream"
|
|
fi
|
|
sentinel=${AI_MEMORY_ACCEPTANCE_SENTINEL:-AMWS-FAKE-COMMAND-CODE}
|
|
now=$(date +%s)
|
|
printf '{"type":"message","id":"u-%s","parentId":null,"timestamp":"2026-08-07T17:00:01Z","message":{"role":"user","content":[{"type":"text","text":"%s"}],"meta":{"source":"user"}}}\n' \
|
|
"$now" "$sentinel" >>"$stream"
|
|
printf '{"type":"message","id":"a-%s","parentId":"u-%s","timestamp":"2026-08-07T17:00:02Z","message":{"role":"assistant","content":[{"type":"thinking","thinking":"private reasoning","signature":"private signature"},{"type":"text","text":"%s reply"}],"meta":{"source":"model"}}}\n' \
|
|
"$now" "$now" "$sentinel" >>"$stream"
|
|
;;
|
|
grok)
|
|
printf '%s\n' "$@" >"$AI_MEMORY_ACCEPTANCE_ARGV_LOG"
|
|
# Honor the wrapper-owned `--session-id <id>` (fresh) and `--resume <id>`
|
|
# (returning) selectors the way the real CLI does.
|
|
session_id=""
|
|
previous_arg=""
|
|
for arg in "$@"; do
|
|
case "$previous_arg" in
|
|
--session-id | --resume) session_id=$arg ;;
|
|
esac
|
|
previous_arg=$arg
|
|
done
|
|
[ -n "$session_id" ] || session_id="019f-fake-$(date +%s)-$$"
|
|
# The bucket directory name is a URL-encoded cwd in the real layout, but
|
|
# discovery must read summary.json's info.cwd instead of parsing it.
|
|
session_dir="${GROK_HOME:?grok fake mode requires GROK_HOME}/sessions/%2Ffixture%2Fbucket/$session_id"
|
|
mkdir -p "$session_dir"
|
|
chat="$session_dir/chat_history.jsonl"
|
|
if [ ! -f "$session_dir/summary.json" ]; then
|
|
printf '{"info":{"id":"%s","cwd":"%s"}}\n' "$session_id" "$PWD" >"$session_dir/summary.json"
|
|
printf '{"type":"system","content":"fake grok system prompt"}\n' >"$chat"
|
|
fi
|
|
sentinel=${AI_MEMORY_ACCEPTANCE_SENTINEL:-AMWS-FAKE-GROK}
|
|
printf '{"type":"user","content":[{"type":"text","text":"%s"}]}\n' "$sentinel" >>"$chat"
|
|
printf '{"type":"assistant","content":"%s reply"}\n' "$sentinel" >>"$chat"
|
|
;;
|
|
antigravity)
|
|
printf '%s\n' "$@" >"$AI_MEMORY_ACCEPTANCE_ARGV_LOG"
|
|
session_id=""
|
|
previous_arg=""
|
|
for arg in "$@"; do
|
|
if [ "$previous_arg" = --conversation ]; then
|
|
session_id=$arg
|
|
fi
|
|
previous_arg=$arg
|
|
done
|
|
if [ -z "$session_id" ]; then
|
|
session_id=${AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_SESSION_ID:?antigravity fake mode requires a fresh session id}
|
|
fi
|
|
conversations="$HOME/.gemini/antigravity-cli/conversations"
|
|
mkdir -p "$conversations"
|
|
database="$conversations/$session_id.db"
|
|
if [ ! -f "$database" ]; then
|
|
uri="file://$PWD"
|
|
uri_hex=$(printf '%s' "$uri" | od -An -tx1 | tr -d ' \n')
|
|
printf -v uri_length '%02x' "${#uri}"
|
|
nested="0a${uri_length}${uri_hex}"
|
|
printf -v nested_length '%02x' "$((2 + ${#uri}))"
|
|
metadata="0a${nested_length}${nested}"
|
|
sqlite3 "$database" \
|
|
"CREATE TABLE trajectory_metadata_blob (id text DEFAULT 'main', data blob, PRIMARY KEY (id));
|
|
INSERT INTO trajectory_metadata_blob (id, data) VALUES ('main', x'$metadata');
|
|
CREATE TABLE trajectory_blob (data blob);
|
|
INSERT INTO trajectory_blob (data) VALUES (x'414d57532d505249564154452d5452414a4543544f5259');"
|
|
fi
|
|
payload=$(jq -nc --arg id "$session_id" --arg cwd "$PWD" \
|
|
'{invocationNum: 0, conversationId: $id, workspacePaths: [$cwd]}')
|
|
printf '%s' "$payload" | \
|
|
AI_MEMORY_HOOK_URL="${AI_MEMORY_SERVER_URL:?}" \
|
|
"$AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_HOOK" \
|
|
>"$AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_HOOK_LOG"
|
|
;;
|
|
esac
|
|
EOF
|
|
chmod +x "$FAKE"
|
|
|
|
printf 'running deterministic wrapper edge checks\n'
|
|
|
|
# Utility invocations must not discover and import another process's recent
|
|
# session merely because it is active in the same checkout.
|
|
UTILITY_CODEX_HOME="$CONFIG/utility-codex"
|
|
mkdir -p "$UTILITY_CODEX_HOME/sessions/2026/01/01"
|
|
printf '%s\n%s\n' \
|
|
"{\"type\":\"session_meta\",\"payload\":{\"id\":\"utility-unrelated\",\"cwd\":\"$REPO\"}}" \
|
|
'{"type":"response_item","payload":{"type":"message","role":"user","content":[{"type":"input_text","text":"must not import"}]}}' \
|
|
>"$UTILITY_CODEX_HOME/sessions/2026/01/01/rollout-utility.jsonl"
|
|
(
|
|
cd "$REPO"
|
|
CODEX_HOME="$UTILITY_CODEX_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=argv \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/utility-argv.log" \
|
|
"$BIN" --data-dir "$DATA" run --new edge-utility --executable "$FAKE" \
|
|
codex --version >"$LOGS/edge-utility.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --version) "$TMP/utility-argv.log"
|
|
# The repository checkpoint is still recorded; the unrelated user message is
|
|
# not. A buggy post-exit discovery path reports two imported events here.
|
|
grep -q "workstream 'edge-utility' saved 1 new event(s)" "$LOGS/edge-utility.log"
|
|
|
|
(
|
|
cd "$REPO"
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=argv \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/argv.log" \
|
|
"$BIN" --data-dir "$DATA" run --new edge-argv --executable "$FAKE" \
|
|
codex --yolo -m gpt-5 "prompt words" >"$LOGS/edge-argv.log" 2>&1
|
|
)
|
|
diff -u \
|
|
<(printf '%s\n' -m gpt-5 "prompt words" --dangerously-bypass-approvals-and-sandbox) \
|
|
"$TMP/argv.log"
|
|
|
|
set +e
|
|
(
|
|
cd "$REPO"
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=exit AI_MEMORY_ACCEPTANCE_EXIT_CODE=23 \
|
|
"$BIN" --data-dir "$DATA" run --new edge-exit --executable "$FAKE" \
|
|
codex >"$LOGS/edge-exit.log" 2>&1
|
|
)
|
|
exit_code=$?
|
|
set -e
|
|
[ "$exit_code" -eq 23 ] || {
|
|
printf 'managed child exit code was %s, expected 23\n' "$exit_code" >&2
|
|
exit 1
|
|
}
|
|
|
|
(
|
|
cd "$REPO"
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=lease \
|
|
AI_MEMORY_ACCEPTANCE_SLEEP=7 \
|
|
AI_MEMORY_ACCEPTANCE_STARTED="$TMP/lease-started" \
|
|
"$BIN" --data-dir "$DATA" run --new edge-lease --executable "$FAKE" \
|
|
codex >"$LOGS/edge-lease-owner.log" 2>&1
|
|
) &
|
|
lease_pid=$!
|
|
for _ in $(seq 1 100); do
|
|
[ -f "$TMP/lease-started" ] && break
|
|
sleep 0.05
|
|
done
|
|
[ -f "$TMP/lease-started" ] || {
|
|
printf 'lease owner did not start\n' >&2
|
|
exit 1
|
|
}
|
|
set +e
|
|
(
|
|
cd "$REPO"
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=argv \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/lease-contender-argv.log" \
|
|
"$BIN" --data-dir "$DATA" run --workstream edge-lease --executable "$FAKE" \
|
|
codex >"$LOGS/edge-lease-contender.log" 2>&1
|
|
)
|
|
lease_code=$?
|
|
set -e
|
|
[ "$lease_code" -ne 0 ] || {
|
|
printf 'a concurrent managed writer unexpectedly acquired the lease\n' >&2
|
|
exit 1
|
|
}
|
|
wait "$lease_pid"
|
|
|
|
# Bare run must fail before contacting the server when this checkout has no
|
|
# native session in any auto-detected harness.
|
|
mkdir -p "$CONFIG/empty-home"
|
|
set +e
|
|
(
|
|
cd "$REPO"
|
|
HOME="$CONFIG/empty-home" XDG_DATA_HOME="$CONFIG/empty-home/xdg-data" \
|
|
"$BIN" --data-dir "$DATA" run >"$LOGS/edge-auto-empty.log" 2>&1
|
|
)
|
|
auto_empty_code=$?
|
|
set -e
|
|
[ "$auto_empty_code" -ne 0 ] || {
|
|
printf 'bare run unexpectedly started without a checkout-local session\n' >&2
|
|
exit 1
|
|
}
|
|
# The harness list in this message grows as adapters join the automatic
|
|
# pool (Kimi joined after Crush), so match the stable tail instead.
|
|
grep -q 'session was found for this directory' \
|
|
"$LOGS/edge-auto-empty.log"
|
|
|
|
# On a new workstream, bare run automatically adopts the newest local session.
|
|
AUTO_HOME="$CONFIG/auto-home"
|
|
AUTO_CODEX_HOME="$AUTO_HOME/.codex"
|
|
AUTO_CLAUDE_HOME="$AUTO_HOME/.claude"
|
|
AUTO_COMMAND_CODE_HOME="$AUTO_HOME/.commandcode"
|
|
AUTO_BIN="$AUTO_HOME/bin"
|
|
mkdir -p "$AUTO_CODEX_HOME/sessions/2026/01/01" \
|
|
"$AUTO_CLAUDE_HOME/projects/fixture" \
|
|
"$AUTO_COMMAND_CODE_HOME/projects/opaque_fixture_slug" "$AUTO_BIN"
|
|
ln -s "$FAKE" "$AUTO_BIN/codex"
|
|
ln -s "$FAKE" "$AUTO_BIN/claude"
|
|
ln -s "$FAKE" "$AUTO_BIN/command-code"
|
|
printf '{"sessionId":"auto-claude-old","cwd":"%s"}\n' "$REPO" \
|
|
>"$AUTO_CLAUDE_HOME/projects/fixture/auto-claude-old.jsonl"
|
|
sleep 1
|
|
printf '{"type":"session_meta","payload":{"id":"auto-codex-new","cwd":"%s"}}\n' \
|
|
"$REPO" >"$AUTO_CODEX_HOME/sessions/2026/01/01/rollout-auto.jsonl"
|
|
sleep 1
|
|
AUTO_COMMAND_CODE_ID="7c1d5698-204a-4c0f-ae9c-43db7fc4e41d"
|
|
printf '{"type":"session","version":3,"id":"%s","timestamp":"2026-08-07T17:00:00Z","cwd":"%s"}\n' \
|
|
"$AUTO_COMMAND_CODE_ID" "$REPO" \
|
|
>"$AUTO_COMMAND_CODE_HOME/projects/opaque_fixture_slug/$AUTO_COMMAND_CODE_ID.jsonl"
|
|
(
|
|
cd "$REPO"
|
|
HOME="$AUTO_HOME" CODEX_HOME="$AUTO_CODEX_HOME" \
|
|
CLAUDE_CONFIG_DIR="$AUTO_CLAUDE_HOME" PATH="$AUTO_BIN:$PATH" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=argv \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/auto-newest-argv.log" \
|
|
"$BIN" --data-dir "$DATA" run --workspace edge-auto --project edge-auto --yolo \
|
|
>"$LOGS/edge-auto-newest.log" 2>&1
|
|
)
|
|
diff -u \
|
|
<(printf '%s\n' --session "$AUTO_COMMAND_CODE_ID" --yolo) \
|
|
"$TMP/auto-newest-argv.log"
|
|
|
|
# Establish Claude after Command Code, then verify bare run follows the managed
|
|
# workstream's current Claude link instead of newer but obsolete native files.
|
|
(
|
|
cd "$REPO"
|
|
HOME="$AUTO_HOME" CLAUDE_CONFIG_DIR="$AUTO_CLAUDE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=argv \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/auto-claude-first-argv.log" \
|
|
"$BIN" --data-dir "$DATA" run --workspace edge-auto --project edge-auto \
|
|
--executable "$FAKE" claude >"$LOGS/edge-auto-claude-first.log" 2>&1
|
|
)
|
|
mapfile -t auto_claude_first <"$TMP/auto-claude-first-argv.log"
|
|
[ "${auto_claude_first[0]:-}" = --session-id ] || {
|
|
printf 'Claude did not create a fresh managed session\n' >&2
|
|
exit 1
|
|
}
|
|
auto_claude_id=${auto_claude_first[1]}
|
|
printf '{"sessionId":"%s","cwd":"%s"}\n' "$auto_claude_id" "$REPO" \
|
|
>"$AUTO_CLAUDE_HOME/projects/fixture/$auto_claude_id.jsonl"
|
|
(
|
|
cd "$REPO"
|
|
HOME="$AUTO_HOME" CODEX_HOME="$AUTO_CODEX_HOME" \
|
|
CLAUDE_CONFIG_DIR="$AUTO_CLAUDE_HOME" PATH="$AUTO_BIN:$PATH" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=argv \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/auto-managed-precedence-argv.log" \
|
|
"$BIN" --data-dir "$DATA" run --workspace edge-auto --project edge-auto \
|
|
>"$LOGS/edge-auto-managed-precedence.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --resume "$auto_claude_id") \
|
|
"$TMP/auto-managed-precedence-argv.log"
|
|
|
|
# A handled failure after lease acquisition must release immediately. A
|
|
# malformed Crush config fails after context fetch; the next run below would
|
|
# hit a stale 409 if cancellation were missing.
|
|
BAD_CRUSH_CONFIG="$CONFIG/bad-crush"
|
|
mkdir -p "$BAD_CRUSH_CONFIG"
|
|
printf '{not-json\n' >"$BAD_CRUSH_CONFIG/crush.json"
|
|
set +e
|
|
(
|
|
cd "$REPO"
|
|
HOME="$AUTO_HOME" CRUSH_GLOBAL_CONFIG="$BAD_CRUSH_CONFIG" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=crush \
|
|
"$BIN" --data-dir "$DATA" run --workspace edge-auto --project edge-auto \
|
|
--executable "$FAKE" crush >"$LOGS/edge-crush-invalid-config.log" 2>&1
|
|
)
|
|
bad_crush_code=$?
|
|
set -e
|
|
[ "$bad_crush_code" -ne 0 ] || {
|
|
printf 'malformed Crush config unexpectedly succeeded\n' >&2
|
|
exit 1
|
|
}
|
|
grep -q 'parsing Crush config' "$LOGS/edge-crush-invalid-config.log"
|
|
|
|
# Crush has no SessionStart hook. Verify the launcher fetches the packet into a
|
|
# temporary supported global-context config, then removes it after exit.
|
|
(
|
|
cd "$REPO"
|
|
HOME="$AUTO_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=crush \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/crush-context-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_CRUSH_ENV_LOG="$TMP/crush-context-env.log" \
|
|
AI_MEMORY_ACCEPTANCE_CRUSH_CONFIG_LOG="$TMP/crush-context-config.json" \
|
|
AI_MEMORY_ACCEPTANCE_CRUSH_PACKET_LOG="$TMP/crush-context-packet.md" \
|
|
"$BIN" --data-dir "$DATA" run --workspace edge-auto --project edge-auto \
|
|
--executable "$FAKE" --yolo crush >"$LOGS/edge-crush-context.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --yolo) "$TMP/crush-context-argv.log"
|
|
grep -q 'ai-memory managed workstream' "$TMP/crush-context-packet.md"
|
|
crush_context_dir=$(cat "$TMP/crush-context-env.log")
|
|
[ ! -e "$crush_context_dir" ] || {
|
|
printf 'temporary Crush context directory was not removed\n' >&2
|
|
exit 1
|
|
}
|
|
|
|
# Kimi fake-mode fixture: the fake kimi honors `--session <id>`, writes the
|
|
# native store layout ($KIMI_CODE_HOME/sessions/<bucket>/<id>/state.json plus
|
|
# agents/main/wire.jsonl), and appends the round sentinel as
|
|
# context.append_message records. A fresh launch injects no selector because
|
|
# Kimi Code cannot mint a caller-supplied session id; the wrapper links the
|
|
# session post-exit by exact-checkout discovery through state.json.
|
|
KIMI_FAKE_HOME="$CONFIG/kimi-fake"
|
|
mkdir -p "$KIMI_FAKE_HOME"
|
|
(
|
|
cd "$REPO"
|
|
KIMI_CODE_HOME="$KIMI_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=kimi \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/kimi-first-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-KIMI-ONE" \
|
|
"$BIN" --data-dir "$DATA" run --new edge-kimi --executable "$FAKE" \
|
|
kimi >"$LOGS/edge-kimi-first.log" 2>&1
|
|
)
|
|
# The fake echoes one line per argv element, so a zero-argument fresh launch
|
|
# leaves a single empty line in the log; assert no real argument arrived
|
|
# rather than asserting the file is byte-empty.
|
|
if grep -q . "$TMP/kimi-first-argv.log"; then
|
|
printf 'fresh kimi launch unexpectedly received a session selector\n' >&2
|
|
cat "$TMP/kimi-first-argv.log" >&2
|
|
exit 1
|
|
fi
|
|
kimi_session_dir=$(find "$KIMI_FAKE_HOME/sessions" -mindepth 2 -maxdepth 2 -type d -print -quit)
|
|
[ -n "$kimi_session_dir" ] || {
|
|
printf 'fake kimi did not create a native session store\n' >&2
|
|
exit 1
|
|
}
|
|
kimi_session_id=$(basename "$kimi_session_dir")
|
|
kimi_ws_hex=$(sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT lower(hex(id)) FROM workstreams WHERE name = 'edge-kimi' ORDER BY selected_at DESC LIMIT 1;")
|
|
[ "${#kimi_ws_hex}" -eq 32 ] || {
|
|
printf 'could not resolve the edge-kimi workstream id\n' >&2
|
|
exit 1
|
|
}
|
|
kimi_ws_id="${kimi_ws_hex:0:8}-${kimi_ws_hex:8:4}-${kimi_ws_hex:12:4}-${kimi_ws_hex:16:4}-${kimi_ws_hex:20:12}"
|
|
kimi_first_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$kimi_ws_id" --limit 100 --json "AMWS-FAKE-KIMI-ONE")
|
|
jq -e --arg id "$kimi_session_id" \
|
|
'[.[] | select(.agent == "kimi-code" and .role == "assistant" and (.content | contains("AMWS-FAKE-KIMI-ONE")) and .native_session_id == $id)] | length == 1' \
|
|
<<<"$kimi_first_hits" >/dev/null || {
|
|
printf 'kimi wire.jsonl sentinel was not imported from the discovered session\n' >&2
|
|
tail -80 "$LOGS/edge-kimi-first.log" >&2
|
|
exit 1
|
|
}
|
|
(
|
|
cd "$REPO"
|
|
KIMI_CODE_HOME="$KIMI_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=kimi \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/kimi-second-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-KIMI-TWO" \
|
|
"$BIN" --data-dir "$DATA" run --workstream edge-kimi --executable "$FAKE" \
|
|
kimi-cli >"$LOGS/edge-kimi-second.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --session "$kimi_session_id") "$TMP/kimi-second-argv.log"
|
|
kimi_second_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$kimi_ws_id" --limit 100 --json "AMWS-FAKE-KIMI")
|
|
jq -e \
|
|
'([.[] | select(.role == "assistant" and (.content | contains("AMWS-FAKE-KIMI-ONE")))] | length == 1)
|
|
and ([.[] | select(.role == "assistant" and (.content | contains("AMWS-FAKE-KIMI-TWO")))] | length == 1)' \
|
|
<<<"$kimi_second_hits" >/dev/null || {
|
|
printf 'kimi incremental import duplicated or missed a round sentinel\n' >&2
|
|
tail -80 "$LOGS/edge-kimi-second.log" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Deleting the linked native store must heal the established workstream instead
|
|
# of feeding the dead id back to Kimi forever.
|
|
rm -rf "$kimi_session_dir"
|
|
(
|
|
cd "$REPO"
|
|
KIMI_CODE_HOME="$KIMI_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=kimi \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/kimi-orphan-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-KIMI-RECOVERED" \
|
|
"$BIN" --data-dir "$DATA" run --workstream edge-kimi --executable "$FAKE" \
|
|
kimi >"$LOGS/edge-kimi-orphan.log" 2>&1
|
|
)
|
|
if grep -q . "$TMP/kimi-orphan-argv.log"; then
|
|
printf 'orphaned kimi session was not replaced by a fresh launch\n' >&2
|
|
cat "$TMP/kimi-orphan-argv.log" >&2
|
|
exit 1
|
|
fi
|
|
grep -q "linked kimi session .* is missing from its native store" \
|
|
"$LOGS/edge-kimi-orphan.log"
|
|
kimi_recovered_dir=$(find "$KIMI_FAKE_HOME/sessions" -mindepth 2 -maxdepth 2 -type d -print -quit)
|
|
[ -n "$kimi_recovered_dir" ] || {
|
|
printf 'orphan recovery did not create a replacement kimi session\n' >&2
|
|
exit 1
|
|
}
|
|
kimi_recovered_id=$(basename "$kimi_recovered_dir")
|
|
[ "$kimi_recovered_id" != "$kimi_session_id" ] || {
|
|
printf 'orphan recovery reused the deleted kimi session id\n' >&2
|
|
exit 1
|
|
}
|
|
kimi_current_id=$(sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT native_session_id FROM workstream_native_sessions WHERE workstream_id = x'${kimi_ws_hex}' AND agent_kind = 'kimi-code' AND is_current = 1;")
|
|
[ "$kimi_current_id" = "$kimi_recovered_id" ] || {
|
|
printf 'orphan recovery did not repoint the kimi workstream\n' >&2
|
|
exit 1
|
|
}
|
|
|
|
# Command Code v3 fixture: the fake creates an exact UUID transcript under an
|
|
# opaque project slug, then accepts the same `--session <uuid>` selector on the
|
|
# returning leg. HOME isolates the documented user store from real sessions.
|
|
COMMAND_CODE_FAKE_HOME="$CONFIG/command-code-fake"
|
|
mkdir -p "$COMMAND_CODE_FAKE_HOME"
|
|
(
|
|
cd "$REPO"
|
|
HOME="$COMMAND_CODE_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=command-code \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/command-code-first-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-COMMAND-CODE-ONE" \
|
|
"$BIN" --data-dir "$DATA" run --new edge-command-code --executable "$FAKE" \
|
|
--yolo command-code >"$LOGS/edge-command-code-first.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --yolo) "$TMP/command-code-first-argv.log"
|
|
command_code_session_file=$(find "$COMMAND_CODE_FAKE_HOME/.commandcode/projects" \
|
|
-mindepth 2 -maxdepth 2 -name '*.jsonl' ! -name '*.checkpoints.jsonl' -print -quit)
|
|
[ -n "$command_code_session_file" ] || {
|
|
printf 'fake command-code did not create a native session transcript\n' >&2
|
|
exit 1
|
|
}
|
|
command_code_session_id=$(basename "$command_code_session_file" .jsonl)
|
|
command_code_ws_hex=$(sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT lower(hex(id)) FROM workstreams WHERE name = 'edge-command-code' ORDER BY selected_at DESC LIMIT 1;")
|
|
[ "${#command_code_ws_hex}" -eq 32 ] || {
|
|
printf 'could not resolve the edge-command-code workstream id\n' >&2
|
|
exit 1
|
|
}
|
|
command_code_ws_id="${command_code_ws_hex:0:8}-${command_code_ws_hex:8:4}-${command_code_ws_hex:12:4}-${command_code_ws_hex:16:4}-${command_code_ws_hex:20:12}"
|
|
command_code_first_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$command_code_ws_id" --limit 100 --json "AMWS-FAKE-COMMAND-CODE-ONE")
|
|
jq -e --arg id "$command_code_session_id" \
|
|
'[.[] | select(.agent == "command-code" and .role == "assistant" and (.content | contains("AMWS-FAKE-COMMAND-CODE-ONE")) and .native_session_id == $id)] | length == 1' \
|
|
<<<"$command_code_first_hits" >/dev/null || {
|
|
printf 'command-code transcript sentinel was not imported\n' >&2
|
|
tail -80 "$LOGS/edge-command-code-first.log" >&2
|
|
exit 1
|
|
}
|
|
(
|
|
cd "$REPO"
|
|
HOME="$COMMAND_CODE_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=command-code \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/command-code-second-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-COMMAND-CODE-TWO" \
|
|
"$BIN" --data-dir "$DATA" run --workstream edge-command-code --executable "$FAKE" \
|
|
cmdc >"$LOGS/edge-command-code-second.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --session "$command_code_session_id") \
|
|
"$TMP/command-code-second-argv.log"
|
|
command_code_second_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$command_code_ws_id" --limit 100 --json "AMWS-FAKE-COMMAND-CODE")
|
|
jq -e \
|
|
'([.[] | select(.role == "assistant" and (.content | contains("AMWS-FAKE-COMMAND-CODE-ONE")))] | length == 1)
|
|
and ([.[] | select(.role == "assistant" and (.content | contains("AMWS-FAKE-COMMAND-CODE-TWO")))] | length == 1)' \
|
|
<<<"$command_code_second_hits" >/dev/null || {
|
|
printf 'command-code incremental import duplicated or missed a sentinel\n' >&2
|
|
tail -80 "$LOGS/edge-command-code-second.log" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Kiro fake-mode fixtures cover both incompatible native stores. The fake owns
|
|
# each fresh id, writes the sanitized schema observed in live acceptance, and
|
|
# honors the exact engine-specific resume selected by the wrapper.
|
|
KIRO_FAKE_HOME="$CONFIG/kiro-fake"
|
|
mkdir -p "$KIRO_FAKE_HOME"
|
|
(
|
|
cd "$REPO"
|
|
KIRO_HOME="$KIRO_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=kiro \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/kiro-first-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-KIRO-ONE" \
|
|
"$BIN" --data-dir "$DATA" run --new edge-kiro --executable "$FAKE" \
|
|
--yolo kiro >"$LOGS/edge-kiro-first.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --trust-all-tools) "$TMP/kiro-first-argv.log"
|
|
kiro_session_file=$(find "$KIRO_FAKE_HOME/sessions/cli" -maxdepth 1 -name '*.jsonl' -print -quit)
|
|
[ -n "$kiro_session_file" ] || {
|
|
printf 'fake kiro did not create a native session store\n' >&2
|
|
exit 1
|
|
}
|
|
kiro_session_id=$(basename "$kiro_session_file" .jsonl)
|
|
kiro_ws_hex=$(sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT lower(hex(id)) FROM workstreams WHERE name = 'edge-kiro' ORDER BY selected_at DESC LIMIT 1;")
|
|
[ "${#kiro_ws_hex}" -eq 32 ] || {
|
|
printf 'could not resolve the edge-kiro workstream id\n' >&2
|
|
exit 1
|
|
}
|
|
kiro_ws_id="${kiro_ws_hex:0:8}-${kiro_ws_hex:8:4}-${kiro_ws_hex:12:4}-${kiro_ws_hex:16:4}-${kiro_ws_hex:20:12}"
|
|
kiro_first_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$kiro_ws_id" --limit 100 --json "AMWS-FAKE-KIRO-ONE")
|
|
jq -e --arg id "$kiro_session_id" \
|
|
'[.[] | select(.agent == "kiro-cli" and .role == "assistant" and (.content | contains("AMWS-FAKE-KIRO-ONE")) and .native_session_id == $id)] | length == 1' \
|
|
<<<"$kiro_first_hits" >/dev/null || {
|
|
printf 'kiro event-stream sentinel was not imported from the discovered session\n' >&2
|
|
tail -80 "$LOGS/edge-kiro-first.log" >&2
|
|
exit 1
|
|
}
|
|
(
|
|
cd "$REPO"
|
|
KIRO_HOME="$KIRO_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=kiro \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/kiro-second-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-KIRO-TWO" \
|
|
"$BIN" --data-dir "$DATA" run --workstream edge-kiro --executable "$FAKE" \
|
|
kiro-cli >"$LOGS/edge-kiro-second.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --resume-id "$kiro_session_id") "$TMP/kiro-second-argv.log"
|
|
kiro_second_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$kiro_ws_id" --limit 100 --json "AMWS-FAKE-KIRO")
|
|
jq -e \
|
|
'([.[] | select(.role == "assistant" and (.content | contains("AMWS-FAKE-KIRO-ONE")))] | length == 1)
|
|
and ([.[] | select(.role == "assistant" and (.content | contains("AMWS-FAKE-KIRO-TWO")))] | length == 1)' \
|
|
<<<"$kiro_second_hits" >/dev/null || {
|
|
printf 'kiro incremental import duplicated or missed a round sentinel\n' >&2
|
|
tail -80 "$LOGS/edge-kiro-second.log" >&2
|
|
exit 1
|
|
}
|
|
(
|
|
cd "$REPO"
|
|
KIRO_HOME="$KIRO_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=kiro-v3 \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/kiro-v3-first-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-KIRO-V3-ONE" \
|
|
"$BIN" --data-dir "$DATA" run --new edge-kiro-v3 --executable "$FAKE" \
|
|
--yolo kiro --v3 >"$LOGS/edge-kiro-v3-first.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --v3) "$TMP/kiro-v3-first-argv.log"
|
|
kiro_v3_session_dir=$(find "$KIRO_FAKE_HOME/sessions" -mindepth 2 -maxdepth 2 \
|
|
-type d -name 'sess_*' -print -quit)
|
|
[ -n "$kiro_v3_session_dir" ] || {
|
|
printf 'fake kiro v3 did not create a native session store\n' >&2
|
|
exit 1
|
|
}
|
|
kiro_v3_session_id=$(basename "$kiro_v3_session_dir")
|
|
kiro_v3_ws_hex=$(sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT lower(hex(id)) FROM workstreams WHERE name = 'edge-kiro-v3' ORDER BY selected_at DESC LIMIT 1;")
|
|
kiro_v3_ws_id="${kiro_v3_ws_hex:0:8}-${kiro_v3_ws_hex:8:4}-${kiro_v3_ws_hex:12:4}-${kiro_v3_ws_hex:16:4}-${kiro_v3_ws_hex:20:12}"
|
|
kiro_v3_first_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$kiro_v3_ws_id" --limit 100 --json "AMWS-FAKE-KIRO-V3-ONE")
|
|
jq -e --arg id "$kiro_v3_session_id" \
|
|
'[.[] | select(.agent == "kiro-cli" and .role == "assistant" and (.content | contains("AMWS-FAKE-KIRO-V3-ONE")) and .native_session_id == $id)] | length == 1' \
|
|
<<<"$kiro_v3_first_hits" >/dev/null || {
|
|
printf 'kiro v3 sentinel was not imported from the discovered session\n' >&2
|
|
tail -80 "$LOGS/edge-kiro-v3-first.log" >&2
|
|
exit 1
|
|
}
|
|
(
|
|
cd "$REPO"
|
|
KIRO_HOME="$KIRO_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=kiro-v3 \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/kiro-v3-second-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-KIRO-V3-TWO" \
|
|
"$BIN" --data-dir "$DATA" run --workstream edge-kiro-v3 --executable "$FAKE" \
|
|
kiro >"$LOGS/edge-kiro-v3-second.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --v3 --resume-id "$kiro_v3_session_id") \
|
|
"$TMP/kiro-v3-second-argv.log"
|
|
kiro_v3_second_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$kiro_v3_ws_id" --limit 100 --json "AMWS-FAKE-KIRO-V3")
|
|
jq -e \
|
|
'([.[] | select(.role == "assistant" and (.content | contains("AMWS-FAKE-KIRO-V3-ONE")))] | length == 1)
|
|
and ([.[] | select(.role == "assistant" and (.content | contains("AMWS-FAKE-KIRO-V3-TWO")))] | length == 1)' \
|
|
<<<"$kiro_v3_second_hits" >/dev/null || {
|
|
printf 'kiro v3 incremental import duplicated or missed a round sentinel\n' >&2
|
|
tail -80 "$LOGS/edge-kiro-v3-second.log" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Antigravity fake-mode fixture: `agy` owns the conversation id and SQLite
|
|
# database, while its real PreInvocation hook links that id and accepts any
|
|
# pending workstream context. The private trajectory table is never decoded or
|
|
# copied into the ledger.
|
|
ANTIGRAVITY_FAKE_HOME="$CONFIG/antigravity-fake"
|
|
ANTIGRAVITY_FAKE_ID="a0d5ac62-2501-4780-b783-76d159c56cb3"
|
|
ANTIGRAVITY_HOOK="$ROOT/hooks/antigravity-cli/session-start.sh"
|
|
mkdir -p "$ANTIGRAVITY_FAKE_HOME"
|
|
antigravity_first_observations=$(agent_observation_count antigravity-cli)
|
|
(
|
|
cd "$REPO"
|
|
HOME="$ANTIGRAVITY_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=antigravity \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/antigravity-first-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_SESSION_ID="$ANTIGRAVITY_FAKE_ID" \
|
|
AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_HOOK="$ANTIGRAVITY_HOOK" \
|
|
AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_HOOK_LOG="$TMP/antigravity-first-hook.json" \
|
|
"$BIN" --data-dir "$DATA" run --new edge-antigravity --executable "$FAKE" \
|
|
--yolo antigravity >"$LOGS/edge-antigravity-first.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --dangerously-skip-permissions) \
|
|
"$TMP/antigravity-first-argv.log"
|
|
antigravity_ws_hex=$(workstream_hex edge-antigravity)
|
|
[ "${#antigravity_ws_hex}" -eq 32 ] || {
|
|
printf 'could not resolve the edge-antigravity workstream id\n' >&2
|
|
exit 1
|
|
}
|
|
antigravity_native=$(assert_managed_leg "$antigravity_ws_hex" antigravity-cli 0 0 0 \
|
|
"$LOGS/edge-antigravity-first.log" "$antigravity_first_observations")
|
|
[ "$antigravity_native" = "$ANTIGRAVITY_FAKE_ID" ] || {
|
|
printf 'Antigravity linked native conversation %s, expected %s\n' \
|
|
"$antigravity_native" "$ANTIGRAVITY_FAKE_ID" >&2
|
|
exit 1
|
|
}
|
|
antigravity_ws_id="${antigravity_ws_hex:0:8}-${antigravity_ws_hex:8:4}-${antigravity_ws_hex:12:4}-${antigravity_ws_hex:16:4}-${antigravity_ws_hex:20:12}"
|
|
private_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$antigravity_ws_id" --limit 100 --json "PRIVATE")
|
|
jq -e 'length == 0' <<<"$private_hits" >/dev/null || {
|
|
printf 'Antigravity private trajectory payload leaked into the workstream ledger\n' >&2
|
|
exit 1
|
|
}
|
|
|
|
antigravity_second_before=$(latest_workstream_sequence "$antigravity_ws_hex")
|
|
antigravity_second_delivery=$(current_delivery_cursor "$antigravity_ws_hex" antigravity-cli)
|
|
antigravity_second_observations=$(agent_observation_count antigravity-cli)
|
|
(
|
|
cd "$REPO"
|
|
HOME="$ANTIGRAVITY_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=antigravity \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/antigravity-second-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_SESSION_ID="$ANTIGRAVITY_FAKE_ID" \
|
|
AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_HOOK="$ANTIGRAVITY_HOOK" \
|
|
AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_HOOK_LOG="$TMP/antigravity-second-hook.json" \
|
|
"$BIN" --data-dir "$DATA" run --workstream edge-antigravity \
|
|
--executable "$FAKE" agy >"$LOGS/edge-antigravity-second.log" 2>&1
|
|
)
|
|
diff -u <(printf '%s\n' --conversation "$ANTIGRAVITY_FAKE_ID") \
|
|
"$TMP/antigravity-second-argv.log"
|
|
returned_antigravity=$(assert_managed_leg "$antigravity_ws_hex" antigravity-cli \
|
|
"$antigravity_second_before" "$antigravity_second_delivery" 0 \
|
|
"$LOGS/edge-antigravity-second.log" "$antigravity_second_observations")
|
|
[ "$returned_antigravity" = "$ANTIGRAVITY_FAKE_ID" ] || {
|
|
printf 'returning Antigravity launch did not resume %s\n' "$ANTIGRAVITY_FAKE_ID" >&2
|
|
exit 1
|
|
}
|
|
|
|
# A fresh Antigravity conversation joining Kimi's established workstream must
|
|
# receive the prior visible ledger through the real hook's injectSteps output.
|
|
ANTIGRAVITY_CROSS_HOME="$CONFIG/antigravity-cross"
|
|
ANTIGRAVITY_CROSS_ID="9576275f-7c4e-4709-b372-22d1ad2a0af8"
|
|
mkdir -p "$ANTIGRAVITY_CROSS_HOME"
|
|
antigravity_cross_before=$(latest_workstream_sequence "$kimi_ws_hex")
|
|
antigravity_cross_delivery=$(current_delivery_cursor "$kimi_ws_hex" antigravity-cli)
|
|
antigravity_cross_observations=$(agent_observation_count antigravity-cli)
|
|
(
|
|
cd "$REPO"
|
|
HOME="$ANTIGRAVITY_CROSS_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=antigravity \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/antigravity-cross-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_SESSION_ID="$ANTIGRAVITY_CROSS_ID" \
|
|
AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_HOOK="$ANTIGRAVITY_HOOK" \
|
|
AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_HOOK_LOG="$TMP/antigravity-cross-hook.json" \
|
|
"$BIN" --data-dir "$DATA" run --workstream edge-kimi --executable "$FAKE" \
|
|
antigravity-cli >"$LOGS/edge-antigravity-cross.log" 2>&1
|
|
)
|
|
if grep -q . "$TMP/antigravity-cross-argv.log"; then
|
|
printf 'fresh Antigravity launch unexpectedly received a session selector\n' >&2
|
|
cat "$TMP/antigravity-cross-argv.log" >&2
|
|
exit 1
|
|
fi
|
|
jq -e '.injectSteps[0].ephemeralMessage | contains("AMWS-FAKE-KIMI")' \
|
|
"$TMP/antigravity-cross-hook.json" >/dev/null || {
|
|
printf 'Antigravity hook did not receive the prior Kimi workstream history\n' >&2
|
|
cat "$TMP/antigravity-cross-hook.json" >&2
|
|
exit 1
|
|
}
|
|
assert_managed_leg "$kimi_ws_hex" antigravity-cli "$antigravity_cross_before" \
|
|
"$antigravity_cross_delivery" 1 "$LOGS/edge-antigravity-cross.log" \
|
|
"$antigravity_cross_observations" >/dev/null
|
|
|
|
# Grok fake-mode fixture: the fake grok honors the wrapper's `--session-id`
|
|
# (fresh) and `--resume <id>` (returning) selectors, writes the native store
|
|
# layout ($GROK_HOME/sessions/<bucket>/<id>/summary.json plus
|
|
# chat_history.jsonl), and appends the round sentinel. The returning launch
|
|
# must also receive the undelivered workstream packet through `--rules`.
|
|
GROK_FAKE_HOME="$CONFIG/grok-fake"
|
|
mkdir -p "$GROK_FAKE_HOME"
|
|
(
|
|
cd "$REPO"
|
|
GROK_HOME="$GROK_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=grok \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/grok-first-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-GROK-ONE" \
|
|
"$BIN" --data-dir "$DATA" run --new edge-grok --executable "$FAKE" \
|
|
grok >"$LOGS/edge-grok-first.log" 2>&1
|
|
)
|
|
grep -qx -- '--session-id' "$TMP/grok-first-argv.log" || {
|
|
printf 'fresh grok launch did not receive a wrapper session id\n' >&2
|
|
cat "$TMP/grok-first-argv.log" >&2
|
|
exit 1
|
|
}
|
|
if grep -qx -- '--rules' "$TMP/grok-first-argv.log"; then
|
|
printf 'fresh grok launch on an empty workstream unexpectedly received --rules\n' >&2
|
|
exit 1
|
|
fi
|
|
grok_session_id=$(basename "$(find "$GROK_FAKE_HOME/sessions" -mindepth 2 -maxdepth 2 -type d -print -quit)")
|
|
[ -n "$grok_session_id" ] || {
|
|
printf 'fake grok did not create a native session store\n' >&2
|
|
exit 1
|
|
}
|
|
grok_ws_hex=$(sqlite3 "$DATA/db/memory.sqlite" \
|
|
"SELECT lower(hex(id)) FROM workstreams WHERE name = 'edge-grok' ORDER BY selected_at DESC LIMIT 1;")
|
|
[ "${#grok_ws_hex}" -eq 32 ] || {
|
|
printf 'could not resolve the edge-grok workstream id\n' >&2
|
|
exit 1
|
|
}
|
|
grok_ws_id="${grok_ws_hex:0:8}-${grok_ws_hex:8:4}-${grok_ws_hex:12:4}-${grok_ws_hex:16:4}-${grok_ws_hex:20:12}"
|
|
grok_first_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$grok_ws_id" --limit 100 --json "AMWS-FAKE-GROK-ONE")
|
|
jq -e --arg id "$grok_session_id" \
|
|
'[.[] | select(.agent == "grok" and .role == "assistant" and (.content | contains("AMWS-FAKE-GROK-ONE")) and .native_session_id == $id)] | length == 1' \
|
|
<<<"$grok_first_hits" >/dev/null || {
|
|
printf 'grok chat_history.jsonl sentinel was not imported\n' >&2
|
|
tail -80 "$LOGS/edge-grok-first.log" >&2
|
|
exit 1
|
|
}
|
|
# Returning to the same grok session must resume it (`--resume <id>`) and,
|
|
# because that session already produced every workstream event, receive no
|
|
# context packet.
|
|
(
|
|
cd "$REPO"
|
|
GROK_HOME="$GROK_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=grok \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/grok-second-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-GROK-TWO" \
|
|
"$BIN" --data-dir "$DATA" run --workstream edge-grok --executable "$FAKE" \
|
|
grok-build >"$LOGS/edge-grok-second.log" 2>&1
|
|
)
|
|
grep -A1 -x -- '--resume' "$TMP/grok-second-argv.log" | grep -qx "$grok_session_id" || {
|
|
printf 'returning grok launch did not resume the linked session\n' >&2
|
|
cat "$TMP/grok-second-argv.log" >&2
|
|
exit 1
|
|
}
|
|
if grep -qx -- '--rules' "$TMP/grok-second-argv.log"; then
|
|
printf 'same-session grok resume unexpectedly received a context packet\n' >&2
|
|
exit 1
|
|
fi
|
|
grok_second_hits=$("$BIN" --data-dir "$DATA" workstream-search \
|
|
--workstream-id "$grok_ws_id" --limit 100 --json "AMWS-FAKE-GROK")
|
|
jq -e \
|
|
'([.[] | select(.role == "assistant" and (.content | contains("AMWS-FAKE-GROK-ONE")))] | length == 1)
|
|
and ([.[] | select(.role == "assistant" and (.content | contains("AMWS-FAKE-GROK-TWO")))] | length == 1)' \
|
|
<<<"$grok_second_hits" >/dev/null || {
|
|
printf 'grok incremental import duplicated or missed a round sentinel\n' >&2
|
|
tail -80 "$LOGS/edge-grok-second.log" >&2
|
|
exit 1
|
|
}
|
|
|
|
# A fresh grok session joining the established edge-kimi workstream must
|
|
# receive that workstream's history through `--rules`, with historical tool
|
|
# activity labelled as completed evidence.
|
|
grok_cross_before=$(latest_workstream_sequence "$kimi_ws_hex")
|
|
grok_cross_delivery=$(current_delivery_cursor "$kimi_ws_hex" grok)
|
|
(
|
|
cd "$REPO"
|
|
GROK_HOME="$GROK_FAKE_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=grok \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/grok-cross-argv.log" \
|
|
AI_MEMORY_ACCEPTANCE_SENTINEL="AMWS-FAKE-GROK-CROSS" \
|
|
"$BIN" --data-dir "$DATA" run --workstream edge-kimi --executable "$FAKE" \
|
|
grok >"$LOGS/edge-grok-cross.log" 2>&1
|
|
)
|
|
grep -qx -- '--session-id' "$TMP/grok-cross-argv.log" || {
|
|
printf 'grok joining an established workstream did not start a fresh session\n' >&2
|
|
cat "$TMP/grok-cross-argv.log" >&2
|
|
exit 1
|
|
}
|
|
grep -qx -- '--rules' "$TMP/grok-cross-argv.log" || {
|
|
printf 'grok joining an established workstream did not receive --rules context\n' >&2
|
|
cat "$TMP/grok-cross-argv.log" >&2
|
|
exit 1
|
|
}
|
|
grep -q 'AMWS-FAKE-KIMI' "$TMP/grok-cross-argv.log" || {
|
|
printf 'the grok --rules packet did not carry the prior harness history\n' >&2
|
|
exit 1
|
|
}
|
|
assert_managed_leg "$kimi_ws_hex" grok "$grok_cross_before" \
|
|
"$grok_cross_delivery" 1 \
|
|
"$LOGS/edge-grok-cross.log" >/dev/null
|
|
|
|
# A blank first launch remains eligible for one-time native-session adoption.
|
|
# Use a pseudo-terminal because redirected/scripted launches deliberately skip
|
|
# the chooser.
|
|
ADOPTION_CODEX_HOME="$CONFIG/adoption-codex"
|
|
mkdir -p "$ADOPTION_CODEX_HOME/sessions/2026/01/01"
|
|
(
|
|
cd "$REPO"
|
|
CODEX_HOME="$ADOPTION_CODEX_HOME" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=argv \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/adoption-blank-argv.log" \
|
|
"$BIN" --data-dir "$DATA" run --new edge-adopt --executable "$FAKE" \
|
|
codex >"$LOGS/edge-adoption-blank.log" 2>&1
|
|
)
|
|
printf '{"type":"session_meta","payload":{"id":"adoption-codex-id","cwd":"%s"}}\n' \
|
|
"$REPO" >"$ADOPTION_CODEX_HOME/sessions/2026/01/01/rollout-adoption.jsonl"
|
|
|
|
ADOPTION_RUNNER="$TMP/adoption-runner.sh"
|
|
cat >"$ADOPTION_RUNNER" <<'EOF'
|
|
#!/usr/bin/env bash
|
|
set -euo pipefail
|
|
cd "$AI_MEMORY_ACCEPTANCE_REPO"
|
|
exec "$AI_MEMORY_ACCEPTANCE_BIN" --data-dir "$AI_MEMORY_ACCEPTANCE_DATA" \
|
|
run --workstream edge-adopt --executable "$AI_MEMORY_ACCEPTANCE_FAKE" "$@"
|
|
EOF
|
|
chmod +x "$ADOPTION_RUNNER"
|
|
|
|
printf '\n' | env \
|
|
AI_MEMORY_ACCEPTANCE_REPO="$REPO" \
|
|
AI_MEMORY_ACCEPTANCE_BIN="$BIN" \
|
|
AI_MEMORY_ACCEPTANCE_DATA="$DATA" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE="$FAKE" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=argv \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/adoption-codex-argv.log" \
|
|
CODEX_HOME="$ADOPTION_CODEX_HOME" \
|
|
script -qefc "$ADOPTION_RUNNER codex" /dev/null \
|
|
>"$LOGS/edge-adoption-codex.log" 2>&1
|
|
diff -u <(printf '%s\n' resume adoption-codex-id) "$TMP/adoption-codex-argv.log"
|
|
|
|
# Once Codex establishes the workstream, Claude must start clean even when an
|
|
# obsolete checkout-local Claude session exists.
|
|
ADOPTION_CLAUDE_HOME="$CONFIG/adoption-claude"
|
|
mkdir -p "$ADOPTION_CLAUDE_HOME/projects/fixture"
|
|
printf '{"sessionId":"obsolete-claude-id","cwd":"%s"}\n' "$REPO" \
|
|
>"$ADOPTION_CLAUDE_HOME/projects/fixture/obsolete-claude-id.jsonl"
|
|
printf '\n' | env \
|
|
AI_MEMORY_ACCEPTANCE_REPO="$REPO" \
|
|
AI_MEMORY_ACCEPTANCE_BIN="$BIN" \
|
|
AI_MEMORY_ACCEPTANCE_DATA="$DATA" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE="$FAKE" \
|
|
AI_MEMORY_ACCEPTANCE_FAKE_MODE=argv \
|
|
AI_MEMORY_ACCEPTANCE_ARGV_LOG="$TMP/adoption-claude-argv.log" \
|
|
CLAUDE_CONFIG_DIR="$ADOPTION_CLAUDE_HOME" \
|
|
script -qefc "$ADOPTION_RUNNER claude" /dev/null \
|
|
>"$LOGS/edge-adoption-claude.log" 2>&1
|
|
mapfile -t adoption_claude_argv <"$TMP/adoption-claude-argv.log"
|
|
[ "${adoption_claude_argv[0]:-}" = --session-id ] || {
|
|
printf 'established workstream did not create a fresh Claude session\n' >&2
|
|
exit 1
|
|
}
|
|
[ "${adoption_claude_argv[1]:-}" != obsolete-claude-id ] || {
|
|
printf 'established workstream adopted an obsolete Claude session\n' >&2
|
|
exit 1
|
|
}
|
|
|
|
# `ai-memory workstreams` must answer from the same checkout identity `run`
|
|
# selects with, without turning that identity into readable output. The list is
|
|
# taken from the repository the previous legs launched in, so `edge-adopt` --
|
|
# the workstream the Claude adoption leg selected last -- has to lead it.
|
|
workstreams_json=$(cd "$REPO" && "$BIN" --data-dir "$DATA" workstreams --json)
|
|
jq -e '(type == "array") and (length > 1)' <<<"$workstreams_json" >/dev/null || {
|
|
printf 'workstreams did not list this checkout\n' >&2
|
|
printf '%s\n' "$workstreams_json" >&2
|
|
exit 1
|
|
}
|
|
jq -e '[.[] | select(.current)] | length == 1' <<<"$workstreams_json" >/dev/null || {
|
|
printf 'workstreams did not mark exactly one current selection\n' >&2
|
|
printf '%s\n' "$workstreams_json" >&2
|
|
exit 1
|
|
}
|
|
jq -e '.[0].current and .[0].name == "edge-adopt"' <<<"$workstreams_json" >/dev/null || {
|
|
printf 'workstreams did not lead with the checkout current workstream\n' >&2
|
|
printf '%s\n' "$workstreams_json" >&2
|
|
exit 1
|
|
}
|
|
jq -e --arg name edge-kimi \
|
|
'[.[] | select(.name == $name and (.linked_harnesses | index("kimi-code")))] | length == 1' \
|
|
<<<"$workstreams_json" >/dev/null || {
|
|
printf 'workstreams did not report the harnesses linked to edge-kimi\n' >&2
|
|
printf '%s\n' "$workstreams_json" >&2
|
|
exit 1
|
|
}
|
|
# Discovery is a read of workstream metadata only: the checkout path, its
|
|
# fingerprints, and native session ids must never travel back to the client.
|
|
for leaked in "$REPO" "$kimi_session_id" adoption-codex-id; do
|
|
! grep -qF -- "$leaked" <<<"$workstreams_json" || {
|
|
printf 'workstreams leaked private checkout or native session data: %s\n' \
|
|
"$leaked" >&2
|
|
exit 1
|
|
}
|
|
done
|
|
limited_json=$(cd "$REPO" && "$BIN" --data-dir "$DATA" workstreams --limit 1 --json)
|
|
jq -e 'length == 1 and .[0].current' <<<"$limited_json" >/dev/null || {
|
|
printf 'workstreams --limit did not keep the current selection\n' >&2
|
|
printf '%s\n' "$limited_json" >&2
|
|
exit 1
|
|
}
|
|
workstreams_human=$(cd "$REPO" && "$BIN" --data-dir "$DATA" workstreams)
|
|
grep -q '^\* edge-adopt' <<<"$workstreams_human" || {
|
|
printf 'human workstreams output did not mark the current selection\n' >&2
|
|
printf '%s\n' "$workstreams_human" >&2
|
|
exit 1
|
|
}
|
|
|
|
# A different worktree of the same project shares the workspace and project
|
|
# names but must not inherit the first checkout list.
|
|
OTHER_REPO="$TMP/repo-elsewhere"
|
|
mkdir -p "$OTHER_REPO"
|
|
git -C "$OTHER_REPO" init -q
|
|
git -C "$OTHER_REPO" config user.name "ai-memory acceptance"
|
|
git -C "$OTHER_REPO" config user.email "acceptance@localhost"
|
|
printf '# elsewhere\n' >"$OTHER_REPO/README.md"
|
|
git -C "$OTHER_REPO" add README.md
|
|
git -C "$OTHER_REPO" commit -q --no-gpg-sign -m "acceptance fixture"
|
|
other_json=$(cd "$OTHER_REPO" && "$BIN" --data-dir "$DATA" workstreams \
|
|
--project "$(basename "$REPO")" --json)
|
|
jq -e 'length == 0' <<<"$other_json" >/dev/null || {
|
|
printf 'workstreams returned another checkout workstreams\n' >&2
|
|
printf '%s\n' "$other_json" >&2
|
|
exit 1
|
|
}
|
|
|
|
# `ai-memory rename-workstream` must correct a name without disturbing anything
|
|
# keyed on the workstream. The id is stable, so the ledger, managed runs, and
|
|
# linked native sessions follow the rename, and neither the listing order nor
|
|
# the workstream a bare `ai-memory run` resumes may move as a side effect of
|
|
# relabelling.
|
|
before_rename_json=$workstreams_json
|
|
adopt_id=$(jq -r '.[] | select(.name == "edge-adopt") | .workstream_id' \
|
|
<<<"$workstreams_json")
|
|
rename_json=$(cd "$REPO" && "$BIN" --data-dir "$DATA" rename-workstream \
|
|
--from edge-adopt --to edge-adopt-fixed --json)
|
|
jq -e --arg id "$adopt_id" \
|
|
'.workstream_id == $id and .from == "edge-adopt" and .to == "edge-adopt-fixed"' \
|
|
<<<"$rename_json" >/dev/null || {
|
|
printf 'rename-workstream did not report the retitled workstream\n' >&2
|
|
printf '%s\n' "$rename_json" >&2
|
|
exit 1
|
|
}
|
|
renamed_json=$(cd "$REPO" && "$BIN" --data-dir "$DATA" workstreams --json)
|
|
jq -e --arg id "$adopt_id" \
|
|
'.[0].current and .[0].name == "edge-adopt-fixed" and .[0].workstream_id == $id' \
|
|
<<<"$renamed_json" >/dev/null || {
|
|
printf 'rename moved the listing order or the current selection\n' >&2
|
|
printf '%s\n' "$renamed_json" >&2
|
|
exit 1
|
|
}
|
|
jq -e '[.[] | select(.name == "edge-adopt")] | length == 0' \
|
|
<<<"$renamed_json" >/dev/null || {
|
|
printf 'the name the rename replaced survived in the listing\n' >&2
|
|
printf '%s\n' "$renamed_json" >&2
|
|
exit 1
|
|
}
|
|
|
|
# A name another workstream in this checkout already holds is refused, as is a
|
|
# name `run --new` would reject. Both must fail before writing anything.
|
|
if (cd "$REPO" && "$BIN" --data-dir "$DATA" rename-workstream \
|
|
--from edge-kimi --to edge-adopt-fixed) >/dev/null 2>&1; then
|
|
printf 'rename-workstream took a name another workstream already holds\n' >&2
|
|
exit 1
|
|
fi
|
|
if (cd "$REPO" && "$BIN" --data-dir "$DATA" rename-workstream \
|
|
--from edge-kimi --to 'nested/name') >/dev/null 2>&1; then
|
|
printf 'rename-workstream took a name run --new would reject\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
# The stable id is unique across every checkout, so the scope predicate has to
|
|
# repeat on the id selector: an id belonging to another worktree must read as
|
|
# absent rather than as a renamable target.
|
|
if (cd "$OTHER_REPO" && "$BIN" --data-dir "$DATA" rename-workstream \
|
|
--project "$(basename "$REPO")" --workstream-id "$adopt_id" \
|
|
--to hijacked) >/dev/null 2>&1; then
|
|
printf 'rename-workstream reached a workstream outside the calling checkout\n' >&2
|
|
exit 1
|
|
fi
|
|
|
|
# Renaming to the name it already carries writes nothing and is not an error.
|
|
noop_json=$(cd "$REPO" && "$BIN" --data-dir "$DATA" rename-workstream \
|
|
--from edge-adopt-fixed --to edge-adopt-fixed --json)
|
|
jq -e '.from == .to and .to == "edge-adopt-fixed"' <<<"$noop_json" >/dev/null || {
|
|
printf 'renaming to the current name was not reported as a no-op\n' >&2
|
|
printf '%s\n' "$noop_json" >&2
|
|
exit 1
|
|
}
|
|
|
|
# Renaming back by id has to restore the listing exactly. Anything the refused
|
|
# attempts, the no-op, or the rename itself touched -- a bumped `updated_at`, a
|
|
# moved selection, a reordered row -- surfaces as a diff here.
|
|
rename_back=$(cd "$REPO" && "$BIN" --data-dir "$DATA" rename-workstream \
|
|
--workstream-id "$adopt_id" --to edge-adopt)
|
|
grep -q "Renamed workstream 'edge-adopt-fixed' to 'edge-adopt'" \
|
|
<<<"$rename_back" || {
|
|
printf 'human rename output did not report both names\n' >&2
|
|
printf '%s\n' "$rename_back" >&2
|
|
exit 1
|
|
}
|
|
after_rename_json=$(cd "$REPO" && "$BIN" --data-dir "$DATA" workstreams --json)
|
|
[ "$after_rename_json" = "$before_rename_json" ] || {
|
|
printf 'a rename round trip left the listing changed\n' >&2
|
|
diff <(printf '%s\n' "$before_rename_json") \
|
|
<(printf '%s\n' "$after_rename_json") >&2 || true
|
|
exit 1
|
|
}
|
|
|
|
if [ "$DETERMINISTIC_ONLY" = 1 ]; then
|
|
printf 'deterministic managed-workstream acceptance passed\n'
|
|
exit 0
|
|
fi
|
|
|
|
read -r -a requested_harnesses <<<"$HARNESS_WORDS"
|
|
harnesses=()
|
|
for requested_harness in "${requested_harnesses[@]}"; do
|
|
case "$requested_harness" in
|
|
kimi-code | kimi-cli) harness=kimi ;;
|
|
commandcode | cmdc | cmd) harness=command-code ;;
|
|
kiro | kiro-cli)
|
|
printf 'skipping kiro in the scripted real-harness phase: noninteractive Kiro uses a different session store; run the documented interactive Kiro acceptance separately\n' >&2
|
|
continue
|
|
;;
|
|
grok-build) harness=grok ;;
|
|
antigravity-cli | agy) harness=antigravity ;;
|
|
*) harness=$requested_harness ;;
|
|
esac
|
|
harness_command=$harness
|
|
[ "$harness" != antigravity ] || harness_command=agy
|
|
if command -v "$harness_command" >/dev/null 2>&1; then
|
|
harnesses+=("$harness")
|
|
else
|
|
printf 'skipping unavailable harness: %s\n' "$requested_harness" >&2
|
|
fi
|
|
done
|
|
[ "${#harnesses[@]}" -ge 2 ] || {
|
|
printf 'real acceptance needs at least two installed harnesses\n' >&2
|
|
exit 1
|
|
}
|
|
|
|
CLAUDE_CONFIG_HOME="$CONFIG/claude"
|
|
CLAUDE_SETTINGS="$CLAUDE_CONFIG_HOME/settings.json"
|
|
CODEX_ACCEPTANCE_HOME="$CONFIG/codex-home"
|
|
CODEX_HOOKS="$CODEX_ACCEPTANCE_HOME/.codex/hooks.json"
|
|
OPENCODE_CONFIG_HOME="$CONFIG/opencode-xdg"
|
|
OPENCODE_PLUGIN="$OPENCODE_CONFIG_HOME/opencode/plugins/ai-memory.ts"
|
|
OPENCODE2_PLUGIN="$OPENCODE_CONFIG_HOME/opencode/plugins/ai-memory-opencode2.ts"
|
|
OPENCODE_DATA_HOME="$CONFIG/opencode-xdg-data"
|
|
PI_EXTENSION="$CONFIG/pi/ai-memory.ts"
|
|
OMP_EXTENSION="$CONFIG/omp/ai-memory.ts"
|
|
OMP_AGENT_DIR="$CONFIG/omp/agent"
|
|
CRUSH_DATA_DIR="$CONFIG/crush/data"
|
|
KIMI_ACCEPTANCE_HOME="$CONFIG/kimi-home"
|
|
COMMAND_CODE_ACCEPTANCE_HOME="$CONFIG/command-code-home"
|
|
COMMAND_CODE_SETTINGS="$COMMAND_CODE_ACCEPTANCE_HOME/.commandcode/settings.json"
|
|
GROK_ACCEPTANCE_HOME="$CONFIG/grok-home"
|
|
ANTIGRAVITY_ACCEPTANCE_HOME="$CONFIG/antigravity-home"
|
|
ANTIGRAVITY_HOOKS="$ANTIGRAVITY_ACCEPTANCE_HOME/.gemini/config/hooks.json"
|
|
mkdir -p "$(dirname "$CLAUDE_SETTINGS")" "$(dirname "$CODEX_HOOKS")" \
|
|
"$(dirname "$OPENCODE_PLUGIN")" "$(dirname "$PI_EXTENSION")" \
|
|
"$(dirname "$OMP_EXTENSION")" "$OMP_AGENT_DIR" "$OPENCODE_DATA_HOME/opencode" \
|
|
"$CRUSH_DATA_DIR" "$KIMI_ACCEPTANCE_HOME" "$GROK_ACCEPTANCE_HOME" \
|
|
"$(dirname "$COMMAND_CODE_SETTINGS")" \
|
|
"$(dirname "$ANTIGRAVITY_HOOKS")" \
|
|
"$ANTIGRAVITY_ACCEPTANCE_HOME/.gemini/antigravity-cli"
|
|
|
|
# Redirect native transcript stores into the fixture while reusing only the
|
|
# minimum authentication material required for real model calls.
|
|
if [ -f "$HOME/.claude/.credentials.json" ]; then
|
|
cp "$HOME/.claude/.credentials.json" "$CLAUDE_CONFIG_HOME/.credentials.json"
|
|
fi
|
|
if [ -f "$HOME/.local/share/opencode/auth.json" ]; then
|
|
cp "$HOME/.local/share/opencode/auth.json" "$OPENCODE_DATA_HOME/opencode/auth.json"
|
|
fi
|
|
|
|
# Codex only discovers hooks below its home. Use a temporary home so the
|
|
# acceptance config cannot modify or depend on the operator's trusted hooks.
|
|
if [ -f "$HOME/.codex/auth.json" ]; then
|
|
cp "$HOME/.codex/auth.json" "$CODEX_ACCEPTANCE_HOME/.codex/auth.json"
|
|
fi
|
|
|
|
# OMP's installed release drops explicit extension paths when
|
|
# --no-extensions is set. Isolate discovery with a temporary agent directory
|
|
# and copy only settings plus consistent credential/model database backups.
|
|
for database in agent.db models.db; do
|
|
if [ -f "$HOME/.omp/agent/$database" ]; then
|
|
sqlite3 "$HOME/.omp/agent/$database" ".backup '$OMP_AGENT_DIR/$database'"
|
|
fi
|
|
done
|
|
for config_name in auth.json config.yml models-store.json settings.json; do
|
|
if [ -f "$HOME/.omp/agent/$config_name" ]; then
|
|
cp "$HOME/.omp/agent/$config_name" "$OMP_AGENT_DIR/$config_name"
|
|
fi
|
|
done
|
|
|
|
# Preserve OpenCode's provider/model preferences while loading only the
|
|
# acceptance plugin from the isolated XDG config root.
|
|
for config_name in opencode.json opencode.jsonc tui.json; do
|
|
if [ -f "$HOME/.config/opencode/$config_name" ]; then
|
|
cp "$HOME/.config/opencode/$config_name" \
|
|
"$OPENCODE_CONFIG_HOME/opencode/$config_name"
|
|
fi
|
|
done
|
|
|
|
# opencode2's background service defaults to port 49374 — ai-memory's own
|
|
# default — so an isolated service left on defaults crash-loops against any
|
|
# live ai-memory server on this machine. Pin the fixture service at a
|
|
# neighbouring free port before any opencode2 leg runs.
|
|
if command -v opencode2 >/dev/null 2>&1; then
|
|
OPENCODE2_PORT=$((PORT + 1))
|
|
for _ in $(seq 1 50); do
|
|
if ! curl -sS --max-time 0.1 "http://127.0.0.1:$OPENCODE2_PORT/" >/dev/null 2>&1; then
|
|
break
|
|
fi
|
|
OPENCODE2_PORT=$((OPENCODE2_PORT + 1))
|
|
done
|
|
XDG_CONFIG_HOME="$OPENCODE_CONFIG_HOME" XDG_DATA_HOME="$OPENCODE_DATA_HOME" \
|
|
opencode2 service set port "$OPENCODE2_PORT" >/dev/null
|
|
fi
|
|
|
|
# Kimi Code keeps providers/model and hooks in one config.toml under
|
|
# $KIMI_CODE_HOME. Seed the isolated home with the operator's provider
|
|
# settings and minimum login state; install-hooks merges its [[hooks]]
|
|
# entries without rewriting the rest of the file. Native sessions, indexes,
|
|
# logs, and telemetry remain isolated.
|
|
if [ -f "$HOME/.kimi-code/config.toml" ]; then
|
|
cp "$HOME/.kimi-code/config.toml" "$KIMI_ACCEPTANCE_HOME/config.toml"
|
|
fi
|
|
for relative in credentials/kimi-code.json oauth/kimi-code device_id; do
|
|
if [ -f "$HOME/.kimi-code/$relative" ]; then
|
|
mkdir -p "$KIMI_ACCEPTANCE_HOME/$(dirname "$relative")"
|
|
cp "$HOME/.kimi-code/$relative" "$KIMI_ACCEPTANCE_HOME/$relative"
|
|
fi
|
|
done
|
|
|
|
# Command Code resolves its documented user config, credentials, hooks, and
|
|
# session catalog through HOME/USERPROFILE. Copy only login/model preferences;
|
|
# the acceptance HOME receives a fresh session store and hook file.
|
|
for relative in auth.json config.json; do
|
|
if [ -f "$HOME/.commandcode/$relative" ]; then
|
|
cp "$HOME/.commandcode/$relative" \
|
|
"$COMMAND_CODE_ACCEPTANCE_HOME/.commandcode/$relative"
|
|
fi
|
|
done
|
|
|
|
# Grok resolves everything below $GROK_HOME. Seed the isolated home with the
|
|
# operator's login state and settings only; native sessions, logs, and caches
|
|
# remain isolated.
|
|
for relative in auth.json config.toml; do
|
|
if [ -f "$HOME/.grok/$relative" ]; then
|
|
cp "$HOME/.grok/$relative" "$GROK_ACCEPTANCE_HOME/$relative"
|
|
fi
|
|
done
|
|
|
|
# Antigravity resolves login, settings, hooks, and conversation databases below
|
|
# ~/.gemini. Copy only the minimum OAuth and settings files into an isolated
|
|
# HOME; private history, conversation databases, logs, and caches stay out.
|
|
for relative in google_accounts.json oauth_creds.json installation_id; do
|
|
if [ -f "$HOME/.gemini/$relative" ]; then
|
|
cp "$HOME/.gemini/$relative" \
|
|
"$ANTIGRAVITY_ACCEPTANCE_HOME/.gemini/$relative"
|
|
fi
|
|
done
|
|
for relative in antigravity-oauth-token installation_id jetski_state.pbtxt settings.json; do
|
|
if [ -f "$HOME/.gemini/antigravity-cli/$relative" ]; then
|
|
cp "$HOME/.gemini/antigravity-cli/$relative" \
|
|
"$ANTIGRAVITY_ACCEPTANCE_HOME/.gemini/antigravity-cli/$relative"
|
|
fi
|
|
done
|
|
if [ -f "$HOME/.gemini/config/config.json" ]; then
|
|
cp "$HOME/.gemini/config/config.json" \
|
|
"$ANTIGRAVITY_ACCEPTANCE_HOME/.gemini/config/config.json"
|
|
fi
|
|
|
|
install_hook() {
|
|
local agent=$1
|
|
local target=$2
|
|
local -a command=(
|
|
"$BIN" --data-dir "$DATA" install-hooks --apply
|
|
--agent "$agent" --server-url "$URL" --auth-token "$TOKEN"
|
|
--config-file "$target"
|
|
)
|
|
case "$agent" in
|
|
claude-code | codex | kimi-code | antigravity-cli)
|
|
command+=(--hooks-dir "$ROOT/hooks")
|
|
;;
|
|
esac
|
|
XDG_DATA_HOME="$TMP/xdg-data" "${command[@]}" \
|
|
>"$LOGS/install-$agent.log" 2>&1
|
|
}
|
|
|
|
install_hook claude-code "$CLAUDE_SETTINGS"
|
|
install_hook codex "$CODEX_HOOKS"
|
|
install_hook opencode "$OPENCODE_PLUGIN"
|
|
install_hook opencode2 "$OPENCODE2_PLUGIN"
|
|
install_hook pi "$PI_EXTENSION"
|
|
install_hook omp "$OMP_EXTENSION"
|
|
install_hook kimi-code "$KIMI_ACCEPTANCE_HOME/config.toml"
|
|
install_hook command-code "$COMMAND_CODE_SETTINGS"
|
|
install_hook antigravity-cli "$ANTIGRAVITY_HOOKS"
|
|
|
|
agent_wire_name() {
|
|
case "$1" in
|
|
claude) printf 'claude-code\n' ;;
|
|
opencode) printf 'open-code\n' ;;
|
|
opencode2) printf 'open-code\n' ;;
|
|
kimi) printf 'kimi-code\n' ;;
|
|
command-code) printf 'command-code\n' ;;
|
|
antigravity) printf 'antigravity-cli\n' ;;
|
|
*) printf '%s\n' "$1" ;;
|
|
esac
|
|
}
|
|
|
|
uppercase() {
|
|
printf '%s' "$1" | tr '[:lower:]' '[:upper:]'
|
|
}
|
|
|
|
run_harness() {
|
|
local harness=$1
|
|
local current=$2
|
|
local first_run=$3
|
|
local expect_context=$4
|
|
local log="$LOGS/real-$harness-$current.log"
|
|
local prompt
|
|
local expected_agent
|
|
local before_sequence=0
|
|
local before_delivery=0
|
|
local before_observations=0
|
|
local existing_hex
|
|
local -a wrapper_args native_args
|
|
expected_agent=$(agent_wire_name "$harness")
|
|
before_observations=$(agent_observation_count "$expected_agent")
|
|
existing_hex=$(workstream_hex "$WORKSTREAM_NAME")
|
|
if [ -n "$existing_hex" ]; then
|
|
before_sequence=$(latest_workstream_sequence "$existing_hex")
|
|
before_delivery=$(current_delivery_cursor "$existing_hex" "$expected_agent")
|
|
fi
|
|
prompt="Reply with exactly: $current"
|
|
if [ "$first_run" = 1 ]; then
|
|
wrapper_args=(--new "$WORKSTREAM_NAME")
|
|
else
|
|
wrapper_args=(--workstream "$WORKSTREAM_NAME")
|
|
fi
|
|
case "$harness" in
|
|
claude)
|
|
native_args=(-p --settings "$CLAUDE_SETTINGS" --model "${AI_MEMORY_ACCEPTANCE_CLAUDE_MODEL:-haiku}" --permission-mode plan "$prompt")
|
|
;;
|
|
codex)
|
|
native_args=(exec -c 'sandbox_mode="read-only"' --dangerously-bypass-hook-trust --json "$prompt")
|
|
if [ -n "${AI_MEMORY_ACCEPTANCE_CODEX_MODEL:-}" ]; then
|
|
native_args=(exec -c 'sandbox_mode="read-only"' --dangerously-bypass-hook-trust --json --model "$AI_MEMORY_ACCEPTANCE_CODEX_MODEL" "$prompt")
|
|
fi
|
|
;;
|
|
opencode)
|
|
native_args=(run --format json --auto "$prompt")
|
|
[ -z "${AI_MEMORY_ACCEPTANCE_OPENCODE_MODEL:-}" ] || native_args=(run --format json --auto --model "$AI_MEMORY_ACCEPTANCE_OPENCODE_MODEL" "$prompt")
|
|
;;
|
|
opencode2)
|
|
native_args=(run --format json --auto "$prompt")
|
|
[ -z "${AI_MEMORY_ACCEPTANCE_OPENCODE2_MODEL:-${AI_MEMORY_ACCEPTANCE_OPENCODE_MODEL:-}}" ] || native_args=(run --format json --auto --model "${AI_MEMORY_ACCEPTANCE_OPENCODE2_MODEL:-$AI_MEMORY_ACCEPTANCE_OPENCODE_MODEL}" "$prompt")
|
|
;;
|
|
pi)
|
|
native_args=(-p --no-tools --no-extensions --extension "$PI_EXTENSION" --session-dir "$CONFIG/pi/sessions" "$prompt")
|
|
[ -z "${AI_MEMORY_ACCEPTANCE_PI_MODEL:-}" ] || native_args=(-p --no-tools --no-extensions --extension "$PI_EXTENSION" --session-dir "$CONFIG/pi/sessions" --model "$AI_MEMORY_ACCEPTANCE_PI_MODEL" "$prompt")
|
|
;;
|
|
crush)
|
|
native_args=(run --quiet --data-dir "$CRUSH_DATA_DIR" "$prompt")
|
|
[ -z "${AI_MEMORY_ACCEPTANCE_CRUSH_MODEL:-}" ] || native_args=(run --quiet --data-dir "$CRUSH_DATA_DIR" --model "$AI_MEMORY_ACCEPTANCE_CRUSH_MODEL" "$prompt")
|
|
;;
|
|
omp)
|
|
native_args=(-p --no-tools --extension "$OMP_EXTENSION" --session-dir "$CONFIG/omp/sessions" "$prompt")
|
|
[ -z "${AI_MEMORY_ACCEPTANCE_OMP_MODEL:-}" ] || native_args=(-p --no-tools --extension "$OMP_EXTENSION" --session-dir "$CONFIG/omp/sessions" --model "$AI_MEMORY_ACCEPTANCE_OMP_MODEL" "$prompt")
|
|
;;
|
|
kimi)
|
|
native_args=(-p "$prompt")
|
|
[ -z "${AI_MEMORY_ACCEPTANCE_KIMI_MODEL:-}" ] || native_args=(-p -m "$AI_MEMORY_ACCEPTANCE_KIMI_MODEL" "$prompt")
|
|
;;
|
|
command-code)
|
|
native_args=(-p --no-auto-update "$prompt")
|
|
[ -z "${AI_MEMORY_ACCEPTANCE_COMMAND_CODE_MODEL:-}" ] || native_args=(-p --no-auto-update -m "$AI_MEMORY_ACCEPTANCE_COMMAND_CODE_MODEL" "$prompt")
|
|
;;
|
|
grok)
|
|
native_args=(-p "$prompt")
|
|
[ -z "${AI_MEMORY_ACCEPTANCE_GROK_MODEL:-}" ] || native_args=(-p -m "$AI_MEMORY_ACCEPTANCE_GROK_MODEL" "$prompt")
|
|
;;
|
|
antigravity)
|
|
native_args=(-p --print-timeout "${AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_TIMEOUT:-5m}" "$prompt")
|
|
[ -z "${AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_MODEL:-}" ] || native_args=(-p --print-timeout "${AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_TIMEOUT:-5m}" --model "$AI_MEMORY_ACCEPTANCE_ANTIGRAVITY_MODEL" "$prompt")
|
|
;;
|
|
*)
|
|
printf 'unsupported acceptance harness: %s\n' "$harness" >&2
|
|
return 1
|
|
;;
|
|
esac
|
|
|
|
printf 'running real harness: %s\n' "$harness" >&2
|
|
if [ "$harness" = claude ]; then
|
|
(cd "$REPO" && CLAUDE_CONFIG_DIR="$CLAUDE_CONFIG_HOME" \
|
|
"$BIN" --data-dir "$DATA" run "${wrapper_args[@]}" "$harness" "${native_args[@]}") \
|
|
>"$log" 2>&1
|
|
elif [ "$harness" = codex ]; then
|
|
(cd "$REPO" && HOME="$CODEX_ACCEPTANCE_HOME" \
|
|
CODEX_HOME="$CODEX_ACCEPTANCE_HOME/.codex" \
|
|
"$BIN" --data-dir "$DATA" run "${wrapper_args[@]}" "$harness" "${native_args[@]}") \
|
|
>"$log" 2>&1
|
|
elif [ "$harness" = opencode ] || [ "$harness" = opencode2 ]; then
|
|
(cd "$REPO" && XDG_CONFIG_HOME="$OPENCODE_CONFIG_HOME" \
|
|
XDG_DATA_HOME="$OPENCODE_DATA_HOME" \
|
|
"$BIN" --data-dir "$DATA" run "${wrapper_args[@]}" "$harness" "${native_args[@]}") \
|
|
>"$log" 2>&1
|
|
elif [ "$harness" = omp ]; then
|
|
(cd "$REPO" && PI_CODING_AGENT_DIR="$OMP_AGENT_DIR" \
|
|
"$BIN" --data-dir "$DATA" run "${wrapper_args[@]}" "$harness" "${native_args[@]}") \
|
|
>"$log" 2>&1
|
|
elif [ "$harness" = kimi ]; then
|
|
(cd "$REPO" && KIMI_CODE_HOME="$KIMI_ACCEPTANCE_HOME" \
|
|
"$BIN" --data-dir "$DATA" run "${wrapper_args[@]}" "$harness" "${native_args[@]}") \
|
|
>"$log" 2>&1
|
|
elif [ "$harness" = command-code ]; then
|
|
(cd "$REPO" && HOME="$COMMAND_CODE_ACCEPTANCE_HOME" \
|
|
"$BIN" --data-dir "$DATA" run "${wrapper_args[@]}" "$harness" "${native_args[@]}") \
|
|
>"$log" 2>&1
|
|
elif [ "$harness" = grok ]; then
|
|
(cd "$REPO" && GROK_HOME="$GROK_ACCEPTANCE_HOME" \
|
|
"$BIN" --data-dir "$DATA" run "${wrapper_args[@]}" "$harness" "${native_args[@]}") \
|
|
>"$log" 2>&1
|
|
elif [ "$harness" = antigravity ]; then
|
|
(cd "$REPO" && HOME="$ANTIGRAVITY_ACCEPTANCE_HOME" \
|
|
"$BIN" --data-dir "$DATA" run "${wrapper_args[@]}" "$harness" "${native_args[@]}") \
|
|
>"$log" 2>&1
|
|
else
|
|
(cd "$REPO" && "$BIN" --data-dir "$DATA" run \
|
|
"${wrapper_args[@]}" "$harness" "${native_args[@]}") >"$log" 2>&1
|
|
fi
|
|
|
|
local hex native_id
|
|
hex=$(workstream_hex "$WORKSTREAM_NAME")
|
|
[ "${#hex}" -eq 32 ] || {
|
|
printf 'could not resolve workstream %s after the %s leg\n' \
|
|
"$WORKSTREAM_NAME" "$harness" >&2
|
|
tail -120 "$log" >&2
|
|
return 1
|
|
}
|
|
if ! native_id=$(assert_managed_leg "$hex" "$expected_agent" \
|
|
"$before_sequence" "$before_delivery" "$expect_context" "$log" \
|
|
"$before_observations"); then
|
|
return 1
|
|
fi
|
|
printf '%s\n' "$native_id"
|
|
}
|
|
|
|
WORKSTREAM_NAME="native-acceptance-$(date +%s)-$$"
|
|
RUN_TAG="$(date +%s)-$$"
|
|
first_harness=${harnesses[0]}
|
|
first_agent=$(agent_wire_name "$first_harness")
|
|
first_native=""
|
|
previous_agent=""
|
|
index=0
|
|
for harness in "${harnesses[@]}"; do
|
|
current="AMWS-$RUN_TAG-$(uppercase "$harness")"
|
|
current_agent=$(agent_wire_name "$harness")
|
|
first_run=0
|
|
expect_context=0
|
|
[ "$index" -ne 0 ] || first_run=1
|
|
if [ -n "$previous_agent" ] && [ "$current_agent" != "$previous_agent" ]; then
|
|
expect_context=1
|
|
fi
|
|
if ! native_id=$(run_harness "$harness" "$current" "$first_run" \
|
|
"$expect_context"); then
|
|
exit 1
|
|
fi
|
|
if [ "$index" -eq 0 ]; then
|
|
first_native=$native_id
|
|
fi
|
|
previous_agent=$current_agent
|
|
index=$((index + 1))
|
|
done
|
|
|
|
return_sentinel="AMWS-$RUN_TAG-$(uppercase "$first_harness")-RETURN"
|
|
return_expects_context=0
|
|
[ "$first_agent" = "$previous_agent" ] || return_expects_context=1
|
|
if ! returned_native=$(run_harness "$first_harness" "$return_sentinel" 0 \
|
|
"$return_expects_context"); then
|
|
exit 1
|
|
fi
|
|
[ "$returned_native" = "$first_native" ] || {
|
|
printf '%s resumed native session %s, expected %s\n' \
|
|
"$first_harness" "$returned_native" "$first_native" >&2
|
|
exit 1
|
|
}
|
|
|
|
printf 'real managed-workstream acceptance passed: %s\n' "${harnesses[*]}"
|
|
printf 'returned to %s native session %s\n' "$first_harness" "$first_native"
|
|
printf 'native harness session stores and resume paths were exercised\n'
|