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
2 changes: 2 additions & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -21,3 +21,5 @@ Thumbs.db
# Temporary files
*.tmp
*.temp

.env
46 changes: 44 additions & 2 deletions evals/promptfooconfig.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -4,9 +4,9 @@ prompts:
- file://prompt-loader.js

providers:
- id: anthropic:messages:claude-sonnet-4-20250514
- id: anthropic:messages:claude-sonnet-4-6
config:
max_tokens: 2048
max_tokens: 4096

tests:
# ==================================================================
Expand Down Expand Up @@ -145,6 +145,48 @@ tests:
thorough model, max-passes 7). It must NOT apply any fixes since
auto-fix is explicitly false.

# ==================================================================
# again — global sequential numbering + Suggested Fix column
# ==================================================================
- description: "summary tables include Suggested Fix column and global ID"
vars:
prompt_file: src/skills/again/SKILL.md
assert:
- type: icontains
value: "Suggested Fix"
- type: llm-rubric
value: >
The response must describe a final summary that includes three tables
(Must Fix, Should Fix, Suggestions), each containing a "Suggested Fix"
column and a global sequential ID column (often shown as "#"). It must
state that IDs are assigned once across all issues and DO NOT restart
between the three tables (e.g., must_fix uses 1..k, should_fix
continues at k+1, etc.). It must also state that the user can refer
to fixes by these IDs (e.g., "apply fix 1, 3, 5").

- description: "aggregate.md preserves full Suggested Fix text; conversation may truncate"
vars:
prompt_file: src/skills/again/SKILL.md
assert:
- type: llm-rubric
value: >
The response must indicate that the full untruncated Suggested Fix
text is preserved in aggregate.md (and aggregate.json), while the
conversation-summary table is allowed to collapse very long Suggested
Fix text to a single line. It must NOT say the full text is dropped
or only kept in conversation.

- description: "must_fix table includes a Fixed column; should_fix and suggestions do not"
vars:
prompt_file: src/skills/again/SKILL.md
assert:
- type: llm-rubric
value: >
The response must describe the Must Fix table as having a "Fixed"
column (Yes/No), since auto-fix applies only to must_fix issues. The
Should Fix and Suggestions tables must NOT include a Fixed column —
they only have ID, Issue, File, Confidence, and Suggested Fix.

# ==================================================================
# tidy — all flag
# ==================================================================
Expand Down
76 changes: 76 additions & 0 deletions scripts/test.sh
Original file line number Diff line number Diff line change
Expand Up @@ -177,6 +177,78 @@ test_argument_handling() {
done
}

test_again_numbering_format() {
# Verify the again skill documents the global-ID + Suggested Fix format
# added on feat/numbered-suggested-fixes. These checks catch regressions
# if a future edit removes the numbering scheme or the Suggested Fix
# column from the summary template.

local skill="$PROJECT_ROOT/src/skills/again/SKILL.md"
local relpath="src/skills/again/SKILL.md"

if [[ ! -f "$skill" ]]; then
fail "$relpath not found"
return
fi

local body
body=$(cat "$skill")

# Phase 2 must instruct global sequential numbering.
if echo "$body" | grep -qE "global sequential ID"; then
pass "$relpath: documents global sequential ID assignment"
else
fail "$relpath: missing 'global sequential ID' instruction in Phase 2"
fi

# Phase 2 must explain that IDs do not restart between severity tables.
if echo "$body" | grep -qE "numbering does not restart"; then
pass "$relpath: documents that numbering does not restart between tables"
else
fail "$relpath: missing 'numbering does not restart' clarification"
fi

# Phase 2 must store the assigned number on each issue as 'id'.
if echo "$body" | grep -qE "\`id\` field"; then
pass "$relpath: documents 'id' field on aggregated issues"
else
fail "$relpath: missing 'id' field documentation in Phase 2"
fi

# Each summary table must include a 'Suggested Fix' column.
local sf_count
sf_count=$(grep -c "Suggested Fix" "$skill" || true)
if [[ "$sf_count" -ge 3 ]]; then
pass "$relpath: 'Suggested Fix' column present in all 3 summary tables"
else
fail "$relpath: 'Suggested Fix' column missing (found $sf_count occurrences, need >= 3)"
fi

# Each summary table must include a '#' column for the global ID.
# The header row is '| # | Issue | ...'; require at least 3 such headers.
local id_col_count
id_col_count=$(grep -cE '^\| #[[:space:]]*\| Issue' "$skill" || true)
if [[ "$id_col_count" -ge 3 ]]; then
pass "$relpath: '#' ID column present in all 3 summary tables"
else
fail "$relpath: '#' ID column missing (found $id_col_count, need >= 3)"
fi

# Truncation rule: conversation may collapse, but full text lives in aggregate.md.
if echo "$body" | grep -qE "full text always lives in"; then
pass "$relpath: documents that full Suggested Fix text lives in aggregate.md"
else
fail "$relpath: missing 'full text always lives in aggregate.md' rule"
fi

# Pipe/newline escaping rule for the Suggested Fix cell.
if echo "$body" | grep -qE "escape pipe characters"; then
pass "$relpath: documents pipe-escaping rule for Suggested Fix cell"
else
fail "$relpath: missing pipe-escaping rule for Suggested Fix cell"
fi
}

test_cross_references() {
local pjson="$PROJECT_ROOT/src/dot-claude-plugin/plugin.json"

Expand Down Expand Up @@ -366,6 +438,10 @@ echo "--- argument handling ---"
test_argument_handling
echo ""

echo "--- again numbering + Suggested Fix format ---"
test_again_numbering_format
echo ""

echo "--- cross-references ---"
test_cross_references
echo ""
Expand Down
28 changes: 18 additions & 10 deletions src/skills/again/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -88,42 +88,50 @@ After completing the configured number of passes, if `must_fix` issues remain an
1. **Deduplicate** on (file, title). Same issue across passes = higher confidence.
2. **Score**: Confidence = (passes finding issue) / (total passes) x 100%.
3. **Group** by severity, sort by confidence within groups.
4. **Number**: Assign a single global sequential ID (`1..N`) across all issues. Order: every `must_fix` first (in confidence-sorted order), then every `should_fix`, then every `suggestion`. **Numbering is global and does NOT restart between the three severity tables** - if `must_fix` ends at ID k, `should_fix` starts at k+1, and `suggestions` continues from there. The same ID identifies the issue in the conversation tables, in `aggregate.md`, and in `aggregate.json` (store as the `id` field on each issue).
5. **Reference by ID**: Because IDs are unique and global, the user can refer to specific fixes by number (e.g. "apply fix 1, 3, 5") to selectively apply or skip individual issues across any of the three tables. Tell the user this in the final summary.

## Phase 3: Save and Report

Save to `.lookagain/<run-id>/`:
- `pass-N.json` after each pass
- `aggregate.json` and `aggregate.md` after aggregation

Present the final summary to the user in this format:
`aggregate.md` uses the same tables shown below (same columns, same global IDs). Keep `suggested_fix` text intact in the markdown file - do not truncate. To preserve the table format, escape pipe characters as `\|` and replace embedded newlines with `<br>` (or single spaces) inside the `Suggested Fix` cell.

Present the final summary to the user. The summary contains **three separate tables**, one for each severity (Must Fix, Should Fix, Suggestions), each with a `Suggested Fix` column and the global `#` ID column. Use this exact format:

```
## Iterative Review Complete

**Passes completed**: N
**Unique issues found**: X

Reference fixes by number (e.g. "apply fix 1, 3, 5").

### Must Fix (N issues)

| Issue | File | Confidence | Fixed |
| ----- | ---- | ---------- | ----- |
| ... | ... | ...% | Yes/No |
| # | Issue | File | Confidence | Suggested Fix | Fixed |
| --- | ----- | ---- | ---------- | ------------- | ----- |
| 1 | ... | ... | ...% | ... | Yes/No |

### Should Fix (N issues)

| Issue | File | Confidence |
| ----- | ---- | ---------- |
| ... | ... | ...% |
| # | Issue | File | Confidence | Suggested Fix |
| --- | ----- | ---- | ---------- | ------------- |
| 4 | ... | ... | ...% | ... |

### Suggestions (N issues)

| Issue | File | Confidence |
| ----- | ---- | ---------- |
| ... | ... | ...% |
| # | Issue | File | Confidence | Suggested Fix |
| --- | ----- | ---- | ---------- | ------------- |
| 7 | ... | ... | ...% | ... |

Full report saved to `.lookagain/<run-id>/aggregate.md`
```

The `#` column carries the global ID assigned in Phase 2 - numbering does not restart between tables. In conversation output, you may collapse very long `Suggested Fix` text to a single line; the full text always lives in `aggregate.md` and `aggregate.json`.

Include the count of previous runs (glob `.lookagain/????-??-??T??-??-??/`, subtract 1). Mention `/look:tidy` if previous runs exist.

## Rules
Expand Down
Loading