@olegkoval/agent-skills 1.59.3 → 1.60.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (29) hide show
  1. package/.github/prompts/self-critique.prompt.md +167 -43
  2. package/README.md +1 -1
  3. package/adapters/claude/olko-reflection/skills/self-critique/SKILL.md +167 -43
  4. package/adapters/claude/olko-reflection/skills/self-critique/scripts/claim-ledger.mjs +159 -0
  5. package/adapters/claude/olko-reflection/skills/self-critique/scripts/rule-store.mjs +193 -0
  6. package/adapters/cursor/olko-reflection/skills/self-critique/SKILL.md +167 -43
  7. package/adapters/cursor/olko-reflection/skills/self-critique/scripts/claim-ledger.mjs +159 -0
  8. package/adapters/cursor/olko-reflection/skills/self-critique/scripts/rule-store.mjs +193 -0
  9. package/adapters/grok/olko-reflection/skills/self-critique/SKILL.md +167 -43
  10. package/adapters/grok/olko-reflection/skills/self-critique/scripts/claim-ledger.mjs +159 -0
  11. package/adapters/grok/olko-reflection/skills/self-critique/scripts/rule-store.mjs +193 -0
  12. package/catalog/skills.json +1 -1
  13. package/docs/superpowers/plans/2026-10-27-self-critique-v2.md +306 -0
  14. package/docs/superpowers/specs/2026-10-27-self-critique-v2-spec.md +104 -0
  15. package/package.json +1 -1
  16. package/plugins/olko-apple-kit/.claude-plugin/plugin.json +1 -1
  17. package/plugins/olko-creative/.claude-plugin/plugin.json +1 -1
  18. package/plugins/olko-garmin-kit/.claude-plugin/plugin.json +1 -1
  19. package/plugins/olko-git-tools/.claude-plugin/plugin.json +1 -1
  20. package/plugins/olko-github-pr/.claude-plugin/plugin.json +1 -1
  21. package/plugins/olko-obsidian/.claude-plugin/plugin.json +1 -1
  22. package/plugins/olko-product/.claude-plugin/plugin.json +1 -1
  23. package/plugins/olko-reflection/.claude-plugin/plugin.json +1 -1
  24. package/plugins/olko-reflection/skills/self-critique/SKILL.md +167 -43
  25. package/plugins/olko-reflection/skills/self-critique/scripts/claim-ledger.mjs +159 -0
  26. package/plugins/olko-reflection/skills/self-critique/scripts/rule-store.mjs +193 -0
  27. package/plugins/olko-release/.claude-plugin/plugin.json +1 -1
  28. package/plugins/olko-skill-meta/.claude-plugin/plugin.json +1 -1
  29. package/plugins/olko-web-ops/.claude-plugin/plugin.json +1 -1
@@ -0,0 +1,306 @@
1
+ # Self-critique v2 Implementation Plan
2
+
3
+ > **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
4
+
5
+ **Goal:** Make the self-critique score a function of a labelled claim ledger instead of a critic's vibe, blind the critic in round one, re-verify what the critic infers, restate the corrected answer, and give every durable rule a fire counter and a prune path, with a repeatable end-to-end test that proves a correct answer scores 90+ on round one and a seeded-wrong answer does not.
6
+
7
+ **Architecture:** Two small Node scripts carry the deterministic parts (`claim-ledger.mjs`: score, anchors, ratchet, summary table; `rule-store.mjs`: list, checklist, fire, prune, evidence cap over memory files). `SKILL.md` is rewritten to call them at fixed moments. A gated end-to-end test runs the skill headless with `claude -p` on two fixture answers against a fixture repo and asserts the scores and the refuted claims.
8
+
9
+ **Tech Stack:** Node 22 (ESM, `node:test`, no dependencies), POSIX sh test wrappers, Claude Code CLI 2.1.x for the gated e2e.
10
+
11
+ **Spec:** `docs/superpowers/specs/2026-10-27-self-critique-v2-spec.md` (sections "Weak parts", "Killer feature", "Changes recommended").
12
+
13
+ ## Global Constraints
14
+
15
+ - Canonical content lives only at `plugins/olko-reflection/skills/self-critique/`; run `./scripts/build-adapters.sh` after any skill change and commit the generated tree (repo `CLAUDE.md`).
16
+ - No em dashes anywhere in skill content.
17
+ - `catalog/skills.json` is the only metadata source; the skill `description` there must match the SKILL.md frontmatter description.
18
+ - Node `>=22`, no new npm dependencies.
19
+ - Commit messages are conventional and never mention an assistant.
20
+ - Scripts must exit 0 and print nothing harmful on malformed input (same stance as `critique-nudge.mjs`): a malformed ledger prints a JSON error to stderr and exits 2, never throws a stack trace.
21
+ - The Stop hook is out of scope for this plan (the spec lists it; the user's pasted scope does not).
22
+
23
+ ## Review Focus
24
+
25
+ 1. A ledger where every claim is `verified` and nothing is `missed` must score exactly 100; a critic that returns no findings must not be able to lower the score (Task 1 test `scores 100 for a clean ledger`).
26
+ 2. A claim labelled `checked_by: inferred` that the author accepted without re-verification is penalised, so a hallucinated critic finding costs the critic, not the author (Task 1 test `penalises accepted inferred findings`).
27
+ 3. A memory file whose frontmatter has no `metadata.fires` key must still list, fire and prune without crashing (Task 3 test `fire adds the keys when missing`).
28
+ 4. The e2e correct-answer fixture must reach 90+ on round one; if it does not, the scoring or the blind prompt is still inflating, and the test fails loudly rather than passing on round three (Task 6 assertion A).
29
+ 5. A rule with more than three dated evidence lines is flagged, never silently truncated (Task 3 test `evidence-check flags a fourth incident`).
30
+
31
+ ---
32
+
33
+ ### Task 1: `claim-ledger.mjs` score, anchors, ratchet
34
+
35
+ **Files:**
36
+ - Create: `plugins/olko-reflection/skills/self-critique/scripts/claim-ledger.mjs`
37
+ - Create: `tests/self-critique-ledger.test.mjs`
38
+ - Create: `tests/test-self-critique-ledger.sh` (one line: `node --test tests/self-critique-ledger.test.mjs`, same shape as `tests/test-lekker-workflow.sh`)
39
+
40
+ **Interfaces:**
41
+ - Consumes: nothing.
42
+ - Produces: ESM exports `scoreLedger(ledger) -> { score: number, breakdown: Record<string, number> }`, `ratchet(prev, next) -> { winner: 'prev'|'next', score: number }`, `anchors() -> Array<{ label: string, ledger: Ledger, score: number }>`, `summaryMarkdown(ledger) -> string`. CLI: `node claim-ledger.mjs score <ledger.json>`, `ratchet <a.json> <b.json>`, `anchors`, `summary <ledger.json>`.
43
+
44
+ Ledger shape (JSON, validated by `validateLedger`, which returns a list of error strings):
45
+
46
+ ```json
47
+ {
48
+ "version": 1,
49
+ "claims": [
50
+ { "id": "c1", "text": "retries default to 3", "load_bearing": true,
51
+ "origin": "both", "status": "verified", "checked_by": "read",
52
+ "receipt": "config.json:2 \"retries\": 3" }
53
+ ],
54
+ "missed": [ { "id": "m1", "text": "handler swallows errors", "load_bearing": true } ]
55
+ }
56
+ ```
57
+
58
+ Enumerations: `origin` in `author | critic | both`; `status` in `verified | refuted | unverifiable | inferred`; `checked_by` in `command | read | inferred`.
59
+
60
+ Penalty table (the whole scoring rule; copy verbatim into the script as one exported `PENALTIES` object):
61
+
62
+ | key | points |
63
+ | --- | --- |
64
+ | `refuted_load_bearing` | 25 |
65
+ | `refuted` | 10 |
66
+ | `unverifiable_load_bearing` | 10 |
67
+ | `unverifiable` | 4 |
68
+ | `inferred_accepted` (status `inferred`, any origin) | 5 |
69
+ | `missed_load_bearing` | 15 |
70
+ | `missed` | 5 |
71
+ | `only_critic_found` (origin `critic`, status `verified`: author relied on it without listing it) | 5 |
72
+
73
+ `score = max(0, 100 - sum)`. Ties in `ratchet` go to `next`.
74
+
75
+ - [ ] **Step 1: Write the failing tests**
76
+
77
+ ```js
78
+ test('scores 100 for a clean ledger', () => {
79
+ assert.equal(scoreLedger({ version: 1, claims: [verified('c1', true)], missed: [] }).score, 100)
80
+ })
81
+ test('applies the penalty table', () => {
82
+ const l = { version: 1, claims: [refuted('c1', true), unverifiable('c2', false), inferred('c3', false)], missed: [{ id: 'm1', text: 'x', load_bearing: true }] }
83
+ assert.deepEqual(scoreLedger(l), { score: 51, breakdown: { refuted_load_bearing: 25, unverifiable: 4, inferred_accepted: 5, missed_load_bearing: 15 } })
84
+ })
85
+ test('penalises accepted inferred findings', () => {
86
+ assert.equal(scoreLedger({ version: 1, claims: [inferred('c1', false)], missed: [] }).score, 95)
87
+ })
88
+ test('floors at 0', () => { /* five refuted load-bearing claims */ assert.equal(score, 0) })
89
+ test('ratchet keeps the higher score and prefers next on a tie', () => {
90
+ assert.deepEqual(ratchet(clean, bad), { winner: 'prev', score: 100 })
91
+ assert.deepEqual(ratchet(clean, clean), { winner: 'next', score: 100 })
92
+ })
93
+ test('anchors are produced by the same function', () => {
94
+ for (const a of anchors()) assert.equal(scoreLedger(a.ledger).score, a.score)
95
+ assert.deepEqual(anchors().map((a) => a.score), [95, 72, 40])
96
+ })
97
+ test('validateLedger names every bad field', () => {
98
+ assert.deepEqual(validateLedger({ version: 1, claims: [{ id: 'c1', status: 'maybe' }], missed: [] }),
99
+ ['claims[0].text missing', 'claims[0].load_bearing missing', 'claims[0].origin missing', 'claims[0].status invalid: maybe', 'claims[0].checked_by missing'])
100
+ })
101
+ test('summaryMarkdown renders one row per claim with a status word', () => {
102
+ assert.match(summaryMarkdown(l), /\| c1 \| .* \| refuted \|/)
103
+ })
104
+ test('cli exits 2 with a JSON error on malformed input', () => {
105
+ const r = spawnSync('node', [script, 'score', badPath]); assert.equal(r.status, 2); JSON.parse(r.stderr)
106
+ })
107
+ ```
108
+
109
+ - [ ] **Step 2: Run to verify they fail**
110
+
111
+ Run: `sh tests/test-self-critique-ledger.sh`
112
+ Expected: FAIL, module not found.
113
+
114
+ - [ ] **Step 3: Implement the module**
115
+
116
+ Signatures: `validateLedger(ledger: unknown): string[]`, `scoreLedger(ledger: Ledger): { score, breakdown }` (breakdown omits zero keys), `ratchet(prev: Ledger, next: Ledger)`, `anchors()`, `summaryMarkdown(ledger)` producing a table with columns `id | claim | origin | status | checked_by | receipt`. The CLI branch runs only when `import.meta.url` matches `process.argv[1]`. The three anchors are hand-built ledgers: a 95 (one `unverifiable`), a 72 (one `refuted_load_bearing` and three `unverifiable` non-load-bearing... pick any combination the table makes exact), a 40.
117
+
118
+ - [ ] **Step 4: Run to verify they pass**
119
+
120
+ Run: `sh tests/test-self-critique-ledger.sh`
121
+ Expected: PASS, 9 tests.
122
+
123
+ - [ ] **Step 5: Commit**
124
+
125
+ ```bash
126
+ git add plugins/olko-reflection/skills/self-critique/scripts/claim-ledger.mjs tests/self-critique-ledger.test.mjs tests/test-self-critique-ledger.sh
127
+ git commit -m "feat(self-critique): score from a labelled claim ledger with anchors and a ratchet"
128
+ ```
129
+
130
+ ### Task 2: Blind critic prompt and claim diff in SKILL.md
131
+
132
+ **Files:**
133
+ - Modify: `plugins/olko-reflection/skills/self-critique/SKILL.md` (sections "Procedure" steps 1 to 3, "Critic prompt template", "Notes")
134
+ - Modify: `catalog/skills.json` (self-critique `description`)
135
+
136
+ **Interfaces:**
137
+ - Consumes: Task 1 CLI commands and the ledger JSON shape.
138
+ - Produces: the round-one critic output contract used by Task 6's e2e: a fenced JSON block with `claims` (the critic's own list, each with `text`, `load_bearing`, `status`, `checked_by`, `receipt`), `missed`, `where_wrong`, `shapes`, `verdict`. No `score` field: the score is computed.
139
+
140
+ - [ ] **Step 1: Rewrite Step 1 (Snapshot)**: keep the receipt rules; add that the author's claim list is written to `.self-critique/author-claims.json` (array of `{ id, text, load_bearing, receipt }`) and is NOT shown to the critic in round one.
141
+
142
+ - [ ] **Step 2: Rewrite Step 2 (Spawn the critic)**: round one input is the original task text and the answer verbatim only. The critic extracts claims itself. Replace `{{CLAIMS_AND_SOURCES}}` in the template with `{{TASK}}`. Add to the "Do this" list: "Label every finding with `checked_by`: `command` (you ran it and quote the output), `read` (you opened the file or record and quote it), `inferred` (reasoning only)." Remove `score` from the critic's output list and add `claims`.
143
+
144
+ - [ ] **Step 3: Add Step 2b (Merge into the ledger)**: the author matches critic claims to author claims by meaning and sets `origin` (`both`, `author`, `critic`); every critic finding with `checked_by: inferred` is re-verified by the author before it may become `refuted`; if the author cannot verify it, it stays `inferred`. Save `.self-critique/ledger-v1.json`, run `node <skill>/scripts/claim-ledger.mjs score` and `summary`. State that claims found by only one side are reported under their own heading, "Asymmetric claims".
145
+
146
+ - [ ] **Step 4: Rewrite Step 3 (Decide and loop)**: stop at `score >= 90` or no actionable fixes; max 3 rounds; after a revision, run `ratchet` on the previous and new ledger and keep the winner; round two and later hand the critic the author's receipts for still-open claims (it is no longer blind). Add a cost line: the critic runs on a mid-tier model (Sonnet class) by default; escalate one tier only when a mid-tier round found nothing and the answer is load-bearing. Verify load-bearing claims first when the budget is tight.
147
+
148
+ - [ ] **Step 5: Paste the anchors**: add a "Score anchors" subsection containing the output of `node scripts/claim-ledger.mjs anchors` rendered as three short ledgers with their scores, and the penalty table from Task 1.
149
+
150
+ - [ ] **Step 6: Update the catalog description** to: "Adversarially critique your own last answer: a blind critic agent re-derives and verifies every claim against live sources, the score is computed from a labelled claim ledger, you revise under a ratchet, restate the corrected answer, and turn each caught error into a durable rule with a fire counter." Copy the same string into the SKILL.md frontmatter `description`.
151
+
152
+ - [ ] **Step 7: Rebuild and validate**
153
+
154
+ Run: `./scripts/build-adapters.sh && npm run validate && git diff --stat`
155
+ Expected: validators pass; generated adapters for self-critique changed.
156
+
157
+ - [ ] **Step 8: Commit**
158
+
159
+ ```bash
160
+ git add -A plugins/olko-reflection/skills/self-critique catalog/skills.json adapters .claude-plugin .cursor-plugin .grok-plugin .github/prompts .github/copilot-instructions.md
161
+ git commit -m "feat(self-critique): blind round-one critic, computed score, checked_by labels"
162
+ ```
163
+
164
+ ### Task 3: `rule-store.mjs` list, checklist, fire, prune, evidence cap
165
+
166
+ **Files:**
167
+ - Create: `plugins/olko-reflection/skills/self-critique/scripts/rule-store.mjs`
168
+ - Create: `tests/self-critique-rule-store.test.mjs`
169
+ - Create: `tests/test-self-critique-rule-store.sh`
170
+
171
+ **Interfaces:**
172
+ - Consumes: a directory of Claude Code memory files: Markdown with a YAML frontmatter block holding `name`, `description`, and a `metadata:` map indented by two spaces (observed format: `metadata:\n type: feedback\n modified: ...`).
173
+ - Produces: exports `parseRule(text) -> { name, description, metadata: Record<string,string>, body }`, `serializeRule(rule) -> string`, `listRules(dir) -> Rule[]`, `checklistMarkdown(dir) -> string`, `fire(dir, name, isoDate) -> Rule`, `prune(dir, { idleDays, today }) -> Array<{ name, reason: 'never-fired' | 'idle' }>`, `evidenceCheck(dir, { max: 3 }) -> Array<{ name, count }>`. CLI mirrors them: `list --dir`, `checklist --dir`, `fire <name> --dir --date`, `prune --dir --idle-days 60 --date`, `evidence-check --dir`.
174
+
175
+ An evidence line is a body line matching `/^\s*-\s.*\b20\d\d-\d\d-\d\d\b/`.
176
+
177
+ - [ ] **Step 1: Write the failing tests** (each builds a tmp dir with two fixture files, one with `metadata.fires: 2` and `metadata.last_fired: 2026-09-01`, one with no such keys)
178
+
179
+ ```js
180
+ test('listRules reads name, description, fires and last_fired', ...) // fires defaults to 0, last_fired to null
181
+ test('checklistMarkdown emits one bullet per rule: "- [name]: description"', ...)
182
+ test('fire adds the keys when missing', () => { const r = fire(dir, 'rule-b', '2026-10-27'); assert.equal(r.metadata.fires, '1'); assert.equal(r.metadata.last_fired, '2026-10-27'); assert.match(readFileSync(fileB,'utf8'), /^ fires: 1$/m) })
183
+ test('fire increments and preserves every other frontmatter line and the body byte for byte', ...)
184
+ test('prune lists never-fired and idle rules only', () => { assert.deepEqual(prune(dir, { idleDays: 30, today: '2026-10-27' }), [{ name: 'rule-a', reason: 'idle' }, { name: 'rule-b', reason: 'never-fired' }]) })
185
+ test('evidence-check flags a fourth incident', () => { /* body with 4 dated bullets */ assert.deepEqual(evidenceCheck(dir, { max: 3 }), [{ name: 'rule-c', count: 4 }]) })
186
+ test('a file without frontmatter is skipped, not thrown', ...)
187
+ ```
188
+
189
+ - [ ] **Step 2: Run to verify they fail**
190
+
191
+ Run: `sh tests/test-self-critique-rule-store.sh`
192
+ Expected: FAIL, module not found.
193
+
194
+ - [ ] **Step 3: Implement the module.** Frontmatter parsing is line based (no YAML library): top-level `key: value` and `metadata:` followed by two-space-indented `key: value` lines. `serializeRule` must round-trip `parseRule` output unchanged when nothing was modified (the byte-for-byte test pins this).
195
+
196
+ - [ ] **Step 4: Run to verify they pass**
197
+
198
+ Run: `sh tests/test-self-critique-rule-store.sh`
199
+ Expected: PASS, 7 tests.
200
+
201
+ - [ ] **Step 5: Commit**
202
+
203
+ ```bash
204
+ git add plugins/olko-reflection/skills/self-critique/scripts/rule-store.mjs tests/self-critique-rule-store.test.mjs tests/test-self-critique-rule-store.sh
205
+ git commit -m "feat(self-critique): rule store with fire counter, prune and evidence cap"
206
+ ```
207
+
208
+ ### Task 4: Step 5 rewrite: rules that prove their worth
209
+
210
+ **Files:**
211
+ - Modify: `plugins/olko-reflection/skills/self-critique/SKILL.md` (section "Step 5" and "Notes")
212
+
213
+ **Interfaces:**
214
+ - Consumes: Task 3 CLI.
215
+
216
+ - [ ] **Step 1: Add a replay pass to Step 1 (Snapshot)**: before spawning the critic, run `node <skill>/scripts/rule-store.mjs checklist --dir <memory dir>` and check the answer against every rule. Each hit is tagged `(recurring)` in the report and recorded with `fire <name> --date <today>`. State the memory dir resolution: Claude Code auto-memory dir for the current project, else the repo's `.claude/memory`, else skip with a one-line note.
217
+
218
+ - [ ] **Step 2: Replace "sharpen the existing entry"** with: add one dated evidence line to the existing rule, maximum three per rule; a fourth incident means the rule named the incident, not the shape: rewrite the rule text, keep the three most recent evidence lines. Run `evidence-check` before writing and report any flagged rule.
219
+
220
+ - [ ] **Step 3: Add a prune pass**: at the end of Step 5 run `prune --idle-days 60`; list candidates in the report under "Rules proposed for deletion"; never delete without the user's reply.
221
+
222
+ - [ ] **Step 4: Rewrite Step 4 (Report)**: the report opens with the full corrected answer (`v_final`, restated in full, not a diff), then the claim ledger table from `summary`, then Where you were wrong, Asymmetric claims, Recurring rules fired, Iterations, Score trajectory, Unverifiable, Declined fixes, Rules written, Rules proposed for deletion.
223
+
224
+ - [ ] **Step 5: Rebuild, validate, commit**
225
+
226
+ Run: `./scripts/build-adapters.sh && npm run validate`
227
+
228
+ ```bash
229
+ git add -A plugins/olko-reflection/skills/self-critique adapters .claude-plugin .cursor-plugin .grok-plugin .github/prompts .github/copilot-instructions.md
230
+ git commit -m "feat(self-critique): replay stored rules, fire counters, prune pass, restate the corrected answer"
231
+ ```
232
+
233
+ ### Task 5: Fixtures for the end-to-end test
234
+
235
+ **Files:**
236
+ - Create: `tests/fixtures/self-critique/repo/config.json` with exactly `{ "retries": 3, "timeout_ms": 5000 }`
237
+ - Create: `tests/fixtures/self-critique/repo/handler.js`: reads `config.json`, loops `retries` times calling an injected `fn`, wraps it in `try { } catch { }` with an empty catch (the swallowed error is the load-bearing claim a good critic must list under `missed`).
238
+ - Create: `tests/fixtures/self-critique/task.md`: "Explain how handler.js retries and times out, and name any error-handling risk."
239
+ - Create: `tests/fixtures/self-critique/answer-correct.md`: four claims, all true (retries 3, timeout 5000 ms, loop stops on first success, errors are swallowed by an empty catch), each with a `file:line` receipt.
240
+ - Create: `tests/fixtures/self-critique/answer-seeded.md`: claims retries 5 (wrong), timeout 30 s (wrong), "the production dashboard shows zero errors for this handler" (unverifiable), omits the swallowed-error risk (missed, load-bearing).
241
+ - Create: `tests/fixtures/self-critique/expected.json`: `{ "correct": { "min_round1_score": 90 }, "seeded": { "max_round1_score": 70, "must_refute": ["retries", "timeout"], "must_miss": ["swallow"] } }`
242
+
243
+ - [ ] **Step 1: Write the fixtures as specified.**
244
+ - [ ] **Step 2: Verify `node tests/fixtures/self-critique/repo/handler.js` runs and exits 0.**
245
+ - [ ] **Step 3: Commit**
246
+
247
+ ```bash
248
+ git add tests/fixtures/self-critique
249
+ git commit -m "test(self-critique): fixture repo with a correct and a seeded-wrong answer"
250
+ ```
251
+
252
+ ### Task 6: Gated end-to-end test with `claude -p`
253
+
254
+ **Files:**
255
+ - Create: `tests/test-self-critique-e2e.sh`
256
+ - Create: `tests/self-critique-e2e.mjs` (the runner; the sh wrapper only gates and calls it)
257
+
258
+ **Interfaces:**
259
+ - Consumes: Task 2's critic output contract, Task 1's `score` CLI, Task 5 fixtures.
260
+ - Produces: a receipt file `tests/.artifacts/self-critique-e2e-<date>.json` with both ledgers and scores.
261
+
262
+ - [ ] **Step 1: Write the sh wrapper**: if `SELF_CRITIQUE_E2E` is not `1`, print `skip: set SELF_CRITIQUE_E2E=1 to run the headless critique` and exit 0 (so `npm test` stays green in CI); else `node tests/self-critique-e2e.mjs`.
263
+
264
+ - [ ] **Step 2: Write the runner.** For each of `correct` and `seeded`: copy the fixture repo to a tmp dir, run `claude -p --output-format json --model sonnet --allowedTools "Read,Grep,Glob,Bash(node:*)" --add-dir <tmp>` with a prompt that passes the task text and the answer text and the round-one critic prompt from SKILL.md verbatim (read it from the file between the markers `## Critic prompt template` and the next `## `), requiring a fenced JSON block as the final output. Parse the block, build a ledger where every critic claim gets `origin: both` when its text shares a keyword with an author claim, else `critic`; score it with `claim-ledger.mjs score`. Assert:
265
+ - A: `correct.score >= expected.correct.min_round1_score`
266
+ - B: `seeded.score <= expected.seeded.max_round1_score`
267
+ - C: every word in `must_refute` appears in the text of a `refuted` claim; every word in `must_miss` appears in `missed`
268
+ Print the two scores and the per-claim summary tables, write the receipt file, exit 1 on any failed assertion with the assertion letter.
269
+
270
+ - [ ] **Step 3: Run it for real, once, and keep the receipt**
271
+
272
+ Run: `SELF_CRITIQUE_E2E=1 sh tests/test-self-critique-e2e.sh`
273
+ Expected: `correct: <score>` at or above 90, `seeded: <score>` at or below 70, assertions A, B, C pass. If A fails, the blind prompt or the penalty table is still inflating: fix the prompt wording in Task 2 (not the threshold) and rerun. Paste the two scores and the receipt path into the PR body.
274
+
275
+ - [ ] **Step 4: Run the full suite**
276
+
277
+ Run: `npm run build && git diff --exit-code && npm test`
278
+ Expected: all pass; generated tree not stale.
279
+
280
+ - [ ] **Step 5: Commit**
281
+
282
+ ```bash
283
+ git add tests/test-self-critique-e2e.sh tests/self-critique-e2e.mjs tests/.artifacts/self-critique-e2e-*.json
284
+ git commit -m "test(self-critique): headless e2e proves a correct answer scores 90+ blind and a seeded one does not"
285
+ ```
286
+
287
+ ### Task 7: Docs and PR
288
+
289
+ **Files:**
290
+ - Modify: `README.md` (self-critique row: description), `docs/superpowers/specs/2026-10-27-self-critique-v2-spec.md` (append a "Shipped" line with the e2e scores)
291
+
292
+ - [ ] **Step 1: Update the README row to the new catalog description; run `npm run validate`.**
293
+ - [ ] **Step 2: Open the PR** against the default branch with a "TL;DR (for humans)" first: what changed for someone running `/self-critique` (blind critic, computed score, corrected answer restated, rules with fire counters), the two e2e scores, and "no change to other skills". Receipts below.
294
+ - [ ] **Step 3: Commit**
295
+
296
+ ```bash
297
+ git add README.md docs/superpowers/specs/2026-10-27-self-critique-v2-spec.md
298
+ git commit -m "docs(self-critique): v2 description and e2e receipt"
299
+ ```
300
+
301
+ ---
302
+
303
+ ## Self-review notes
304
+
305
+ - Spec coverage: weak part 1 (score) is Task 1 and 2; weak part 2 (anchored critic) is Task 2; weak part 3 (littering) is Task 3 and 4; weak part 4 (cost, labels, re-verify) is Task 2 steps 2 to 4; weak part 5 (restate) is Task 4 step 4. Killer features 1, 2, 3 map to Tasks 3 and 4, Task 1, and Task 2 respectively. The Stop hook is intentionally out of scope.
306
+ - "Test it actually works": Tasks 1 and 3 pin the deterministic parts; Task 6 is the only test of the prompt itself and is gated because it costs money and needs the CLI; it is run once by hand in Step 3 and its receipt is committed.
@@ -0,0 +1,104 @@
1
+ # /self-critique: deep investigation (2026-10-27)
2
+
3
+ Subject: `olko-reflection/1.59.1/skills/self-critique` (SKILL.md 12.9 KB + `scripts/critique-nudge.mjs` 2.5 KB).
4
+ The skill text is identical in the three cached plugin versions (1.54.0, 1.56.2, 1.59.1).
5
+
6
+ ## Local usage evidence
7
+
8
+ - 122 session transcripts under `~/.claude/projects` contain critic output (`where_wrong` / "iterations to satisfy").
9
+ - Distinct reported score trajectories found by grep (14): 62->82, 55->76->85, 68->82->90, 68->84, 66->88->93, 68->85,
10
+ 72->85, 55->80->88, 80->89, 66->88, 62->80->90, 62->78->88, 72->84, 58->82.
11
+ Round 1 ranges 55-80 (median ~66); every run gains +15 to +30; 4 of 14 end at >= 90; 0 of 14 scored >= 90 on round 1.
12
+ - 28 memory files in the gic-integrations memory dir cite a critic as their origin. One index line
13
+ ("Cause line names the unverified hop") has grown to ~27 semicolon-joined clauses: the "sharpen the existing rule"
14
+ dedupe instruction turned one file into a dumping ground.
15
+
16
+ ## Strong parts
17
+
18
+ 1. External grounding. The critic has live tool access and must refute against sources. This is the one design the
19
+ literature supports (CRITIC 2023, Huang 2023, Kamoi 2024: self-correction from a prompted LLM alone does not work).
20
+ 2. Claim list before prose, receipt per claim, and a sharp definition of what is NOT a receipt (grep hit, file name,
21
+ sibling behaviour). Pre-marking `unverified` moves the cheapest findings off the critic's bill.
22
+ 3. `missed` and `shapes`. Most review tools only check what is written. This one hunts for the sibling issue the answer
23
+ should have surfaced, and asks for the class of reasoning error, which feeds Step 5.
24
+ 4. Step 5, "write the shape, not the incident". Rule + Why (dated receipt) + How-to-apply, recurrence AND cost gates,
25
+ narrowest-store routing, dedupe, and "state where it was written so the user can veto". The 2026 CLAUDE.md-growth
26
+ study found that attaching the reasoning to each instruction removed 99% of excess instructions; this skill already
27
+ does that.
28
+ 5. Honest report fields: declined fixes ("silently dropping a fix looks identical to missing it"), unverifiable,
29
+ iterations-to-satisfy. Hard cap of 3 rounds, one-challenge rule, same critic for re-score with receipts handed over.
30
+ 6. The Stop hook fails open, has two loop guards, and only nudges.
31
+
32
+ ## Weak parts
33
+
34
+ 1. The score measures the loop, not the truth. No anchor examples, no rubric weights, one integer mixing correctness,
35
+ completeness and calibration. 0/14 local runs scored >= 90 on round 1, and every run gained 15-30 points: that is the
36
+ signature of a critic paid to find things and then rewarding its own fixes (self-preference bias, "Pride and
37
+ Prejudice" 2024; ledger note n87 "offer an agent an honest branch and it will fabricate the finding").
38
+ 2. The critic is anchored by the author. It receives the author's own claim list and sources. CoVe, devils-advocate and
39
+ ratchet-review all keep the checker blind to the author's reasoning. Here the critic verifies the author's framing
40
+ instead of re-deriving the claims.
41
+ 3. Rule littering in practice. The dedupe rule says "sharpen the existing entry", which produced a 27-clause index line
42
+ nobody can read. No expiry, no "did this rule fire" signal, no cap on rules per store, and the "honest test of Step 5"
43
+ is stated but never measured.
44
+ 4. No cost model. Three rounds of live verification with no model tier guidance, no triage of load-bearing claims
45
+ first, no cheap pre-pass. The project rules elsewhere say critics should run on Sonnet; the skill does not.
46
+ 5. Findings are not labelled by how they were checked. agent-review-panel tags each finding CMD_CONFIRMED /
47
+ STATIC-INFERENCE; CriticGPT shows critics hallucinate bugs. Here a critic's inferred finding and a command-confirmed
48
+ one carry the same weight, and nothing re-verifies what the critic adds.
49
+ 6. The hook `block`s the Stop event to force an offer line, which conflicts with the owner's own "hooks act, never
50
+ block" rule, and the 1500-char threshold misses tool-heavy turns (acknowledged in the file).
51
+ 7. The corrected answer is not the deliverable. The report lists where v1 was wrong; the user still has to mentally
52
+ merge v1 and the fixes. Nothing says "restate the final answer in full".
53
+ 8. Single lens. Notes mention "give the critic distinct lenses" but the procedure spawns one critic with one prompt.
54
+
55
+ ## Similar work and what to inherit (from the research leaf, all URLs opened)
56
+
57
+ | Source | Idea worth taking |
58
+ | --- | --- |
59
+ | ratchet-review (GitHub) | Score anchors (worked examples at 3 levels) and a ratchet: a revision that scores lower is discarded |
60
+ | devils-advocate (GitHub) | Checker never sees author's reasoning; mandatory Unverified section; `(recurring)` tag on re-review |
61
+ | agent-review-panel (GitHub) | Per-finding epistemic label (command-confirmed / read / inferred); re-verify what the judge adds; "unanimous agreement is the most dangerous failure mode" |
62
+ | claude-skill-critique (GitHub) | Triage each finding Worth-fixing / Matter-of-taste / Wrong, with declined ones logged |
63
+ | The Fool (jeffallan) | Pre-mortem first pass: assume the answer is wrong, which claim breaks it |
64
+ | Chain-of-Verification (2023) | Verification questions answered independently of the draft |
65
+ | CriticGPT (2024) | Critics hallucinate bugs: a false-positive filter is part of the design |
66
+ | MemGuard (2026) | Each stored lesson carries confidence, status and lifecycle metadata |
67
+ | Osmani, "Audit your agent files" | A rule that must always hold belongs in a hook or test, not in prose |
68
+ | Catastrophic Remembering (2026) | Prompt files triple in size; the dated Why is the known fix. Keep it, add pruning |
69
+
70
+ ## Killer feature
71
+
72
+ Today: Step 5. Every other critic tool stops at a score or a list of findings. This one converts each caught error
73
+ into a durable rule with a dated receipt and a firing moment, routed to the narrowest store, and tells the user
74
+ where it wrote it. 28 rules in one project trace back to it. The pitch: "a critique that ends at a score fixes one
75
+ answer; one that ends at a rule fixes every answer after it."
76
+
77
+ What could be the killer feature (ranked):
78
+
79
+ 1. Rules that prove their own worth. Each rule gets a `fires` counter and `last_fired` date. The critic's first pass
80
+ replays the stored shapes as a checklist against the new answer and marks hits `(recurring)`. A rule that has not
81
+ fired in N sessions is proposed for deletion. This turns memory from a growing file into a measured regression bank,
82
+ and it is the thing no other tool has.
83
+ 2. Calibrated score. Two or three anchor examples in the prompt, a ratchet, and a per-claim ledger (verified /
84
+ refuted / unverifiable / inferred, each with its receipt) instead of one integer. The claim ledger is also the
85
+ marketing artifact: a screenshot of claims turning green.
86
+ 3. Blind critic. Hand the critic the original task and the answer only. It extracts claims itself, then the author's
87
+ list is compared with the critic's: claims only one side found are the most interesting output.
88
+
89
+ ## Changes recommended to the skill (smallest set)
90
+
91
+ 1. Critic prompt: remove `{{CLAIMS_AND_SOURCES}}` from the first round; the critic extracts claims blind, then
92
+ receives the author's receipts for round two.
93
+ 2. Add `checked_by` to every `where_wrong` / `missed` item: `command`, `read`, `inferred`. Author re-verifies
94
+ `inferred` before accepting.
95
+ 3. Add three score anchors and a ratchet rule (lower re-score discards the revision, report best).
96
+ 4. Step 5: replace "sharpen the existing entry" with "add one evidence line, max 3 per rule; a fourth incident means
97
+ the rule is wrong, rewrite it". Add `last_fired` and a prune pass.
98
+ 5. Step 4: end with the full corrected answer, not only the diff of findings.
99
+ 6. Model tier line: critic on Sonnet unless a Sonnet round found nothing and the answer is load-bearing.
100
+ 7. Hook: change from `block` to an additive note on the next turn, or keep `block` but document the exception.
101
+
102
+ ## Shipped
103
+
104
+ 2026-10-27: Tasks 1 to 7 landed on branch feat/self-critique-v2. Deterministic tests: ledger 9/9, rule store 7/7, offline e2e 4/4. The headless e2e run (`SELF_CRITIQUE_E2E=1`) was skipped on the first pass by decision of the owner, so there are no live round-one scores yet; run it once and append the two scores here.
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@olegkoval/agent-skills",
3
- "version": "1.59.3",
3
+ "version": "1.60.0",
4
4
  "private": false,
5
5
  "publishConfig": {
6
6
  "access": "public"
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-apple-kit",
3
3
  "description": "Build and ship Apple platform apps: macOS menubar apps, App Store submissions.",
4
- "version": "1.59.3",
4
+ "version": "1.60.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-creative",
3
3
  "description": "Creative and personal projects: photo galleries, music players, listings, wiki editing.",
4
- "version": "1.59.3",
4
+ "version": "1.60.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-garmin-kit",
3
3
  "description": "Build, test and publish Garmin Connect IQ watch faces.",
4
- "version": "1.59.3",
4
+ "version": "1.60.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-git-tools",
3
3
  "description": "Everyday git and GitHub CLI operations: conventional commits, branch hygiene.",
4
- "version": "1.59.3",
4
+ "version": "1.60.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-github-pr",
3
3
  "description": "Drive GitHub pull requests to merge-ready: review-bot loops, CI fixes, descriptions, dependency triage.",
4
- "version": "1.59.3",
4
+ "version": "1.60.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-obsidian",
3
3
  "description": "Keep an Obsidian vault in sync with work: PR sync, task rollover, morning routine.",
4
- "version": "1.59.3",
4
+ "version": "1.60.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-product",
3
3
  "description": "Take a product idea to a shippable build: MVP passes, full-stack scaffolds, UX/UI quality loops, launch plans.",
4
- "version": "1.59.3",
4
+ "version": "1.60.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "olko-reflection",
3
3
  "description": "Look back and improve: self-critique, retrospectives, performance review, rapid learning.",
4
- "version": "1.59.3",
4
+ "version": "1.60.0",
5
5
  "author": {
6
6
  "name": "Oleg Koval"
7
7
  },