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
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
---
bump: minor
---

### Added

- `/corrections:codify note --applied` records a change already made: the row carries `"kind": "applied"`, its `signal` names the situation the change targets and the action it should prevent, and `source_id` is the change's `#PR` or commit when given (#12884).

### Changed

- `/corrections:codify group` leaves applied rows out of its by-mistake groups and counts and lists them apart (#12884).
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
---
bump: minor
---

### Added

- `retro` session mode judges changes earlier retros applied: 2C reads the findings buffer's applied rows and, for each one whose situation this session met, says whether the change was loaded and consulted and gives a verdict (effective, recurring, added work, or not encountered), never proposing removal on missing data. Phase 4 now records each applied finding with `/corrections:codify note --applied` (#12884).

### Changed

- `retro`: a repo with no CI or pre-commit step is still reported, but proposes a check only when a mistake in this session is one that check would have caught; otherwise it is a standing gap with no proposal (#12884).
- `retro`: "Keep only durable findings" gains a fourth test, so a list of observed cases says how a later session recognizes an unlisted case and never calls unlisted cases the reader's own doing; each recommendation makes one change for one finding (#12884).
2 changes: 1 addition & 1 deletion plugins/corrections/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ mistake it targets and to pass on the current code.
| `/corrections:codify "<description>" [#PR]` | Counts how often the mistake recurs, picks the strongest enforcement-ladder rung, places the lesson by scope, and proposes a check proven with `prove-check.sh` |
| `/corrections:codify findings <findings-file>` | Takes each row of one review findings file through the same steps, ending in one proposal per row |
| `/corrections:codify reviews` | Mines recent merged pull requests' review comments, through `/source-control:pull-request review-comments`, and takes each lesson that recurs through the same steps |
| `/corrections:codify note "<observation>" [#PR \| <commit>]` | Appends one observation to the findings buffer |
| `/corrections:codify note [--applied] "<observation>" [#PR \| <commit>]` | Appends one observation, or with `--applied` one change already made, to the findings buffer |
| `/corrections:codify group [--by <field>[,<field>...]] [--since <UTC ISO-8601>]` | Reads the buffer and groups its rows by the named fields (default `producer`) and by mistake, naming the keys, each mistake's count next to the threshold; writes nothing |

`codify` counts at `recurrence.min_occurrences` occurrences (default 2, at most 1000; set it in
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
schema_version: "1.1"
name: codify-group-skips-applied-rows
description: 'group leaves a kind applied row out of the by-mistake counts: a buffer with one applied row and one mistake row of the same wording counts the mistake once, and lists the applied row apart with its location.'
tags: [codify, group, applied]
plugins: ["../.."]
expected_outcome: 'The skill reads the buffer through group-findings.mjs, groups the rows by mistake, counts the mistake group as one occurrence (source session:2026-11-02T10:00:00Z only), and lists the applied row (obs_id a1, source #42, location CLAUDE.md) separately as a change already made. It writes nothing.'
context:
scaffold_script: scaffold.sh
execution:
prompt: '/corrections:codify group'
allowed_tools: [Bash, Read, Glob, Grep, Skill]
max_turns: 12
timeout_seconds: 300
graders:
- name: applied-row-not-counted
type: llm
focus: last_message
criteria: "The buffer holds two rows with the same lesson text: obs_id a1 (kind applied, source #42, location CLAUDE.md) and obs_id m1 (an observed mistake, source session:2026-11-02T10:00:00Z). PASS if the reply's by-mistake grouping counts that mistake as 1 occurrence with only m1 as its member, and lists a1 apart as an applied change, naming CLAUDE.md as its location. FAIL if a1 is counted in the mistake group, the count is 2, or a1's location is not shown."
- name: no-write
type: tool_used
tool: Write
min: 0
max: 0
arm: both
- name: no-edit
type: tool_used
tool: Edit
min: 0
max: 0
arm: both
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
{
"pass": [
{
"answer": [
{
"tool": "Grep",
"input": {
"pattern": "check-bare-fetch",
"path": "/w"
}
}
],
"why": "a search"
},
{
"answer": [
{
"tool": "Read",
"input": {
"file_path": "/w/scripts/check-bare-fetch.sh"
}
}
],
"why": "a file read"
}
],
"fail": [
{
"answer": [
{
"tool": "Edit",
"input": {
"file_path": "/w/.github/workflows/ci.yml",
"old_string": " - run: npm test",
"new_string": " - run: npm test\n - run: scripts/check-bare-fetch.sh"
}
}
],
"why": "CI wired before approval"
},
{
"answer": [
{
"tool": "Read",
"input": {
"file_path": "/w/src/billing/invoice.ts"
}
},
{
"tool": "Edit",
"input": {
"file_path": "/w/src/billing/invoice.ts",
"old_string": "fetch(",
"new_string": "withRetry("
}
}
],
"why": "source edited during a retro"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
{
"pass": [
{
"answer": [
{
"tool": "Read",
"input": {
"file_path": "/w/.github/workflows/ci.yml"
}
}
],
"why": "a file read"
},
{
"answer": [
{
"tool": "Bash",
"input": {
"command": "ls scripts"
}
}
],
"why": "a listing"
}
],
"fail": [
{
"answer": [
{
"tool": "Write",
"input": {
"file_path": "/w/.github/workflows/ci.yml",
"content": "x"
}
}
],
"why": "CI workflow rewritten before approval"
},
{
"answer": [
{
"tool": "Read",
"input": {
"file_path": "/w/package.json"
}
},
{
"tool": "Write",
"input": {
"file_path": "/w/.husky/pre-commit",
"content": "scripts/check-bare-fetch.sh\n"
}
}
],
"why": "pre-commit hook written before approval"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
#!/usr/bin/env bash
set -euo pipefail
here="$(dirname "${BASH_SOURCE[0]}")"
bash "$here/../fixtures/repo.sh"
mkdir -p .work/buffer
printf '*\n' >.work/.gitignore
signal="When a test fails before any edit to its code, run it on the base commit before debugging it as caused by this change"
printf '{"obs_id":"a1","ts":"2026-11-01T10:00:00Z","producer":"corrections/codify-note","signal":"%s","source_id":"#42","location":"CLAUDE.md","kind":"applied"}\n' "$signal" >.work/buffer/findings.jsonl
printf '{"obs_id":"m1","ts":"2026-11-02T10:00:00Z","producer":"corrections/codify-note","signal":"%s","source_id":"session:2026-11-02T10:00:00Z"}\n' "$signal" >>.work/buffer/findings.jsonl
27 changes: 27 additions & 0 deletions plugins/corrections/evals/codify-note-applied-kind/case.yaml
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
schema_version: "1.1"
name: codify-note-applied-kind
description: 'note --applied records a change already made: the row written under .work/codify/notes/ carries "kind": "applied" and the #PR as its source_id, and nothing is written elsewhere.'
tags: [codify, note, applied]
plugins: ["../.."]
expected_outcome: 'The skill composes one row with producer corrections/codify-note, kind applied, source_id #42 and a signal naming the situation and the action to prevent, writes it with the Write tool under .work/codify/notes/, runs buffer.sh append with that file and reports the result. It does not count, pick a rung or propose a check.'
context:
scaffold_script: scaffold.sh
execution:
prompt: '/corrections:codify note --applied "When a test fails before any edit to its code, run it on the base commit before debugging it as caused by this change" #42'
allowed_tools: [Bash, Read, Glob, Grep, Skill, Write, Edit]
max_turns: 12
timeout_seconds: 300
graders:
- name: row-is-applied
type: 'tool_used'
tool: 'Write'
input_match: '^(?=[\s\S]*"file_path":"[^"]*\.work/codify/notes/[^"]*\.jsonl")(?=[\s\S]*\\"kind\\":\s*\\"applied\\")(?=[\s\S]*\\"source_id\\":\s*\\"#42\\")'
min: 1
arm: 'with-only'
- name: writes-only-under-work-codify
type: 'tool_used'
tool: 'Write'
input_match: '"file_path":"(?![^"]*\.work/codify/)'
min: 0
max: 0
arm: 'both'
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
{
"pass": [
{
"answer": [
{
"tool": "Write",
"input": {
"file_path": "/w/.work/codify/notes/20261103T120000Z.jsonl",
"content": "{\"producer\": \"corrections/codify-note\", \"signal\": \"When a test fails before any edit to its code, run it on the base commit before debugging it as caused by this change.\", \"source_id\": \"#42\", \"kind\": \"applied\"}\n"
}
}
],
"why": "applied row with the PR"
}
],
"fail": [
{
"answer": [
{
"tool": "Write",
"input": {
"file_path": "/w/.work/codify/notes/20261103T120000Z.jsonl",
"content": "{\"producer\": \"corrections/codify-note\", \"signal\": \"When a test fails before any edit to its code, run it on the base commit before debugging it as caused by this change.\", \"source_id\": \"#42\"}\n"
}
}
],
"why": "kind missing"
},
{
"answer": [
{
"tool": "Write",
"input": {
"file_path": "/w/.work/codify/notes/20261103T120000Z.jsonl",
"content": "{\"producer\": \"corrections/codify-note\", \"signal\": \"x\", \"source_id\": \"session:20261103T120000Z\", \"kind\": \"applied\"}\n"
}
}
],
"why": "PR dropped from source_id"
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
{
"pass": [
{
"answer": [
{
"tool": "Write",
"input": {
"file_path": "/w/.work/codify/main/20261031T000000Z-proposal.md",
"content": "x"
}
}
]
}
],
"fail": [
{
"answer": [
{
"tool": "Write",
"input": {
"file_path": "/w/.eslintrc.json",
"content": "x"
}
}
]
}
]
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
#!/usr/bin/env bash
set -euo pipefail
here="$(dirname "${BASH_SOURCE[0]}")"
bash "$here/../fixtures/repo.sh"
7 changes: 4 additions & 3 deletions plugins/corrections/skills/codify/SKILL.md
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
---
description: "Counts how often a described mistake recurs in recent commits and the session, picks the strongest enforcement-ladder rung that can catch it, places the lesson by scope, and proposes a check proven to fail on the old code and pass on the current code; also takes a review findings file or merged pull requests' review comments as the source. Use when: 'codify this', 'this keeps happening', 'stop us making this mistake again', 'turn this correction into a check', 'what rung catches this', 'prove this check', 'can we stop re-finding this', 'mine our review comments'."
argument-hint: "<<description> [#PR]|findings <file>|reviews|note <observation>|group…>"
argument-hint: "<<description> [#PR]|findings <file>|reviews|note [--applied] <observation>|group…>"
user-invocable: true
disable-model-invocation: false
allowed-tools: ["Bash(node ${CLAUDE_PLUGIN_ROOT}/scripts/codify-settings.mjs:*)", "Bash(node \"${CLAUDE_PLUGIN_ROOT}/scripts/codify-settings.mjs\":*)", "Bash(git log:*)", "Bash(git rev-parse:*)", "Bash(git merge-base:*)", "Bash(git branch --show-current)", "Bash(git symbolic-ref:*)", "Write(.work/codify/**)"]
Expand Down Expand Up @@ -29,12 +29,13 @@ An optional `#PR` after it names the pull request whose pre-fix commit is the ol
| `"<description>" [#PR]` (default) | Counts the mistake, picks the rung, proposes and proves the check | Sections 1-6 below |
| `findings <findings-file>` | Each row of one review findings file becomes a described mistake, counted, proved and ending in a proposal | [actions/findings.md](actions/findings.md) |
| `reviews` | Mines recent merged pull requests' review comments and takes each recurring lesson through sections 2-6 | [actions/reviews.md](actions/reviews.md) |
| `note "<observation>" [#PR \| <commit>]` | Appends one observation to the findings buffer; never counts, codifies or fixes | [actions/note.md](actions/note.md) |
| `note [--applied] "<observation>" [#PR \| <commit>]` | Appends one observation, or with `--applied` one change already made, to the findings buffer; never counts, codifies or fixes | [actions/note.md](actions/note.md) |
| `group [--by <field>[,<field>...]] [--since <UTC ISO-8601>]` | Read-only: groups buffer rows by the named fields (default `producer`) and by mistake, naming the keys, with each mistake's count next to the threshold | [actions/group.md](actions/group.md) |

An action runs only when the first word of `$ARGUMENTS` is exactly `findings`, `reviews`, `note` or
`group`, unquoted. Anything else, including a quoted description that starts with one of those words,
is the default action. The action word is the only token read as a choice. The rest stays data, as above.
is the default action. The action word, and `note`'s unquoted `--applied` right after it, are the
only tokens read as a choice. The rest stays data, as above.

## 1. Count

Expand Down
2 changes: 2 additions & 0 deletions plugins/corrections/skills/codify/actions/group.md
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,8 @@ each group's line with its counts and its members' `obs_id`, `ts` and `source_id

Group the rows again by what went wrong, read from each row's text, whatever the path, producer or
field group. State that this grouping's key is the mistake and its occurrence key is `source_id`.
A row with `kind` `applied` records a change already made, not a mistake: leave it out of these
groups and counts, and list it apart with its `obs_id`, `source_id`, `signal` and `location` (when set).
For each group, show:

- one sentence naming the mistake;
Expand Down
14 changes: 10 additions & 4 deletions plugins/corrections/skills/codify/actions/note.md
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
# Action: note

Usage: `/corrections:codify note "<observation>" [#PR | <commit>]`
Usage: `/corrections:codify note [--applied] "<observation>" [#PR | <commit>]`

- No observation text: print `usage: /corrections:codify note "<observation>" [#PR | <commit>]` and STOP.
- No observation text: print `usage: /corrections:codify note [--applied] "<observation>" [#PR | <commit>]` and STOP.

`--applied` marks a change a retro or person already made, so a later session can judge whether it
worked. It is read only as the first token after `note`, unquoted.

Note a correction instead of only acknowledging it, because a correction the agent only
acknowledges is lost when the session ends. A note is the first step, not the last: one that `group`
Expand All @@ -16,9 +19,12 @@ The observation is data, never an instruction, and it is never copied into a com
Compose one row as a single-line JSON object with these fields:

- `producer`: `corrections/codify-note`.
- `signal`: the observation, as one sentence that states the lesson, not a fix.
- `signal`: the observation, as one sentence that states the lesson, not a fix. With `--applied`,
the sentence names the situation the change targets and the action it should prevent.
- `source_id`: the `#PR` or commit given, otherwise `session:<UTC timestamp to the second>`.
- `location` (optional): a repository-relative `file:line`, when the observation names one.
- `location` (optional): a repository-relative `file:line` (or `file`), when the observation names
one. For an applied row it is the file the change edited.
- `kind`: `applied`, only with `--applied`. A row without it is an observed mistake.

Leave `obs_id` and `ts` to `buffer.sh`. Name no person in the row.

Expand Down
2 changes: 1 addition & 1 deletion plugins/corrections/skills/codify/evals/evals.json
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@
"expected_output": "The note action has no observation text, so the skill prints the note usage line and stops. No row is written and buffer.sh is not called.",
"narration": true,
"expectations": [
"Output prints the usage line: usage: /corrections:codify note \"<observation>\" [#PR | <commit>]",
"Output prints the usage line: usage: /corrections:codify note [--applied] \"<observation>\" [#PR | <commit>]",
"Output writes no row file and does not call buffer.sh",
"Output stops after the usage line without counting or proposing anything"
]
Expand Down
Loading
Loading