Turn recurring review findings into proposed rule and skill updates.
Scanned 9/3/2026
Install to Claude Code
npx -y skills add imisic/claude-marketplace --skill a-self-learner --agent claude-codeInstalls into .claude/skills of the current project.
Are you the author of A Self Learner?
Add the live security badge to your README — it updates automatically with every re-scan.
[](https://www.skillsdirectory.com/skills/imisic-a-self-learner)More formats (shields.io, HTML) on the badges page.
---
name: a-self-learner
description: Turn recurring review findings into proposed rule and skill updates.
disable-model-invocation: true
---
# Self-Learner: Review → Rule/Skill Feedback Loop
Closes the loop between review skills and rule/skill files. When a project review catches the same class of issue repeatedly, this skill proposes a preventive update (a new rule, a new preflight check, or a whitelist entry) and hands it off to `a-rules-optimizer` or `a-review-optimizer` for application.
**Input:** optional flags
- `--dry-run` (default): analyze and propose, never write
- `--apply`: after each proposal, ask user, apply on approval
- `--since YYYY-MM-DD`: only consider findings from this date forward
- `--threshold N`: override recurrence threshold (default 3 for recurring, 5 for chronic)
If the project has no `.claude/reviews/` directory yet, the skill reports "no history to learn from" and offers to scaffold the convention (see `references/review-log-schema.md`).
---
## Core Principles
**Propose, never silently write.** Every rule/skill change must be shown to the user as a diff with rationale, then applied only after explicit approval. Propose only; never auto-apply and never auto-commit. A proposal workflow is not commit authorization, whatever your project or global commit rules say.
**Delegate writes to the other optimizers.** This skill never edits rule files or review SKILL.md directly. It generates a structured proposal and invokes `a-rules-optimizer` (for rule writes) or `a-review-optimizer` (for preflight/agent writes). Keeps each skill's scope tight.
**Evidence > opinion.** A recurrence claim needs ≥ N findings of the same category across distinct dates. "Feels recurring" is not good enough; the cluster must survive the grouping algorithm in `references/recurrence-detection.md`.
**Record rejections.** Proposals the user declines get logged to `rejected-proposals.md` with the reason. Next run shouldn't re-raise the same rejection: only surface if new evidence appears (e.g., the issue recurred another 5 times since rejection).
**Per-project scope.** Each project owns its own `.claude/reviews/` directory. The skill does not cross-pollinate learnings between projects (that's a future extension; for now, deliberate isolation keeps project conventions separate).
---
## Phase 1: Ingest
Read the signals. The skill needs data before it can cluster anything.
### 1a: Review log
```bash
test -f .claude/reviews/review-issues.jsonl && wc -l .claude/reviews/review-issues.jsonl
test -f .claude/reviews/review-issues-archive.jsonl && wc -l .claude/reviews/review-issues-archive.jsonl
```
If neither exists, this project hasn't been capturing findings yet. Stop here and report:
> No review history found at `.claude/reviews/review-issues.jsonl`. To enable self-learning, add capture calls to this project's `*-review` skill (see `references/review-log-schema.md` for the format and `references/capture-finding.sh` for the helper). Until then, there's nothing for this skill to learn from.
**Optionally, offer to scaffold the convention** so the user doesn't have to wire it by hand. If they accept, this is the one thing `a-self-learner` writes directly (inert plumbing, not a rule/skill change; still show what lands and get an explicit yes first):
1. **Drop the helper.** Copy `references/capture-finding.sh` to `.claude/scripts/capture-finding.sh` and `chmod +x` it. It creates `.claude/reviews/` on first call, computes `category_hash`, and appends one JSON line per finding.
2. **Seed the ledgers.** Create `.claude/reviews/` with empty `applied-learnings.md` and `rejected-proposals.md` (just their `#` header lines from `references/review-log-schema.md`). The `review-issues.jsonl` and `review-issues-archive.jsonl` files appear on the first capture and first archive respectively; don't pre-create them.
3. **Git posture.** These files are meant to be tracked per-project (they document how defenses evolved). Do NOT gitignore them.
4. **Wire the capture calls (delegate, don't hand-edit).** The review skill's SKILL.md must call `capture-finding.sh` once per confirmed finding. Editing that review skill is `a-review-optimizer`'s job, not this skill's. Emit a proposal for `a-review-optimizer`: "add a capture-finding.sh call per confirmed finding, passing `--project/--skill/--run-id/--dimension/--severity/--category/--file/--line/--message`; slug the `--category` as one-pattern-one-slug (see `references/review-log-schema.md` slug hygiene)." Route it through the normal Phase 4 approval gate. If no `*-review` skill exists yet, tell the user to create one (via `a-review-optimizer`) first, since capture has nothing to hook into otherwise.
Either way, stop for this run: even scaffolded, the log is empty until the next review populates it. Re-run after findings accumulate.
If the file exists, read every line as JSON. Drop malformed lines with a warning (don't silently discard, print them so the user can fix the capture).
### 1b: Project memory (optional convention)
These sources exist only if the project keeps an agent-memory convention; many don't. Treat every one as optional: if a path is absent, skip it silently and lean on the review log and `rejected-proposals.md`. Do not stall waiting for files a fresh project never had.
Also ingest:
- Project `MEMORY.md` "Review History" section, if present (existing convention in some projects, surface debt scores and round summaries).
- `.claude/projects/<project-id>/memory/feedback_fp_*.md`: documented false positives. These become the KEEP-THIS-WHITELISTED signal for Phase 3.
- `.claude/projects/<project-id>/memory/feedback_review_*.md`: severity / priority feedback (e.g. "deprioritize X for this threat model").
### 1c: Existing review & rules context
- Read the project's `*-review` SKILL.md (whichever skill writes to the log). Extract: agent names, current check IDs, known-correct pattern whitelists.
- Read `.claude/scripts/preflight*.sh` if present. Extract check IDs.
- Read `.claude/rules/` index.
Knowing what's already checked prevents proposing duplicates.
### 1d: Previously processed
Read `.claude/reviews/applied-learnings.md` and `rejected-proposals.md` if present. Cluster IDs already applied or rejected don't need re-proposing unless new evidence arrived.
---
## Phase 2: Cluster
Group findings into patterns. See `references/recurrence-detection.md` for the full algorithm. Summary:
1. **Group** by `category_hash`: precomputed by the capture helper as `sha256(category)[:16]`, a pure function of the stable `category` slug. One cluster per category.
2. The hash deliberately excludes the free-text `message` (method names / paths vary per finding) and the `dimension` (the same pattern gets tagged differently by different agents): including either one fragmented a single pattern into many hashes. See `references/review-log-schema.md`.
3. **Count** occurrences per hash across all ingested findings.
4. **Classify**:
- `Recurring`: ≥ 3 occurrences across ≥ 2 distinct dates
- `Chronic`: ≥ 5 occurrences OR spans > 30 days OR in ≥ 3 distinct files
- `False Positive`: user dismissed ≥ 2 times (from feedback_fp_* or explicit rejection)
- `One-off`: everything else; ignore
5. **Attribute** each cluster to a review dimension (security / architecture / quality / performance). For a cluster whose findings carry more than one dimension (~4% of categories drift this way), pick the dominant one (most frequent, ties broken by highest severity). Determines which optimizer gets the proposal.
Between grouping (2) and counting (3), run the **near-duplicate slug gate** (`recurrence-detection.md` Step 2b): the hash is the slug, so slug drift (`inline-event-handler` vs `inline-event-handler-in-view`) splits one pattern into several sub-threshold clusters. Surface candidate merge-families for human confirmation before classifying: don't auto-merge.
Emit `recurring-patterns.md` as the Phase 2 artifact: one section per cluster with the data from `references/recurrence-detection.md` output format.
---
## Phase 3: Propose
For each cluster, decide the target and draft a proposal. See `references/proposal-template.md` for the exact format.
### 3a: Recurring / Chronic → preventive action
Pick one of these target actions based on the cluster's dimension and what's already in place:
| Cluster dimension | No existing check | Check exists but misses | Pattern-level (architectural) |
|-|-|-|-|
| Security | Propose preflight check via `a-review-optimizer` | Update check pattern via `a-review-optimizer` | Propose rule file via `a-rules-optimizer` |
| Architecture | Propose preflight check | Update check pattern | Propose rule file |
| Quality | Propose preflight check | Update check pattern | Propose rule, possibly scoped narrowly |
| Performance | Propose preflight check | Update check pattern | Propose rule file |
**When in doubt, lean toward `a-rules-optimizer`**: rules prevent issues before code is written; preflight checks catch them after. Prevention is cheaper.
**Before proposing a NEW rule, check whether the rule already exists but isn't loading.** A cluster can keep recurring while the governing rule already exists, because it lives in a file whose `paths:` scope never matches the file type where violations happen (e.g. a JS rule scoped `public/js/**` that never loads while editing PHP views, where every regression sits). Signal: the cluster's `file` values cluster in one file type, and grep finds the rule already stated in a differently-scoped rule file. When you see this, the proposal is not "add a rule"; it's "restate the existing rule in a file scoped to where the violations occur, with a one-line cross-reference to the source of truth," routed to `a-rules-optimizer`. This path-visibility fix is cheaper and truer than inventing a duplicate rule. Name the root cause explicitly in the proposal's `rationale`.
### 3b: False Positive clusters → whitelist action
Target: the review skill's "KNOWN CORRECT PATTERNS (DO NOT FLAG)" section (see `a-review-optimizer`'s Phase 4a output). Propose adding:
```
- <normalized pattern>: <project-specific reason>, see <file:line of representative occurrence>
```
Use the existing `feedback_fp_*.md` text verbatim where possible: the user wrote it in their own words for a reason.
### 3c: Proposal structure
Every proposal must include (see `references/proposal-template.md`):
- `cluster_id`: stable identifier (hash prefix)
- `pattern`: one-line human description
- `evidence`: list of findings supporting the cluster (file:line + date + severity)
- `rationale`: why a preventive change is warranted (count, span, severity distribution)
- `target_skill`: `a-rules-optimizer` or `a-review-optimizer`
- `target_file`: which file the other skill should modify
- `proposed_diff`: concrete change, not a description of one
- `expected_effect`: what future reviews should differ about after this lands
Missing fields → proposal is incomplete, don't show it to the user.
---
## Phase 4: User approval gate (hard stop)
**This phase must pause for user input.** Present proposals one at a time, batched by priority (Chronic first, then Recurring, then False Positive whitelists). For each:
1. Show the proposal in human-readable form (pattern, evidence count, rationale, the diff).
2. Ask: "Apply this? [y/n/skip/details]"
3. On `y` → delegate to the target skill with the proposal as input. That skill writes the file. Do not write anything directly from `a-self-learner`.
4. On `n` → append to `rejected-proposals.md` with reason (ask the user for the reason if `n` alone; "no reason given" is acceptable).
5. On `skip` → leave for next run, neither apply nor reject.
6. On `details` → show evidence in full (all finding lines, not just count), then re-prompt.
**Commit policy.** Even after approval, this skill does not commit. The target skill writes the file; the user decides when to stage and commit per the global per-commit-ask rule. Do not invoke `git commit` from this workflow.
---
## Phase 5: Record
After the approval loop finishes:
1. **`applied-learnings.md`**: append one entry per applied proposal: date, cluster_id, target, summary, file(s) modified. This becomes the project's "how our defenses hardened" changelog.
2. **`rejected-proposals.md`**: already appended to in Phase 4 for each rejection. Also record the evidence count at rejection time so re-proposal threshold can be computed ("this was rejected when count was 4; only re-raise when count exceeds 8").
3. **Archive processed AND resolved findings**: move them from `review-issues.jsonl` to `review-issues-archive.jsonl`, stamped with a processed-date, so the next run starts from a smaller log and doesn't re-cluster old data. Archive a finding when ANY of these holds:
- it participated in an applied or rejected cluster this run;
- a resolution row pairs with it on `(category, file)`, such as a `FIXED:` row written by the project's `*-fix` skill;
- it carries a `covered_by` stamp naming the check or rule that now owns it.
**Archiving is not deletion, and it does not require re-verifying the fix.** The archive file is kept, and if the issue is still present the next review re-captures it. Being conservative here is exactly what breaks the loop: a resolved finding left in the active window re-clusters on every future run and buries the live signal under it.
Archiving only the first case is the common mistake. It is how two projects reached 491 and 140 active rows that were roughly 85% resolved pairs, and on one of them a single commit fixed four findings *in the same commit that appended them to the log*, leaving all four reading as open weeks later.
4. **Report summary**: count of proposals applied / rejected / skipped, list of files modified, suggestion for when to re-run (typically: "after the next 5 reviews, or in ~4 weeks").
---
## What this skill does NOT do
- **Does not write rule files or review SKILLs directly.** Always delegates.
- **Does not auto-commit.** User owns commits per CLAUDE.md.
- **Does not cross-pollinate between projects.** Each project's feedback loop is isolated.
- **Does not re-review the codebase.** It only operates on the review log and feedback memory. If the log is empty, the skill has nothing to do.
- **Does not replace `a-rules-optimizer` or `a-review-optimizer`.** Those remain the authoritative ways to audit against the codebase. This skill adds a *historical-evidence* input that those skills can consume.
---
## Reference files
| File | When to read | Contains |
|-|-|-|
| `references/review-log-schema.md` | Phase 1a (understanding the log format) and any capture-side integration | JSONL schema, field definitions, `.claude/reviews/` directory convention |
| `references/recurrence-detection.md` | Phase 2 (clustering) | Grouping, hashing, thresholds, cluster classification |
| `references/proposal-template.md` | Phase 3 (drafting proposals) | Required fields, format, target-skill routing rules |
| `references/capture-finding.sh` | Setup: helper that existing review skills call to append findings | Small bash script (~30 lines), safe to source and call |
Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.
No comments yet. Be the first to comment!