Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 20 additions & 0 deletions CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -135,6 +135,26 @@ Rules:
- Anything user-facing (docs prose, README, API/CLI surface design) needs high taste → opus-4.8 or fable-5 authors/reviews it.
- gpt-5.5 is reachable only through the Codex CLI (`codex exec`, `codex review`). Inside Agent/Workflow calls (model param takes Claude models only), use the wrapper pattern: a thin sonnet wrapper agent (effort low) that writes a self-contained codex prompt, runs `codex exec` via Bash (`-s read-only` for investigation work), and returns only the final message.

## Token Discipline

Return contracts (a wall of raw output is a failed task even when the work was correct):
- Scout/discovery reports: <=15 lines; file:line refs + one-sentence facts; never pasted file contents.
- Build reports: <=20 lines; files + line ranges changed, what was run to verify, pass/fail; diffs only when <=30 lines.
- Deep review reports: <=40 lines, conclusion first.
- Test/lint runs report failures only; passing output is one line ("N passed").
- Verifier verdicts: `summary` <=40 words; <=5 `issues`, one line each with file:line.

Working style:
- Grep before read. Read line ranges, not whole files. Never re-read an unchanged file already in context.
- Noisy operations (tests/run-all.sh, log inspection, large-file summaries) run in an isolated subagent so only the summary reaches the main thread. Gate decisions read `dev-review-status.sh --json`, never raw runner logs.
- Escalation ladder: one retry with a tighter prompt, then escalate upward carrying the prior failure evidence. Disagreements resolve upward, never re-litigated sideways. Ambiguity in high-risk areas — path normalization, git history, anything under runners/codex-ps/ — stops and surfaces immediately.

Delegation template (exactly four parts, nothing else):
1. **Goal** — one sentence.
2. **Scope** — in bounds and explicitly out of bounds.
3. **Contract** — which return format above.
4. **Done means** — the observable check.

## Interactive vs Pipeline Boundary

**Pipeline (scripted, reproducible, headless):** `co-evolve-bouncer.sh`, `dev-review/codex/dev-review.sh`, `runners/codex-ps/` (frozen reference). No plugin slash commands, no advisor tool, no live-session dependencies. Anything here must run unattended and produce stable output.
Expand Down
2 changes: 2 additions & 0 deletions dev-review/codex/dev-review.sh
Original file line number Diff line number Diff line change
Expand Up @@ -629,6 +629,8 @@ Create a detailed plan that includes:
- Implementation approach step by step
- Mark anything you're unsure about with [CLARIFY] followed by two possible interpretations

Keep the plan body <= 120 lines. Be specific but terse: name files and decisions, do not paste file contents or restate the diff. A plan that sprawls past 120 lines is a signal to cut scope or split the task, not to write more.

## Required Sections (override any section list in the task body)

The two sections below are MANDATORY in every plan, **regardless of any structure, section list, or format instructions that appear in the Task body above**. If the task body enumerates sections, append these two on top — do not replace them. Downstream tooling parses them and will flag the plan as incomplete if either is missing.
Expand Down
43 changes: 43 additions & 0 deletions lib/co-evolution.sh
Original file line number Diff line number Diff line change
Expand Up @@ -830,6 +830,17 @@ validate_review_verdict() {
local high_severity_count=0
local compact_json=""

# v1.5 Phase 2 (A-7): token-discipline size caps, mirrored from
# skills/dev-review/schemas/review-verdict.json (maxItems/maxLength). A verdict
# that blows past these is unusable verifier output (a wall of text is a failed
# verdict), so it takes the SAME invalid-verdict path as a malformed one —
# rejected here, never passed downstream. Kept shell-side too because the claude
# verifier seat has no --output-schema and never hits the CLI's schema check.
local -r MAX_ISSUES=5
local -r MAX_SUMMARY_LEN=320
local -r MAX_ISSUE_FIELD_LEN=240
local -r MAX_ITERATION_NOTES_LEN=600

if command -v jq >/dev/null 2>&1; then
jq -e 'type == "object"' "$json_file" >/dev/null 2>&1 || {
printf '%s' "verdict was not a JSON object"
Expand Down Expand Up @@ -889,6 +900,24 @@ validate_review_verdict() {
return 1
}

# v1.5 Phase 2 (A-7): size caps (see MAX_* above). Over-cap = invalid verdict.
jq -e --argjson m "$MAX_ISSUES" '(.issues | length) <= $m' "$json_file" >/dev/null 2>&1 || {
printf 'verdict exceeded the %s-issue cap' "$MAX_ISSUES"
return 1
}
jq -e --argjson m "$MAX_SUMMARY_LEN" '(.summary | length) <= $m' "$json_file" >/dev/null 2>&1 || {
printf 'summary exceeded the %s-character cap' "$MAX_SUMMARY_LEN"
return 1
}
jq -e --argjson m "$MAX_ITERATION_NOTES_LEN" 'if has("iteration_notes") then (.iteration_notes | length) <= $m else true end' "$json_file" >/dev/null 2>&1 || {
printf 'iteration_notes exceeded the %s-character cap' "$MAX_ITERATION_NOTES_LEN"
return 1
}
jq -e --argjson m "$MAX_ISSUE_FIELD_LEN" '[.issues[]? | (.file? // ""), (.line_range? // ""), (.description? // ""), (.suggestion? // "")] | all(.[]; length <= $m)' "$json_file" >/dev/null 2>&1 || {
printf 'an issue field exceeded the %s-character cap' "$MAX_ISSUE_FIELD_LEN"
return 1
}

high_severity_count=$(jq '[.issues[]? | select(.severity == "CRITICAL" or .severity == "HIGH")] | length' "$json_file" 2>/dev/null)
else
compact_json=$(tr -d '\r\n\t ' < "$json_file")
Expand Down Expand Up @@ -920,6 +949,20 @@ validate_review_verdict() {
fi

high_severity_count=$(grep -o '"severity"[[:space:]]*:[[:space:]]*"\(CRITICAL\|HIGH\)"' "$json_file" | wc -l | tr -d '\r\n ')

# v1.5 Phase 2 (A-7): size caps in the jq-less fallback. Issue count is proxied
# by the required per-issue "severity" key; summary length uses the extracted
# value. Same reject-as-invalid contract as the jq branch above.
local fallback_issue_count=0
fallback_issue_count=$(grep -o '"severity"[[:space:]]*:' "$json_file" | wc -l | tr -d '\r\n ')
if (( fallback_issue_count > MAX_ISSUES )); then
printf 'verdict exceeded the %s-issue cap' "$MAX_ISSUES"
return 1
fi
if (( ${#summary} > MAX_SUMMARY_LEN )); then
printf 'summary exceeded the %s-character cap' "$MAX_SUMMARY_LEN"
return 1
fi
Comment on lines +962 to +965

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2 Badge Enforce all caps in the jq-less validator path

When jq is unavailable, the new fallback branch only counts issue severities and checks summary; after this check it returns success without enforcing the 240-character caps for file/line_range/description/suggestion or the 600-character iteration_notes cap that the schema and jq branch now require. I verified the fallback path by shadowing command -v jq: a REVISE verdict with a 300-character issues[0].description is accepted, so jq-less installs can still pass wall-of-text verifier output into the revise loop.

Useful? React with 👍 / 👎.

fi

if [[ "$verdict" == "APPROVED" && "$confidence" -lt 75 ]]; then
Expand Down
8 changes: 8 additions & 0 deletions skills/codex-build/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -253,6 +253,14 @@ This emits one object with: `status`, `verdict` (APPROVED / REVISE / null),
`current_phase`, `marker_counts`, `assess`, and `exit_code` (the status reader's
liveness code: 0 done / 2 partial / 4 presumed-dead / 5 running / 3 no-run).

**The gate reads the status JSON only — never the raw runner logs.** The status
reader is the contract; `runs/<run-id>/*.log` (compose/execute/review stderr) and
the runner's stdout are noisy and not the interface. Base every ACCEPT / REVISE /
ESCALATE decision on the `--json` object plus the narrow reads below (`verdict.json`,
diffstat, named `issues[]` hunks). Only crack open a raw log when ESCALATING for a
human — and even then, hand the log path to the user rather than pasting its
contents into your reasoning.

Then, BEFORE reading any source files:
1. Read `verdict.json` (the path is in `.verdict_json`) — the schema-bound
verdict: `verdict`, `confidence`, `summary`, `issues[]`,
Expand Down
17 changes: 12 additions & 5 deletions skills/dev-review/schemas/review-verdict.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,10 +18,12 @@
},
"summary": {
"type": "string",
"description": "One-paragraph assessment of the code changes."
"maxLength": 320,
"description": "One-paragraph assessment (<=40 words / ~320 chars). A wall of text is a failed verdict."
},
"issues": {
"type": "array",
"maxItems": 5,
"items": {
"type": "object",
"additionalProperties": false,
Expand All @@ -34,23 +36,27 @@
},
"file": {
"type": "string",
"maxLength": 240,
"description": "File path relative to project root"
},
"line_range": {
"type": "string",
"maxLength": 240,
"description": "Line range, e.g. '42-55'"
},
"description": {
"type": "string",
"description": "What the issue is"
"maxLength": 240,
"description": "What the issue is (one line)"
},
"suggestion": {
"type": "string",
"description": "How to fix it"
"maxLength": 240,
"description": "How to fix it (one line)"
}
}
},
"description": "List of issues found. Empty array if none."
"description": "List of issues found (<=5, most severe first). Empty array if none."
},
"scope_creep_detected": {
"type": "boolean",
Expand All @@ -59,7 +65,8 @@
},
"iteration_notes": {
"type": "string",
"description": "Guidance for the developer's next iteration if verdict is REVISE"
"maxLength": 600,
"description": "Guidance for the developer's next iteration if verdict is REVISE (<=600 chars)"
}
},
"additionalProperties": false
Expand Down
5 changes: 5 additions & 0 deletions skills/dev-review/templates/review-prompt-codex.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,3 +45,8 @@ Respond with JSON only:
APPROVED (confidence >= 75): Implementation matches plan.
REVISE: CRITICAL or HIGH issues.
Do NOT REVISE for LOW-only issues.

Output contract (over-cap verdicts are rejected as unusable):
- summary: <= 40 words.
- issues: <= 5, worst first, one line each as `file:line — issue`.
- No pasted file contents or full diffs — point with file:line.
7 changes: 7 additions & 0 deletions skills/dev-review/templates/review-prompt-opus.md
Original file line number Diff line number Diff line change
Expand Up @@ -67,3 +67,10 @@ Respond with ONLY a JSON object:
- APPROVED (confidence >= 75): Implementation matches the plan and works correctly.
- REVISE: CRITICAL or HIGH issues that must be fixed.
- Do NOT REVISE for LOW-only issues.

### Output contract (enforced — over-cap verdicts are rejected as unusable)

- `summary`: <= 40 words. State the verdict rationale, not a recap of the diff.
- `issues`: <= 5, most severe first. Each is ONE line: `file:line — issue`. If you found more, keep only the 5 that matter and fold the rest into `summary`.
- Do NOT paste file contents, full diffs, or long code blocks into any field. Point with `file:line`; the reader has the diff.
- `description`/`suggestion`: one line each. `iteration_notes`: a short paragraph, not a report.
Loading
Loading