Skip to content

Commit 9a7f176

Browse files
mtwentaoclaude
andcommitted
Block RLCR placeholder summaries
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
1 parent 0ec921a commit 9a7f176

5 files changed

Lines changed: 249 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
@@ -821,6 +821,43 @@ Please write your work summary to: {{SUMMARY_FILE}}"
821821
exit 0
822822
fi
823823

824+
# Check Summary File Is Not Still the Scaffold
825+
# ============================================
826+
# Round summary files are pre-created as editing targets. A file existing is
827+
# therefore not proof that Claude actually summarized completed work. Block the
828+
# common contract-only failure mode before spending a Codex review when the
829+
# summary still contains template placeholders.
830+
if [[ "$IS_FINALIZE_PHASE" != "true" ]]; then
831+
SUMMARY_PLACEHOLDERS=$(grep -nE '\[(Describe what was|List |what changed and why)|none\|add\|update' "$SUMMARY_FILE" 2>/dev/null || true)
832+
if [[ -n "$SUMMARY_PLACEHOLDERS" ]]; then
833+
FALLBACK="# Work Summary Still Placeholder
834+
835+
The summary file exists but still contains scaffold placeholder text:
836+
837+
{{PLACEHOLDER_LINES}}
838+
839+
Writing the round contract is only step 0; it is not implementation progress.
840+
Before exiting, complete at least one non-queued mainline/blocking task and
841+
replace the summary with concrete work, changed files, validation, and remaining
842+
items.
843+
844+
Summary file: {{SUMMARY_FILE}}"
845+
REASON=$(load_and_render_safe "$TEMPLATE_DIR" "block/work-summary-placeholder.md" "$FALLBACK" \
846+
"SUMMARY_FILE=$SUMMARY_FILE" \
847+
"PLACEHOLDER_LINES=$SUMMARY_PLACEHOLDERS")
848+
849+
jq -n \
850+
--arg reason "$REASON" \
851+
--arg msg "Loop: Summary file still contains placeholders for round $CURRENT_ROUND" \
852+
'{
853+
"decision": "block",
854+
"reason": $reason,
855+
"systemMessage": $msg
856+
}'
857+
exit 0
858+
fi
859+
fi
860+
824861
# Check Round Contract Exists
825862
# ========================================
826863

Lines changed: 21 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,21 @@
1+
# Work Summary Still Placeholder
2+
3+
You attempted to exit while the round summary still contains scaffold placeholder text.
4+
5+
**Placeholder lines detected:**
6+
7+
```text
8+
{{PLACEHOLDER_LINES}}
9+
```
10+
11+
**Required Action**:
12+
1. Do not treat the round contract as implementation progress; it is only step 0.
13+
2. Complete at least one non-queued `[mainline]` or truly `[blocking]` task.
14+
3. Replace the summary at `{{SUMMARY_FILE}}` with concrete evidence:
15+
- What was implemented
16+
- Files created/modified
17+
- Validation/tests/commands and outcomes
18+
- Remaining items, if any
19+
- BitLesson Delta
20+
21+
After replacing the placeholder summary, retry the exit.

prompt-template/claude/drift-replan-prompt.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,6 +32,12 @@ Your recovery contract must contain:
3232

3333
Do not start implementation until the recovery contract exists.
3434

35+
**Important**: Rewriting the recovery contract is only the re-anchor
36+
precondition for drift recovery; it is not implementation progress. Do not
37+
attempt to exit after contract-only work. Before exiting, complete at least one
38+
non-queued `[mainline]` or truly `[blocking]` task that proves recovered
39+
mainline movement and replace the summary scaffold with concrete evidence.
40+
3541
## Task Lane Rules
3642

3743
Use the Task system (TaskCreate, TaskUpdate, TaskList) with one required tag per task:

prompt-template/claude/next-round-prompt.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -26,6 +26,12 @@ Your round contract must contain:
2626

2727
Do not start implementation until the round contract exists.
2828

29+
**Important**: Writing or updating the round contract is only the re-anchor
30+
precondition for this round; it is not implementation progress. Do not attempt
31+
to exit after contract-only work. Before exiting, complete at least one
32+
non-queued `[mainline]` or truly `[blocking]` task and replace the summary
33+
scaffold with concrete evidence.
34+
2935
## Task Lane Rules
3036

3137
Use the Task system (TaskCreate, TaskUpdate, TaskList) with one required tag per task:
Lines changed: 179 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#!/usr/bin/env bash
2+
#
3+
# Regression tests for summary placeholder gating in loop-codex-stop-hook.sh.
4+
#
5+
# Round summary files are pre-created as scaffold targets. The stop hook must
6+
# reject a scaffold summary before running Codex, otherwise a contract-only
7+
# round can be reviewed as if implementation work was attempted.
8+
9+
set -euo pipefail
10+
11+
SCRIPT_DIR="$(cd "$(dirname "${BASH_SOURCE[0]}")" && pwd)"
12+
PROJECT_ROOT="$(cd "$SCRIPT_DIR/.." && pwd)"
13+
source "$SCRIPT_DIR/test-helpers.sh"
14+
15+
STOP_HOOK="$PROJECT_ROOT/hooks/loop-codex-stop-hook.sh"
16+
17+
setup_test_dir
18+
export XDG_CACHE_HOME="$TEST_DIR/.cache"
19+
mkdir -p "$XDG_CACHE_HOME"
20+
21+
setup_mock_codex() {
22+
mkdir -p "$TEST_DIR/bin"
23+
cat > "$TEST_DIR/bin/codex" << 'EOF'
24+
#!/usr/bin/env bash
25+
if [[ -n "${MOCK_CODEX_MARKER:-}" ]]; then
26+
: > "$MOCK_CODEX_MARKER"
27+
fi
28+
echo "Mainline Progress Verdict: STALLED"
29+
echo "Final Decision: NOT COMPLETE"
30+
exit 0
31+
EOF
32+
chmod +x "$TEST_DIR/bin/codex"
33+
export PATH="$TEST_DIR/bin:$PATH"
34+
}
35+
36+
create_loop_fixture() {
37+
local repo_dir="$1"
38+
local summary_body="$2"
39+
40+
init_test_git_repo "$repo_dir"
41+
printf 'plans/\n' > "$repo_dir/.gitignore"
42+
git -C "$repo_dir" add .gitignore
43+
git -C "$repo_dir" commit -q -m "Add gitignore"
44+
45+
mkdir -p "$repo_dir/plans"
46+
cat > "$repo_dir/plans/plan.md" << 'EOF'
47+
# Test Plan
48+
49+
Complete real implementation work.
50+
EOF
51+
52+
local loop_dir="$repo_dir/.humanize/rlcr/2026-03-01_00-00-00"
53+
mkdir -p "$loop_dir"
54+
cp "$repo_dir/plans/plan.md" "$loop_dir/plan.md"
55+
56+
local branch base_commit
57+
branch=$(git -C "$repo_dir" rev-parse --abbrev-ref HEAD)
58+
base_commit=$(git -C "$repo_dir" rev-parse HEAD)
59+
60+
cat > "$loop_dir/state.md" << EOF
61+
---
62+
current_round: 0
63+
max_iterations: 42
64+
codex_model: gpt-5.5
65+
codex_effort: high
66+
codex_timeout: 60
67+
push_every_round: false
68+
full_review_round: 5
69+
plan_file: "plans/plan.md"
70+
plan_tracked: false
71+
start_branch: $branch
72+
base_branch: $branch
73+
base_commit: $base_commit
74+
review_started: false
75+
ask_codex_question: false
76+
agent_teams: false
77+
bitlesson_required: false
78+
mainline_stall_count: 0
79+
last_mainline_verdict: unknown
80+
drift_status: normal
81+
---
82+
EOF
83+
84+
printf '%s\n' "$summary_body" > "$loop_dir/round-0-summary.md"
85+
86+
cat > "$loop_dir/round-0-contract.md" << 'EOF'
87+
# Round 0 Contract
88+
89+
- Mainline Objective: Complete real implementation work.
90+
- Target ACs: AC-1.
91+
- Blocking: none.
92+
- Queued: none.
93+
- Success Criteria: concrete implementation evidence exists.
94+
EOF
95+
96+
cat > "$loop_dir/goal-tracker.md" << 'EOF'
97+
# Goal Tracker
98+
## IMMUTABLE SECTION
99+
### Ultimate Goal
100+
Complete real implementation work.
101+
### Acceptance Criteria
102+
- AC-1: Work evidence exists.
103+
---
104+
## MUTABLE SECTION
105+
### Plan Version: 1 (Updated: Round 0)
106+
#### Active Tasks
107+
| Task | Target AC | Status | Tag | Owner | Notes |
108+
|------|-----------|--------|-----|-------|-------|
109+
EOF
110+
111+
echo "$loop_dir"
112+
}
113+
114+
run_stop_hook() {
115+
local repo_dir="$1"
116+
printf '{"hook_event_name":"Stop","cwd":"%s","session_id":"test-session","transcript_path":null}\n' "$repo_dir" \
117+
| CLAUDE_PROJECT_DIR="$repo_dir" "$STOP_HOOK"
118+
}
119+
120+
setup_mock_codex
121+
122+
echo "=========================================="
123+
echo "Summary Placeholder Gate Tests"
124+
echo "=========================================="
125+
126+
# Test 1: scaffold summary blocks before Codex runs.
127+
repo1="$TEST_DIR/repo-placeholder"
128+
marker1="$TEST_DIR/codex-ran-placeholder"
129+
summary_placeholder='# Round 0 Summary
130+
131+
## Work Completed
132+
- [Describe what was implemented in this phase]
133+
134+
## Files Changed
135+
- [List created/modified files]
136+
137+
## Validation
138+
- [List tests/commands run and outcomes]'
139+
create_loop_fixture "$repo1" "$summary_placeholder" >/dev/null
140+
export MOCK_CODEX_MARKER="$marker1"
141+
output1=$(run_stop_hook "$repo1")
142+
unset MOCK_CODEX_MARKER
143+
msg1=$(printf '%s' "$output1" | jq -r '.systemMessage // empty')
144+
reason1=$(printf '%s' "$output1" | jq -r '.reason // empty')
145+
if [[ "$msg1" == "Loop: Summary file still contains placeholders for round 0" ]] && \
146+
[[ "$reason1" == *"Work Summary Still Placeholder"* ]] && \
147+
[[ ! -e "$marker1" ]]; then
148+
pass "placeholder summary is blocked before Codex runs"
149+
else
150+
fail "placeholder summary is blocked before Codex runs" "placeholder block without Codex marker" "msg=$msg1 marker=$([[ -e "$marker1" ]] && echo yes || echo no)"
151+
fi
152+
153+
# Test 2: concrete summary reaches Codex review path.
154+
repo2="$TEST_DIR/repo-concrete"
155+
marker2="$TEST_DIR/codex-ran-concrete"
156+
summary_concrete='# Round 0 Summary
157+
158+
## Work Completed
159+
- Implemented the target change and captured validation evidence.
160+
161+
## Files Changed
162+
- hooks/loop-codex-stop-hook.sh
163+
164+
## Validation
165+
- bash -n hooks/loop-codex-stop-hook.sh: passed
166+
167+
## Remaining Items
168+
- None.'
169+
create_loop_fixture "$repo2" "$summary_concrete" >/dev/null
170+
export MOCK_CODEX_MARKER="$marker2"
171+
output2=$(run_stop_hook "$repo2")
172+
unset MOCK_CODEX_MARKER
173+
if [[ -e "$marker2" ]] && [[ "$output2" == *"Codex found issues"* || "$output2" == *"Mainline Progress Verdict"* ]]; then
174+
pass "concrete summary reaches Codex review"
175+
else
176+
fail "concrete summary reaches Codex review" "Codex marker created" "marker=$([[ -e "$marker2" ]] && echo yes || echo no) output=$output2"
177+
fi
178+
179+
print_test_summary "Summary Placeholder Gate Test Summary"

0 commit comments

Comments
 (0)