Skip to content

Commit 50ed1bc

Browse files
authored
Merge pull request #81 from zevorn/feat/rlcr-integral-context
feat: add integral context to RLCR review prompts
2 parents 7c4356c + c4389bb commit 50ed1bc

5 files changed

Lines changed: 324 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 an ancestor of HEAD (not just a valid object) before using it in git log
970+
if [[ -n "$BASE_COMMIT" ]] && git -C "$PROJECT_ROOT" merge-base --is-ancestor "$BASE_COMMIT" HEAD 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: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
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 - nonexistent object
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 (merge-base --is-ancestor)
125+
if [[ -n "$BAD_COMMIT" ]] && git -C "$TEST_DIR/repo" merge-base --is-ancestor "$BAD_COMMIT" HEAD 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 3b: Valid but unrelated commit (not ancestor of HEAD)
151+
# ========================================
152+
echo ""
153+
echo "Test 3b: Valid but unrelated BASE_COMMIT (orphan branch)"
154+
155+
# Create an orphan branch with its own commit, then switch back
156+
cd "$TEST_DIR/repo"
157+
ORIG_BRANCH=$(git rev-parse --abbrev-ref HEAD)
158+
git checkout -q --orphan orphan-test
159+
echo "orphan" > orphan.txt && git add orphan.txt && git commit -q -m "orphan commit"
160+
ORPHAN_COMMIT=$(git rev-parse HEAD)
161+
git checkout -q "$ORIG_BRANCH"
162+
cd - > /dev/null
163+
164+
# ORPHAN_COMMIT exists but is NOT an ancestor of HEAD
165+
if [[ -n "$ORPHAN_COMMIT" ]] && git -C "$TEST_DIR/repo" merge-base --is-ancestor "$ORPHAN_COMMIT" HEAD 2>/dev/null; then
166+
COMMIT_HISTORY="should not reach here"
167+
else
168+
COMMIT_HISTORY=$(git -C "$TEST_DIR/repo" log --oneline --no-decorate --reverse -30 2>/dev/null)
169+
[[ -n "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(base commit unavailable, showing recent branch commits)
170+
${COMMIT_HISTORY}"
171+
fi
172+
[[ -z "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(no commits yet)"
173+
174+
if echo "$COMMIT_HISTORY" | grep -q "base commit unavailable"; then
175+
pass "Unrelated valid commit triggers annotation"
176+
else
177+
fail "Unrelated valid commit annotation" "base commit unavailable" "$COMMIT_HISTORY"
178+
fi
179+
180+
# ========================================
181+
# Test 4: Missing template - fallback renders full section
182+
# ========================================
183+
echo ""
184+
echo "Test 4: Missing template fallback renders full section"
185+
186+
CURRENT_ROUND=2
187+
COMMIT_HISTORY=$(git -C "$TEST_DIR/repo" log --oneline --no-decorate --reverse "$BASE_COMMIT"..HEAD 2>/dev/null | tail -80)
188+
189+
RECENT_ROUND_FILES=""
190+
for (( r = CURRENT_ROUND - 1; r >= 0 && r >= CURRENT_ROUND - 3; r-- )); do
191+
RECENT_ROUND_FILES+="- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-summary.md
192+
- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-review-result.md
193+
"
194+
done
195+
196+
# Use the exact fallback format from the stop hook
197+
COMMIT_HISTORY_SECTION_FALLBACK="## Development History (Integral Context)
198+
\`\`\`
199+
${COMMIT_HISTORY}
200+
\`\`\`
201+
### Recent Round Files
202+
Read these files before conducting your review to understand the trajectory of work:
203+
${RECENT_ROUND_FILES}"
204+
205+
# Point to a non-existent template to force fallback
206+
RESULT=$(load_and_render_safe "$TEMPLATE_DIR" "codex/non-existent-template.md" "$COMMIT_HISTORY_SECTION_FALLBACK" \
207+
"COMMIT_HISTORY=$COMMIT_HISTORY" \
208+
"RECENT_ROUND_FILES=$RECENT_ROUND_FILES")
209+
210+
FALLBACK_OK=true
211+
echo "$RESULT" | grep -q "Development History" || FALLBACK_OK=false
212+
echo "$RESULT" | grep -q "feat: add feature 1" || FALLBACK_OK=false
213+
echo "$RESULT" | grep -q "Recent Round Files" || FALLBACK_OK=false
214+
echo "$RESULT" | grep -q "round-1-summary.md" || FALLBACK_OK=false
215+
echo "$RESULT" | grep -q "round-0-review-result.md" || FALLBACK_OK=false
216+
echo "$RESULT" | grep -q "Read these files" || FALLBACK_OK=false
217+
218+
if [[ "$FALLBACK_OK" == "true" ]]; then
219+
pass "Fallback renders full section with commits, round files, and directive"
220+
else
221+
fail "Fallback full section" "commits + round files + directive" "$RESULT"
222+
fi
223+
224+
# ========================================
225+
# Test 5: Round 1 - only 1 prior round (boundary)
226+
# ========================================
227+
echo ""
228+
echo "Test 5: Round 1 - only 1 prior round"
229+
230+
CURRENT_ROUND=1
231+
RECENT_ROUND_FILES=""
232+
for (( r = CURRENT_ROUND - 1; r >= 0 && r >= CURRENT_ROUND - 3; r-- )); do
233+
RECENT_ROUND_FILES+="- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-summary.md
234+
- @.humanize/rlcr/${LOOP_TIMESTAMP}/round-${r}-review-result.md
235+
"
236+
done
237+
[[ -z "$RECENT_ROUND_FILES" ]] && RECENT_ROUND_FILES="(first round, no prior history)"
238+
239+
if echo "$RECENT_ROUND_FILES" | grep -q "round-0-summary.md" && \
240+
! echo "$RECENT_ROUND_FILES" | grep -q "round-1-"; then
241+
pass "Round 1 references only round 0"
242+
else
243+
fail "Round 1 boundary" "only round-0 references" "$RECENT_ROUND_FILES"
244+
fi
245+
246+
# ========================================
247+
# Test 6: Empty BASE_COMMIT (legacy loop)
248+
# ========================================
249+
echo ""
250+
echo "Test 6: Empty BASE_COMMIT fallback"
251+
252+
EMPTY_BASE=""
253+
if [[ -n "$EMPTY_BASE" ]] && git -C "$TEST_DIR/repo" merge-base --is-ancestor "$EMPTY_BASE" HEAD 2>/dev/null; then
254+
COMMIT_HISTORY="should not reach here"
255+
else
256+
COMMIT_HISTORY=$(git -C "$TEST_DIR/repo" log --oneline --no-decorate --reverse -30 2>/dev/null)
257+
[[ -n "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(base commit unavailable, showing recent branch commits)
258+
${COMMIT_HISTORY}"
259+
fi
260+
[[ -z "$COMMIT_HISTORY" ]] && COMMIT_HISTORY="(no commits yet)"
261+
262+
if echo "$COMMIT_HISTORY" | grep -q "base commit unavailable"; then
263+
pass "Empty BASE_COMMIT triggers annotation"
264+
else
265+
fail "Empty BASE_COMMIT annotation" "base commit unavailable" "$COMMIT_HISTORY"
266+
fi
267+
268+
# ========================================
269+
# Summary
270+
# ========================================
271+
print_test_summary "Commit History Section (I Component) Tests"

0 commit comments

Comments
 (0)