diff --git a/.gitignore b/.gitignore index 18b3190..61b4ab5 100644 --- a/.gitignore +++ b/.gitignore @@ -21,3 +21,5 @@ Thumbs.db # Temporary files *.tmp *.temp + +.env \ No newline at end of file diff --git a/evals/promptfooconfig.yaml b/evals/promptfooconfig.yaml index af195f7..05952a4 100644 --- a/evals/promptfooconfig.yaml +++ b/evals/promptfooconfig.yaml @@ -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: # ================================================================== @@ -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 # ================================================================== diff --git a/scripts/test.sh b/scripts/test.sh index 6eadb97..52e1c86 100755 --- a/scripts/test.sh +++ b/scripts/test.sh @@ -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" @@ -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 "" diff --git a/src/skills/again/SKILL.md b/src/skills/again/SKILL.md index 83a816e..c17e7b2 100644 --- a/src/skills/again/SKILL.md +++ b/src/skills/again/SKILL.md @@ -88,6 +88,8 @@ 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 @@ -95,7 +97,9 @@ Save to `.lookagain//`: - `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 `
` (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 @@ -103,27 +107,31 @@ Present the final summary to the user in this format: **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//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