Skip to content

Commit 2474513

Browse files
committed
feat: add integral context to RLCR review prompts (PID complete)
Feed accumulated commit history and recent round summaries/reviews to Codex during each review round, completing the PID feedback model: - P (proportional): goal-tracker vs acceptance criteria - I (integral): accumulated commits + last 3 rounds history - D (derivative): current round summary vs prompt Validate BASE_COMMIT with cat-file -e before git log to prevent set -e crash on corrupted/stale state files. Add test-commit-history-section.sh covering all edge cases (9/9 pass). Signed-off-by: Chao Liu <chao.liu.zevorn@gmail.com>
1 parent 722eb25 commit 2474513

5 files changed

Lines changed: 293 additions & 0 deletions

File tree

hooks/loop-codex-stop-hook.sh

Lines changed: 37 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -965,6 +965,37 @@ COMPLETED_ITERATIONS=$((CURRENT_ROUND + 1))
965965
PREV_ROUND=$(( CURRENT_ROUND > 0 ? CURRENT_ROUND - 1 : 0 ))
966966
PREV_PREV_ROUND=$(( CURRENT_ROUND > 1 ? CURRENT_ROUND - 2 : 0 ))
967967

968+
# Integral component: accumulated commit history and recent round references
969+
# Validate BASE_COMMIT is a reachable git object before using it in git log
970+
if [[ -n "$BASE_COMMIT" ]] && git -C "$PROJECT_ROOT" cat-file -e "$BASE_COMMIT" 2>/dev/null; then
971+
COMMIT_HISTORY=$(git -C "$PROJECT_ROOT" log --oneline --no-decorate --reverse "$BASE_COMMIT"..HEAD 2>/dev/null | tail -80)
972+
else
973+
COMMIT_HISTORY=$(git -C "$PROJECT_ROOT" log --oneline --no-decorate --reverse -30 2>/dev/null)
974+
# Annotate so Codex knows this is not the full loop history
975+
[[ -n "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(base commit unavailable, showing recent branch commits)
976+
${COMMIT_HISTORY}"
977+
fi
978+
[[ -z "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(no commits yet)"
979+
980+
RECENT_ROUND_FILES=""
981+
for (( r = CURRENT_ROUND - 1; r >= 0 && r >= CURRENT_ROUND - 3; r-- )); do
982+
RECENT_ROUND_FILES+="- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-summary.md
983+
- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-review-result.md
984+
"
985+
done
986+
[[ -z "$RECENT_ROUND_FILES" ]] && RECENT_ROUND_FILES="(first round, no prior history)"
987+
988+
COMMIT_HISTORY_SECTION_FALLBACK="## Development History (Integral Context)
989+
\`\`\`
990+
${COMMIT_HISTORY}
991+
\`\`\`
992+
### Recent Round Files
993+
Read these files before conducting your review to understand the trajectory of work:
994+
${RECENT_ROUND_FILES}"
995+
COMMIT_HISTORY_SECTION=$(load_and_render_safe "$TEMPLATE_DIR" "codex/commit-history-section.md" "$COMMIT_HISTORY_SECTION_FALLBACK" \
996+
"COMMIT_HISTORY=$COMMIT_HISTORY" \
997+
"RECENT_ROUND_FILES=$RECENT_ROUND_FILES")
998+
968999
# Build the review prompt
9691000
FULL_ALIGNMENT_FALLBACK="# Full Alignment Review (Round {{CURRENT_ROUND}})
9701001
@@ -973,6 +1004,8 @@ Review Claude's work against the plan and goal tracker. Check all goals are bein
9731004
## Claude's Summary
9741005
{{SUMMARY_CONTENT}}
9751006
1007+
{{COMMIT_HISTORY_SECTION}}
1008+
9761009
{{GOAL_TRACKER_UPDATE_SECTION}}
9771010
9781011
Write your review to {{REVIEW_RESULT_FILE}}. End with COMPLETE if done, or list issues."
@@ -984,6 +1017,8 @@ Review Claude's work for this round.
9841017
## Claude's Summary
9851018
{{SUMMARY_CONTENT}}
9861019
1020+
{{COMMIT_HISTORY_SECTION}}
1021+
9871022
{{GOAL_TRACKER_UPDATE_SECTION}}
9881023
9891024
Write your review to {{REVIEW_RESULT_FILE}}. End with COMPLETE if done, or list issues."
@@ -997,6 +1032,7 @@ if [[ "$FULL_ALIGNMENT_CHECK" == "true" ]]; then
9971032
"GOAL_TRACKER_FILE=$GOAL_TRACKER_FILE" \
9981033
"DOCS_PATH=$DOCS_PATH" \
9991034
"GOAL_TRACKER_UPDATE_SECTION=$GOAL_TRACKER_UPDATE_SECTION" \
1035+
"COMMIT_HISTORY_SECTION=$COMMIT_HISTORY_SECTION" \
10001036
"COMPLETED_ITERATIONS=$COMPLETED_ITERATIONS" \
10011037
"LOOP_TIMESTAMP=$LOOP_TIMESTAMP" \
10021038
"PREV_ROUND=$PREV_ROUND" \
@@ -1013,6 +1049,7 @@ else
10131049
"GOAL_TRACKER_FILE=$GOAL_TRACKER_FILE" \
10141050
"DOCS_PATH=$DOCS_PATH" \
10151051
"GOAL_TRACKER_UPDATE_SECTION=$GOAL_TRACKER_UPDATE_SECTION" \
1052+
"COMMIT_HISTORY_SECTION=$COMMIT_HISTORY_SECTION" \
10161053
"COMPLETED_ITERATIONS=$COMPLETED_ITERATIONS" \
10171054
"LOOP_TIMESTAMP=$LOOP_TIMESTAMP" \
10181055
"PREV_ROUND=$PREV_ROUND" \
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
## Development History (Integral Context)
2+
3+
Accumulated commits since loop start (oldest first):
4+
```
5+
{{COMMIT_HISTORY}}
6+
```
7+
8+
### Recent Round Files
9+
Read these files before conducting your review to understand the trajectory of work:
10+
{{RECENT_ROUND_FILES}}
11+
12+
Use this history to identify patterns across rounds: recurring issues, stalled progress, or drift from the mainline objective. Weight recent rounds more heavily but watch for systemic trends in the full commit log.

prompt-template/codex/full-alignment-review.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,8 @@ You MUST read this plan file first to understand the full scope of work before c
1616
<!-- CLAUDE's WORK SUMMARY END -->
1717
---
1818

19+
{{COMMIT_HISTORY_SECTION}}
20+
1921
## Part 1: Goal Tracker Audit (MANDATORY)
2022

2123
Read @{{GOAL_TRACKER_FILE}} and verify:

prompt-template/codex/regular-review.md

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,8 @@ Below is Claude's summary of the work completed:
1717
<!-- CLAUDE's WORK SUMMARY END -->
1818
---
1919

20+
{{COMMIT_HISTORY_SECTION}}
21+
2022
## Part 1: Implementation Review
2123

2224
- Your task is to conduct a deep critical review, focusing on finding implementation issues and identifying gaps between "plan-design" and actual implementation.
Lines changed: 240 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,240 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Test script for the Integral (I) component: commit-history-section
4+
#
5+
# Validates:
6+
# 1. Round 0: "(no commits yet)" and "(first round, no prior history)"
7+
# 2. Round 2+: commit log and round file references rendered correctly
8+
# 3. Corrupted BASE_COMMIT: graceful fallback with annotation
9+
# 4. Template missing: fallback renders the full section including round files
10+
#
11+
12+
set -euo pipefail
13+
14+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
15+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
16+
source "$SCRIPT_DIR/test-helpers.sh"
17+
source "$PROJECT_ROOT/hooks/lib/template-loader.sh"
18+
19+
TEMPLATE_DIR="$PROJECT_ROOT/prompt-template"
20+
21+
echo "========================================"
22+
echo "Testing commit-history-section (I component)"
23+
echo "========================================"
24+
echo ""
25+
26+
# ========================================
27+
# Setup: create a temporary git repo
28+
# ========================================
29+
setup_test_dir
30+
init_test_git_repo "$TEST_DIR/repo"
31+
32+
# ========================================
33+
# Test 1: Round 0 - no commits since base, first round
34+
# ========================================
35+
echo "Test 1: Round 0 - no commits, first round"
36+
37+
CURRENT_ROUND=0
38+
BASE_COMMIT=$(git -C "$TEST_DIR/repo" rev-parse HEAD)
39+
40+
# No commits since BASE_COMMIT..HEAD (same commit)
41+
COMMIT_HISTORY=$(git -C "$TEST_DIR/repo" log --oneline --no-decorate --reverse "$BASE_COMMIT"..HEAD 2>/dev/null | tail -80)
42+
[[ -z "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(no commits yet)"
43+
44+
RECENT_ROUND_FILES=""
45+
LOOP_TIMESTAMP="2026-01-01_00-00-00"
46+
for (( r = CURRENT_ROUND - 1; r >= 0 && r >= CURRENT_ROUND - 3; r-- )); do
47+
RECENT_ROUND_FILES+="- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-summary.md
48+
- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-review-result.md
49+
"
50+
done
51+
[[ -z "$RECENT_ROUND_FILES" ]] && RECENT_ROUND_FILES="(first round, no prior history)"
52+
53+
RESULT=$(load_and_render_safe "$TEMPLATE_DIR" "codex/commit-history-section.md" "FALLBACK" \
54+
"COMMIT_HISTORY=$COMMIT_HISTORY" \
55+
"RECENT_ROUND_FILES=$RECENT_ROUND_FILES")
56+
57+
if echo "$RESULT" | grep -q "(no commits yet)" && echo "$RESULT" | grep -q "(first round, no prior history)"; then
58+
pass "Round 0 shows correct placeholders"
59+
else
60+
fail "Round 0 placeholders" "(no commits yet) and (first round, no prior history)" "$RESULT"
61+
fi
62+
63+
# ========================================
64+
# Test 2: Round 3 - with commits and round history
65+
# ========================================
66+
echo ""
67+
echo "Test 2: Round 3 - commits and round file references"
68+
69+
# Make some commits
70+
cd "$TEST_DIR/repo"
71+
echo "feat1" > feat1.txt && git add feat1.txt && git commit -q -m "feat: add feature 1"
72+
echo "feat2" > feat2.txt && git add feat2.txt && git commit -q -m "feat: add feature 2"
73+
echo "fix1" > fix1.txt && git add fix1.txt && git commit -q -m "fix: resolve bug in feature 1"
74+
cd - > /dev/null
75+
76+
CURRENT_ROUND=3
77+
COMMIT_HISTORY=$(git -C "$TEST_DIR/repo" log --oneline --no-decorate --reverse "$BASE_COMMIT"..HEAD 2>/dev/null | tail -80)
78+
[[ -z "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(no commits yet)"
79+
80+
RECENT_ROUND_FILES=""
81+
for (( r = CURRENT_ROUND - 1; r >= 0 && r >= CURRENT_ROUND - 3; r-- )); do
82+
RECENT_ROUND_FILES+="- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-summary.md
83+
- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-review-result.md
84+
"
85+
done
86+
[[ -z "$RECENT_ROUND_FILES" ]] && RECENT_ROUND_FILES="(first round, no prior history)"
87+
88+
RESULT=$(load_and_render_safe "$TEMPLATE_DIR" "codex/commit-history-section.md" "FALLBACK" \
89+
"COMMIT_HISTORY=$COMMIT_HISTORY" \
90+
"RECENT_ROUND_FILES=$RECENT_ROUND_FILES")
91+
92+
HAS_COMMITS=true
93+
HAS_ROUNDS=true
94+
95+
echo "$RESULT" | grep -q "feat: add feature 1" || HAS_COMMITS=false
96+
echo "$RESULT" | grep -q "feat: add feature 2" || HAS_COMMITS=false
97+
echo "$RESULT" | grep -q "fix: resolve bug in feature 1" || HAS_COMMITS=false
98+
99+
echo "$RESULT" | grep -q "round-2-summary.md" || HAS_ROUNDS=false
100+
echo "$RESULT" | grep -q "round-1-summary.md" || HAS_ROUNDS=false
101+
echo "$RESULT" | grep -q "round-0-summary.md" || HAS_ROUNDS=false
102+
echo "$RESULT" | grep -q "round-2-review-result.md" || HAS_ROUNDS=false
103+
104+
if [[ "$HAS_COMMITS" == "true" ]]; then
105+
pass "Round 3 shows all 3 commits"
106+
else
107+
fail "Round 3 commits" "3 commit messages" "$RESULT"
108+
fi
109+
110+
if [[ "$HAS_ROUNDS" == "true" ]]; then
111+
pass "Round 3 shows round 0-2 file references"
112+
else
113+
fail "Round 3 round files" "round-0/1/2 summary and review files" "$RESULT"
114+
fi
115+
116+
# ========================================
117+
# Test 3: Corrupted BASE_COMMIT - cat-file -e fails
118+
# ========================================
119+
echo ""
120+
echo "Test 3: Corrupted BASE_COMMIT graceful fallback"
121+
122+
BAD_COMMIT="deadbeefdeadbeefdeadbeefdeadbeefdeadbeef"
123+
124+
# Simulate the exact logic from the stop hook
125+
if [[ -n "$BAD_COMMIT" ]] && git -C "$TEST_DIR/repo" cat-file -e "$BAD_COMMIT" 2>/dev/null; then
126+
COMMIT_HISTORY=$(git -C "$TEST_DIR/repo" log --oneline --no-decorate --reverse "$BAD_COMMIT"..HEAD 2>/dev/null | tail -80)
127+
else
128+
COMMIT_HISTORY=$(git -C "$TEST_DIR/repo" log --oneline --no-decorate --reverse -30 2>/dev/null)
129+
[[ -n "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(base commit unavailable, showing recent branch commits)
130+
${COMMIT_HISTORY}"
131+
fi
132+
[[ -z "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(no commits yet)"
133+
134+
if echo "$COMMIT_HISTORY" | grep -q "base commit unavailable"; then
135+
pass "Corrupted BASE_COMMIT triggers annotation"
136+
else
137+
fail "Corrupted BASE_COMMIT annotation" "base commit unavailable" "$COMMIT_HISTORY"
138+
fi
139+
140+
if echo "$COMMIT_HISTORY" | grep -q "feat: add feature"; then
141+
pass "Corrupted BASE_COMMIT still shows recent commits"
142+
else
143+
fail "Corrupted BASE_COMMIT recent commits" "recent branch commits" "$COMMIT_HISTORY"
144+
fi
145+
146+
# Verify no crash (we got here = no set -e crash)
147+
pass "Corrupted BASE_COMMIT did not crash (set -e safe)"
148+
149+
# ========================================
150+
# Test 4: Missing template - fallback renders full section
151+
# ========================================
152+
echo ""
153+
echo "Test 4: Missing template fallback renders full section"
154+
155+
CURRENT_ROUND=2
156+
COMMIT_HISTORY=$(git -C "$TEST_DIR/repo" log --oneline --no-decorate --reverse "$BASE_COMMIT"..HEAD 2>/dev/null | tail -80)
157+
158+
RECENT_ROUND_FILES=""
159+
for (( r = CURRENT_ROUND - 1; r >= 0 && r >= CURRENT_ROUND - 3; r-- )); do
160+
RECENT_ROUND_FILES+="- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-summary.md
161+
- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-review-result.md
162+
"
163+
done
164+
165+
# Use the exact fallback format from the stop hook
166+
COMMIT_HISTORY_SECTION_FALLBACK="## Development History (Integral Context)
167+
\`\`\`
168+
${COMMIT_HISTORY}
169+
\`\`\`
170+
### Recent Round Files
171+
Read these files before conducting your review to understand the trajectory of work:
172+
${RECENT_ROUND_FILES}"
173+
174+
# Point to a non-existent template to force fallback
175+
RESULT=$(load_and_render_safe "$TEMPLATE_DIR" "codex/non-existent-template.md" "$COMMIT_HISTORY_SECTION_FALLBACK" \
176+
"COMMIT_HISTORY=$COMMIT_HISTORY" \
177+
"RECENT_ROUND_FILES=$RECENT_ROUND_FILES")
178+
179+
FALLBACK_OK=true
180+
echo "$RESULT" | grep -q "Development History" || FALLBACK_OK=false
181+
echo "$RESULT" | grep -q "feat: add feature 1" || FALLBACK_OK=false
182+
echo "$RESULT" | grep -q "Recent Round Files" || FALLBACK_OK=false
183+
echo "$RESULT" | grep -q "round-1-summary.md" || FALLBACK_OK=false
184+
echo "$RESULT" | grep -q "round-0-review-result.md" || FALLBACK_OK=false
185+
echo "$RESULT" | grep -q "Read these files" || FALLBACK_OK=false
186+
187+
if [[ "$FALLBACK_OK" == "true" ]]; then
188+
pass "Fallback renders full section with commits, round files, and directive"
189+
else
190+
fail "Fallback full section" "commits + round files + directive" "$RESULT"
191+
fi
192+
193+
# ========================================
194+
# Test 5: Round 1 - only 1 prior round (boundary)
195+
# ========================================
196+
echo ""
197+
echo "Test 5: Round 1 - only 1 prior round"
198+
199+
CURRENT_ROUND=1
200+
RECENT_ROUND_FILES=""
201+
for (( r = CURRENT_ROUND - 1; r >= 0 && r >= CURRENT_ROUND - 3; r-- )); do
202+
RECENT_ROUND_FILES+="- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-summary.md
203+
- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-review-result.md
204+
"
205+
done
206+
[[ -z "$RECENT_ROUND_FILES" ]] && RECENT_ROUND_FILES="(first round, no prior history)"
207+
208+
if echo "$RECENT_ROUND_FILES" | grep -q "round-0-summary.md" && \
209+
! echo "$RECENT_ROUND_FILES" | grep -q "round-1-"; then
210+
pass "Round 1 references only round 0"
211+
else
212+
fail "Round 1 boundary" "only round-0 references" "$RECENT_ROUND_FILES"
213+
fi
214+
215+
# ========================================
216+
# Test 6: Empty BASE_COMMIT (legacy loop)
217+
# ========================================
218+
echo ""
219+
echo "Test 6: Empty BASE_COMMIT fallback"
220+
221+
EMPTY_BASE=""
222+
if [[ -n "$EMPTY_BASE" ]] && git -C "$TEST_DIR/repo" cat-file -e "$EMPTY_BASE" 2>/dev/null; then
223+
COMMIT_HISTORY="should not reach here"
224+
else
225+
COMMIT_HISTORY=$(git -C "$TEST_DIR/repo" log --oneline --no-decorate --reverse -30 2>/dev/null)
226+
[[ -n "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(base commit unavailable, showing recent branch commits)
227+
${COMMIT_HISTORY}"
228+
fi
229+
[[ -z "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(no commits yet)"
230+
231+
if echo "$COMMIT_HISTORY" | grep -q "base commit unavailable"; then
232+
pass "Empty BASE_COMMIT triggers annotation"
233+
else
234+
fail "Empty BASE_COMMIT annotation" "base commit unavailable" "$COMMIT_HISTORY"
235+
fi
236+
237+
# ========================================
238+
# Summary
239+
# ========================================
240+
print_test_summary "Commit History Section (I Component) Tests"

0 commit comments

Comments
 (0)