Skip to content

Commit bf720b9

Browse files
committed
test: add codex hookless lifecycle smoke
1 parent bc7a18d commit bf720b9

2 files changed

Lines changed: 223 additions & 0 deletions

File tree

‎docs/architecture/codex-hookless-lifecycle.md‎

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -92,6 +92,13 @@ The Codex fallback reuses the existing pool and close-loop hygiene instead of in
9292
- Search citation is assisted rather than inferred from every result automatically; the user or skill must choose `--cite` when adoption is known.
9393
- Poor extracted artifacts are not promoted automatically just because they were generated in Codex.
9494

95+
## Verification
96+
97+
Use these non-release checks to verify the Codex fallback from the current worktree:
98+
99+
- `bash scripts/test-codex-hookless-lifecycle.sh` builds the local `ao` binary, seeds a temp Codex home and temp repo, then verifies `ao codex start`, retrieval/citation, `ao codex stop`, and `ao codex status`.
100+
- `bash scripts/test-codex-native-install.sh --skip-lint` verifies the checked-in Codex plugin bundle and public installer flow in a temp home without cutting a tag.
101+
95102
## See Also
96103

97104
- [Knowledge Flywheel](../knowledge-flywheel.md)
Lines changed: 216 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,216 @@
1+
#!/usr/bin/env bash
2+
set -euo pipefail
3+
4+
# test-codex-hookless-lifecycle.sh
5+
# Verifies the non-release Codex hookless lifecycle path against a locally built ao binary.
6+
#
7+
# What it checks:
8+
# 1) builds ao from the current worktree unless --ao-bin is supplied
9+
# 2) seeds a temp Codex home with session_index + history fallback data
10+
# 3) seeds a temp repo with repo-local learnings
11+
# 4) verifies ao codex start writes startup context/state and retrieved citations
12+
# 5) verifies ao lookup and ao search --local --cite record citations
13+
# 6) verifies ao codex stop uses history fallback and writes lifecycle state
14+
# 7) verifies ao codex status reports coherent hookless health and citation counts
15+
#
16+
# Usage:
17+
# bash scripts/test-codex-hookless-lifecycle.sh
18+
# bash scripts/test-codex-hookless-lifecycle.sh --keep-temp
19+
# bash scripts/test-codex-hookless-lifecycle.sh --ao-bin /path/to/ao
20+
21+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
22+
REPO_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
23+
cd "$REPO_ROOT"
24+
25+
AO_BIN=""
26+
KEEP_TEMP="false"
27+
TMP_ROOT=""
28+
29+
usage() {
30+
cat <<'EOF'
31+
test-codex-hookless-lifecycle.sh
32+
33+
Options:
34+
--ao-bin <path> Use an existing ao binary instead of building ./cli/cmd/ao
35+
--keep-temp Preserve the temp Codex home + repo for inspection
36+
--help Show this help
37+
38+
Examples:
39+
bash scripts/test-codex-hookless-lifecycle.sh
40+
bash scripts/test-codex-hookless-lifecycle.sh --keep-temp
41+
bash scripts/test-codex-hookless-lifecycle.sh --ao-bin ./cli/bin/ao
42+
EOF
43+
}
44+
45+
while [[ $# -gt 0 ]]; do
46+
case "$1" in
47+
--ao-bin)
48+
AO_BIN="${2:-}"
49+
shift 2
50+
;;
51+
--keep-temp)
52+
KEEP_TEMP="true"
53+
shift 1
54+
;;
55+
--help|-h)
56+
usage
57+
exit 0
58+
;;
59+
*)
60+
echo "Unknown arg: $1" >&2
61+
usage >&2
62+
exit 2
63+
;;
64+
esac
65+
done
66+
67+
info() {
68+
echo "INFO: $*"
69+
}
70+
71+
fail() {
72+
echo "FAIL: $*" >&2
73+
exit 1
74+
}
75+
76+
require_cmd() {
77+
command -v "$1" >/dev/null 2>&1 || fail "Required command not found: $1"
78+
}
79+
80+
cleanup() {
81+
if [[ "$KEEP_TEMP" != "true" && -n "${TMP_ROOT:-}" && -d "$TMP_ROOT" ]]; then
82+
rm -rf "$TMP_ROOT"
83+
fi
84+
}
85+
trap cleanup EXIT
86+
87+
require_cmd bash
88+
require_cmd git
89+
require_cmd jq
90+
require_cmd rg
91+
92+
TMP_ROOT="$(mktemp -d "${TMPDIR:-/tmp}/codex-hookless-lifecycle-test.XXXXXX")"
93+
HOME_ROOT="$TMP_ROOT/home"
94+
REPO_DIR="$TMP_ROOT/repo"
95+
CODEX_HOME="$HOME_ROOT/.codex"
96+
SESSION_ID="019d1bf7-58ea-79e1-9f5d-02109d930081"
97+
QUERY="explicit codex lifecycle"
98+
99+
mkdir -p "$CODEX_HOME" "$REPO_DIR/.agents/learnings"
100+
101+
if [[ -z "$AO_BIN" ]]; then
102+
require_cmd go
103+
AO_BIN="$TMP_ROOT/ao"
104+
info "Building ao from current worktree"
105+
(
106+
cd "$REPO_ROOT/cli"
107+
go build -o "$AO_BIN" ./cmd/ao
108+
)
109+
fi
110+
111+
[[ -x "$AO_BIN" ]] || fail "ao binary is not executable: $AO_BIN"
112+
113+
cat > "$CODEX_HOME/session_index.jsonl" <<EOF
114+
{"id":"$SESSION_ID","thread_name":"Hookless fallback smoke","updated_at":"2026-03-23T12:00:00Z"}
115+
EOF
116+
117+
cat > "$CODEX_HOME/history.jsonl" <<EOF
118+
{"session_id":"$SESSION_ID","ts":1766945655,"text":"Review prior learnings for codex fallback"}
119+
{"session_id":"$SESSION_ID","ts":1766945658,"text":"Implement explicit ao codex stop closeout"}
120+
EOF
121+
122+
cat > "$REPO_DIR/.agents/learnings/codex-lifecycle.md" <<'EOF'
123+
---
124+
id: codex-lifecycle
125+
type: learning
126+
date: 2026-03-23
127+
source: codex-test
128+
maturity: provisional
129+
utility: 0.9
130+
---
131+
132+
# Explicit Codex lifecycle
133+
134+
Use ao codex start and ao codex stop when runtime hooks are unavailable.
135+
EOF
136+
137+
cat > "$REPO_DIR/.agents/learnings/codex-lifecycle.jsonl" <<'EOF'
138+
{"id":"codex-lifecycle-jsonl","summary":"Explicit Codex lifecycle uses ao codex start and ao codex stop when runtime hooks are unavailable.","maturity":"provisional","utility":0.9}
139+
EOF
140+
141+
git init -q "$REPO_DIR"
142+
143+
run_ao() {
144+
(
145+
cd "$REPO_DIR"
146+
HOME="$HOME_ROOT" \
147+
CODEX_THREAD_ID="$SESSION_ID" \
148+
CODEX_INTERNAL_ORIGINATOR_OVERRIDE="Codex Desktop" \
149+
"$AO_BIN" "$@"
150+
)
151+
}
152+
153+
assert_json() {
154+
local json="$1"
155+
local expr="$2"
156+
local message="$3"
157+
printf '%s' "$json" | jq -e "$expr" >/dev/null || fail "$message"
158+
}
159+
160+
info "Running ao codex start"
161+
start_json="$(run_ao codex start --json --query "$QUERY")"
162+
assert_json "$start_json" '.runtime.mode == "codex-hookless-fallback"' "codex start did not detect hookless Codex mode"
163+
assert_json "$start_json" '.learnings | length >= 1' "codex start did not surface repo-local learnings"
164+
165+
START_CONTEXT_PATH="$(printf '%s' "$start_json" | jq -r '.startup_context_path')"
166+
STATE_PATH="$(printf '%s' "$start_json" | jq -r '.state_path')"
167+
MEMORY_PATH="$(printf '%s' "$start_json" | jq -r '.memory_path')"
168+
[[ -f "$START_CONTEXT_PATH" ]] || fail "startup context not written: $START_CONTEXT_PATH"
169+
[[ -f "$STATE_PATH" ]] || fail "lifecycle state not written on start: $STATE_PATH"
170+
[[ -n "$MEMORY_PATH" && "$MEMORY_PATH" != "null" ]] || fail "codex start did not report a memory path"
171+
172+
CITATIONS_PATH="$REPO_DIR/.agents/ao/citations.jsonl"
173+
[[ -f "$CITATIONS_PATH" ]] || fail "citations ledger missing after codex start"
174+
rg -q '"citation_type":"retrieved"' "$CITATIONS_PATH" || fail "codex start did not record retrieved citations"
175+
176+
info "Running ao lookup for curated retrieval"
177+
lookup_json="$(run_ao lookup --query "$QUERY" --limit 1 --json)"
178+
assert_json "$lookup_json" '.learnings | length >= 1' "lookup did not return the seeded learning"
179+
180+
info "Running ao search --local with assisted citation"
181+
search_json="$(run_ao search "$QUERY" --local --json --cite reference)"
182+
assert_json "$search_json" 'length >= 1' "local search did not return the seeded learning"
183+
rg -q '"citation_type":"reference"' "$CITATIONS_PATH" || fail "search --cite reference did not record a reference citation"
184+
185+
info "Running ao codex stop"
186+
stop_json="$(run_ao codex stop --json)"
187+
assert_json "$stop_json" '.runtime.mode == "codex-hookless-fallback"' "codex stop did not report hookless Codex mode"
188+
assert_json "$stop_json" '.transcript_source == "history-fallback"' "codex stop did not use history fallback in the temp Codex home"
189+
assert_json "$stop_json" '.synthetic_transcript == true' "codex stop did not mark the history transcript as synthetic"
190+
191+
TRANSCRIPT_PATH="$(printf '%s' "$stop_json" | jq -r '.transcript_path')"
192+
HANDOFF_PATH="$(printf '%s' "$stop_json" | jq -r '.session.handoff_written')"
193+
[[ -f "$TRANSCRIPT_PATH" ]] || fail "history fallback transcript missing: $TRANSCRIPT_PATH"
194+
[[ -f "$HANDOFF_PATH" ]] || fail "closeout handoff artifact missing: $HANDOFF_PATH"
195+
196+
info "Running ao codex status"
197+
status_json="$(run_ao codex status --json)"
198+
assert_json "$status_json" '.runtime.mode == "codex-hookless-fallback"' "codex status did not report hookless Codex mode"
199+
printf '%s' "$status_json" | jq -e --arg path "$START_CONTEXT_PATH" '.state.last_start.startup_context_path == $path' >/dev/null \
200+
|| fail "codex status did not preserve the startup context path"
201+
assert_json "$status_json" '.state.last_stop.transcript_source == "history-fallback"' "codex status did not preserve the stop transcript source"
202+
assert_json "$status_json" '.retrieval.learnings >= 1' "codex status did not report retrievable learnings"
203+
assert_json "$status_json" '.capture.sessions_indexed >= 1' "codex status did not report indexed sessions"
204+
assert_json "$status_json" '.citations.retrieved >= 2' "codex status retrieved citation count is too low"
205+
assert_json "$status_json" '.citations.reference >= 1' "codex status reference citation count is too low"
206+
assert_json "$status_json" '.citations.total >= 3' "codex status total citation count is too low"
207+
208+
echo ""
209+
echo "PASS: Codex hookless lifecycle smoke verified"
210+
echo " ao binary: $AO_BIN"
211+
echo " temp root: $TMP_ROOT"
212+
echo " repo dir: $REPO_DIR"
213+
214+
if [[ "$KEEP_TEMP" != "true" ]]; then
215+
echo " temp files: cleaned on exit"
216+
fi

0 commit comments

Comments
 (0)