| name | run-feedback |
| description | Use when a run of a workflow, skill, command, or test produced errors or friction worth keeping, or when executing the end-of-run Retro Global Protocol — collect findings into the feedback inbox, triage them (defect / work-item / noise), and file defects into the known-issues ledger or work-items into the backlog. NOT for fixing already-filed issues (/heal-issues). Triggers: "собери фидбек по прогону", "file run errors", "retro this run", "/run-feedback". |
| tier | 2 |
| version | 1.4 |
Run Feedback
Purpose: Errors from runs evaporate today — a gate fails, gets retried, and the knowledge dies
with the session. This skill closes the loop: deterministic capture (retro step, hooks, transcript
miner) → LLM triage → deterministic filing into the existing ledgers, both of them
thin indexes over record files per known-issues-format: defects to docs/issues/ +
docs/KNOWN_ISSUES.md, work-items to docs/backlog/ + docs/BACKLOG.md. Filed issues marked
auto_fixable: true feed the /heal-issues harness. Machine state lives under the gitignored
.agent/feedback/; the repo-visible trace is the ledgers themselves.
1. Red Flags (Anti-Rationalization)
STOP and READ THIS if you are thinking:
- "I'll append a line to KNOWN_ISSUES.md without creating the issue file" -> WRONG. Lockstep or
nothing — the
file subcommand writes both or neither, for both ledgers. Never hand-edit one
side. Scope of this ban: record files and the index's - **ID** pointer lines. The
prefix→category TABLE row in the index preamble is the ONE hand-edit that is yours (§7 step 6) —
the script cannot write it.
- "A work-item is small, its body can just live in the backlog index line" -> WRONG. Both
indexes are pointer indexes: one line per record, body in
docs/backlog/<slug>.md. An inlined body is how a single entry once reached 7 849 characters —
unreadable, undiffable, impossible to close in parts. If a repo really runs a one-file backlog
(backlog_layout: "flat"), the engine REFUSES a body it would have to flatten; do not "fix" that
by squashing your body into one line — switch the layout or file the body where it belongs.
- "This finding is obviously a dup, I'll file it anyway for completeness" -> WRONG. A duplicate
goes to
--as noise --reason "duplicate of <ID>"; the recurrence is already counted by the
fingerprint merge.
- "I'll classify straight from the inbox JSON without running
triage" -> WRONG. Dup candidates
come from the script (fingerprint + title overlap). Eyeballing misses them.
- "I'll invent severity
HIGH / status handled for a new issue" -> WRONG. The write vocabulary
is owned by known-issues-format (SEV-2/3/4/LOW; open/fixed/documented/by-design/mitigated/wontfix).
Reading tolerates local extensions; writing never emits them.
- "The filing is simple, I'll skip
--dry-run" -> WRONG. Dry-run first, always — it previews the
allocated ID and the exact index line; a mis-filed ID pollutes a hand-maintained ledger.
- "I'll edit a finding JSON in the inbox by hand" -> WRONG. Only the CLI mutates inbox state;
hand edits break fingerprint dedup and the audit trail.
- "The body I just filed came out malformed, I'll fix the issue file" -> WRONG. Ledger writes are
create-only, so a filed body cannot be repaired. The gate now catches the two failures that used to
land silently (RF-2,
fixed): an unterminated fence and a defect body with no
## Reproduction section are refused at
exit 4, and --dry-run echoes the rendered record so you can read the body before it lands.
Still compose in a REAL file rather than a heredoc you cannot re-read — the gate checks structure,
not whether the prose is right. Landed broken → leave it, tell the human.
- "The retro step failed, I should retry it / fail the workflow" -> WRONG. The retro is
non-blocking by contract: report one line and move on; it NEVER changes the calling workflow's verdict.
2. Capabilities
- Queue findings from any surface (
collect) with cross-source dedup by fingerprint.
- Prepare a triage table with duplicate candidates (
triage).
- File defects into
docs/issues/ + KNOWN_ISSUES.md and work-items into docs/backlog/ +
BACKLOG.md — both in lockstep with rollback, both allocating an ID (<PREFIX>-<n> / WI-<n>),
and dismiss noise — all create-only, dry-runnable (file).
- Mine historical Claude Code session transcripts for failures (
mine).
- Deterministic retro ownership for nested workflows (
claim / release).
- Feed the self-heal harness (
issues --status open --json).
3. Execution Mode
- Mode: hybrid.
- Why this mode: capture, dedup, filing, journaling are deterministic file mutations (script-first,
per SKILL_EXECUTION_POLICY §5); classification, severity, and issue-body authorship are judgement
(prompt-side, §7 below).
4. Script Contract
- Command:
python3 .agent/skills/run-feedback/scripts/run_feedback.py <subcommand> …
(stdlib-only; no venv; run from anywhere inside the target repo, or pass --repo-root).
- Subcommands:
collect · triage · file · journal · issues · mine · claim · release · init · doctor
— full flag reference in references/cli_reference.md.
- Config:
docs/feedback/config.json per repo (ledger paths, component→prefix map, backlog
anchor, plus backlog_dir / backlog_prefix / backlog_layout for the work-item ledger).
Resolution: --config → RUN_FEEDBACK_CONFIG env → repo default → built-ins. Schema stays v1:
the work-item keys are additive with defaults (docs/backlog, WI, index+files), so a config
written before them loads unchanged.
- Exit codes:
0 ok (incl. dedup) · 1 unexpected · 2 usage · 3 config/env (also
doctor not-ready) · 4 filing conflict (slug exists, vocab violation, missing backlog anchor) ·
5 finding not found · 6 claim denied.
- Failure semantics: with
--json-errors, failures emit one JSON line
{"v":1,"error",…,"code",…} on stderr; exit code equals code.
- Idempotency:
collect is fire-and-forget safe — a repeated fingerprint merges
(occurrences+1, sources union) and exits 0. file is create-only and conflicts loudly.
- Dry-run:
file --dry-run previews ID, paths, and the exact index line with ZERO writes.
5. Safety Boundaries
- Allowed scope: writes ONLY to
.agent/feedback/** (machine state, gitignored) and — from
file exclusively — the configured issues_dir, index_path, backlog_dir, backlog_path.
- Configured ledger paths are validated structurally, because a repo-supplied
config.json is
untrusted data: relative only, inside the repo, no dot-component (so .claude/commands,
.cursorrules, .envrc, .github/** are all impossible, not merely denylisted), ledger file
keys must be .md, no ()[]<>| ` '" or control characters (they would break out of a
markdown link or forge a line in doctor output), and the forbidden-root/basename check is
casefolded — Path.resolve() does not canonicalize case and macOS/Windows are
case-insensitive, so exact matching let .Claude/commands through.
- Paths the tool DELETES are contained too, not just the ones it creates:
--finding <path> must
resolve inside inbox_dir/filed_dir/dismissed_dir (filing moves the record), and a
finding_id read off disk must match the generated grammar before it is used to build a path — an
absolute one otherwise made pathlib discard the left operand and write anywhere.
- Bootstrap exception (§7 Bootstrap only): finishing
init's todo is agent-side by design —
you MAY hand-edit docs/feedback/*.json, seat the work-item anchor in the repo's existing
backlog (BACKLOG.md / ROADMAP.md / similar — create a thin docs/BACKLOG.md only if none
exists), and add prefix→category table rows. Issue files, index issue lines, and finding JSONs
stay CLI-only even during bootstrap.
- Create-only ledger writes: never edits or deletes an existing issue file or index line;
resolving/flipping statuses belongs to humans or
/heal-issues under known-issues-format rules.
- Lockstep (both ledgers): the anchor/section is resolved BEFORE anything is written, the record
file is written first, and if the index write fails the record file is rolled back.
6. Validation Evidence
- Local verification:
cd .agent/skills/run-feedback/scripts && python3 -m unittest discover -s tests
(fingerprint stability, messy-ID allocation, index placement regression, lockstep rollback for
BOTH ledgers, work-item body preservation + flat-layout refusal, dry-run tree-hash, journal
multiprocess hammer, miner fixtures) and bash tests/test_e2e.sh (zero-test guard + scripted
pipeline in a mktemp repo).
- Expected evidence: green suite;
doctor --json reports ready: true in a configured repo.
- Contract sync:
python3 ../known-issues-format/scripts/check_contract_sync.py stays green.
- Expected security-scanner noise —
run_audit.py reports CRITICAL "secrets" at two kinds of site,
both benign, and it is worth knowing which is which because the alternative is learning to ignore the
scanner entirely:
scripts/tests/test_wi_tail.py, scripts/tests/test_iteration3.py — the synthetic fixtures for
the credential screen. A test for a secret detector necessarily contains secret-shaped strings
("AKIA" + "A"*16, a fake PEM header, postgres://user:pw@host).
scripts/feedback_lib/body.py — the scanner's bearer rule is bearer\s+\w+, so it matches the
English words in this module's own class labels ("HTTP bearer token") and prose. Contorting a
user-facing error label to satisfy a regex that matches ordinary English would be the wrong trade.
Verified by reading every flagged site: no live credential is committed anywhere. A hit in any
other production module is a real finding.
7. Instructions
Triage protocol (the core judgement loop)
- MUST run
triage first — the table carries dup candidates you cannot see from raw JSON.
- For every dup candidate, open the referenced issue file. Genuinely the same defect → do NOT
file; dismiss with
--as noise --reason "duplicate of <ID>" (the fingerprint merge already
recorded the recurrence).
- Classify each remaining finding:
- defect — reproducible wrong behavior with a fix path → issues ledger;
- work-item — enhancement / polish / signal without a broken contract → backlog;
- noise — transient environment, operator error, already-fixed → dismiss with a reason.
- If the finding's resolution is an edit to a SHARED artifact — a skill, an agent prompt, a
workflow, anything installed into other repos — two extra checks before filing:
- Generalize it out of the stack it happened in. Strip the language, tool, repo layout and
project name. A lesson filed as "grep
test/" or "run prettier --check" is either ignored
as foreign on other stacks or, worse, followed literally and returns the wrong answer — Go
keeps tests beside the source, so that grep finds nothing and sanctions the deletion it was
written to prevent. State the rule so it holds on every stack the framework supports; concrete
commands belong in a per-ecosystem table, never in the rule itself.
- Separate guidance from behaviour change. Advisory text is cheap. Text that changes what
runs, what gets committed, or which workflow dispatches is a behaviour change: check it
against the target framework's MANDATORY rules first, and file it as a work-item for
review, not as guidance to land. A rule whose justification describes a scenario the target
framework cannot produce is a defect in the lesson, not a lesson.
- Author the body in a REAL file and pass it via
--body-file — for BOTH classifications. Both
ledgers are thin indexes: the body becomes the record file, the index gets one pointer line.
- Defects: severity strictly from
SEV-2/SEV-3/SEV-4/LOW; category from the ledger's
prefix→category table; body from
assets/templates/issue_body_template.md
(Symptom / Reproduction / Workaround / Fix path / Related / Do-not), citing evidence paths.
MUST make ## Reproduction a fenced sh block with runnable commands — it is the input
executes; prose repros are not auto-healable.
Add ONLY when the fix is mechanical and gate-verifiable — this is the explicit
opt-in the heal harness selects on.
Retro protocol (Global Protocol — end of every terminal workflow)
- Claim: at workflow START run
run_feedback.py claim --run-id "<workflow>-<task-slug>". Exit 6 → another workflow owns this
run's retro → SKIP the retro step entirely (you are nested).
- At workflow END (owner only), gather evidence —
.agent/sessions/latest.yaml blockers, gates
that failed or retried this run, fresh docs/reviews/coverage-* artifacts — and ask the user ONE
question: "Что прошло НЕ гладко в этом прогоне?" with the observed candidates pre-listed.
Non-interactive runs proceed with observed signals only.
collect each signal (--source workflow --kind gate-failure|test-failure|review-finding|blocker|user-friction,
with --workflow, --task-id, --phase context). Then run the Triage protocol above.
- Release:
run_feedback.py release --run-id "<same id>".
- Non-blocking guarantee: any failure inside this protocol → report one line, continue the
workflow's normal completion. Never retry-loop the retro, never change the workflow verdict.
Ad-hoc (/run-feedback command)
Same as the retro protocol without claim/release: gather friction from the CURRENT session (failed
commands, retries, blockers), scoped by the user's hint; mine first when the hint says so.
Bootstrap protocol (unconfigured repo — self-serve, no operator needed)
Trigger: you have findings to process but the repo is not set up. doctor now tells you plainly —
ready: false with configured: false, exit 3 (RF-1; it used to report ready: true while its
own remediation said "run init", so this section had to teach you not to trust the field). Note the
asymmetry that remains and is deliberate: collect/file still run on built-in defaults and exit
0, so filing does not error in an unconfigured repo — only doctor refuses. Bootstrap BEFORE
the first real filing, not after something breaks.
Exit 3 from doctor on a repo with no config is this trigger. Exit 3 from collect/file means
something different: the config EXISTS but is corrupt (or the env is broken).
init is create-only and CANNOT repair it — do not delete or rewrite the corrupt file to force
a re-init, and never run init more than once per run. Inside a retro this is a non-blocking
failure (report one line, move on — the guarantee in the Retro protocol wins over bootstrap);
outside a retro, preserve the file and surface it to the human.
- Run
run_feedback.py init — deterministic part: copies both config templates into
docs/feedback/ (create-only, existing files are never overwritten) and seeds the
id_prefixes map from the EXISTING ledger (component→prefix pairs derived from
docs/issues/*.md frontmatter; conflicts are reported, never guessed).
- Finish the
todo list init prints — this is YOUR judgement, not the script's:
- backlog anchor — the WORK-ITEM destination (
file --as work-item), separate from the
issues ledger (defects go to docs/issues/ + KNOWN_ISSUES.md, which the CLI seeds
itself). The backlog is a human-ranked file, so the CLI refuses to guess an insertion
point — it inserts only at backlog_anchor (<!-- feedback:discovered-issues -->) and
exits 4 without it. FIRST look for the repo's existing backlog under its real name —
docs/BACKLOG.md, docs/ROADMAP.md, or similar — point backlog_path there and seat
the anchor inside its Discovered-Issues section. Create a thin docs/BACKLOG.md ONLY
when nothing of the kind exists (the CLI seeds it from known-issues-format's
backlog_md_template.md on first filing); a second backlog next to a live ROADMAP splits
the project's work-item tracking. Record files go to backlog_dir (default
docs/backlog/), which the CLI creates — leave backlog_layout at index+files unless
the project genuinely wants a single-file backlog;
- heal gates: replace
example-component with ONLY components that have real checks
(unit/e2e/validator commands that exit 0); no gate = honestly not auto-fixable — do NOT
invent gates;
- prefix rows: every prefix in
id_prefixes needs a row in the ledger's
prefix→category table.
- Re-run
doctor — ready: true (which now implies configured: true) with an empty
remediation list — then resume the original
operation (the filing that hit exit 3, the retro, the heal run).
- Config files are repo-visible: mention their creation in your run report/commit. Inside a
retro this protocol is still non-blocking — bootstrap failure → one report line, move on.
8. Workflows
- [ ] claim (workflow start, terminal workflows only)
- [ ] collect observed signals + the one retro question
- [ ] triage → classify → dry-run → file
- [ ] release + one-line report
9. Best Practices & Anti-Patterns
| DO THIS | DO NOT DO THIS |
|---|
file --dry-run before every real filing | Hand-edit KNOWN_ISSUES.md / BACKLOG.md or inbox JSON |
| Author every body (defect AND work-item) in a real file | Inline a work-item body into the backlog index line |
| Dismiss duplicates with the original's ID in the reason | File a second issue for a known fingerprint |
Runnable fenced sh repro in every defect body | Prose-only "point the tool at a slow source" repros |
Mark --auto-fixable only for mechanical, gate-verifiable fixes | Mark honest-scope / design decisions auto-fixable |
Rationalization Table
| Agent Excuse | Reality / Counter-Argument |
|---|
| "The inbox item is obviously noise, I'll just delete the JSON" | Dismissal is file --as noise --reason … — it journals and keeps the audit trail. |
| "I remember this issue already exists, no need to check" | Run triage; fingerprints and title-overlap candidates are computed, not remembered. |
| "The retro question annoys the user, I'll skip asking" | One question per run, pre-filled — that's the contract. Skipping loses the only human-signal channel. |
| "Exit 6 from claim is an error I should fix" | Exit 6 = you are NESTED. Skipping the retro is the correct behavior, not a failure. |
| "Config is corrupt — Bootstrap says I should repair it" | init is create-only; it cannot fix a corrupt file, and retrying it just loops. Inside a retro: one report line, move on. Outside: preserve the file, hand it to the human. |
| "Filing worked (exit 0), so the repo must be configured" | Built-in defaults make filing "work" in an unconfigured repo — file does not gate on configuration. Run doctor: ready: false / configured: false is the answer. Bootstrap comes BEFORE the first real filing. |
| "I'll file the lesson exactly as it happened — the details make it concrete" | Concrete to THIS stack means wrong on the others. A shared-artifact lesson gets generalized before filing (§7 triage step 4). |
| "The backlog has no record dir yet, so this project must want flat bullets" | backlog_layout decides, not the dir listing. The default is index+files and the engine creates the dir; a flat backlog is an explicit opt-in. |
| "It's just a line of guidance, it can't break anything" | If it changes what runs, what gets committed, or which workflow dispatches, it is a behaviour change — check it against the target's MANDATORY rules first (§7 triage step 4). |
10. Examples (Few-Shot)
See examples/usage_example.md for a full capture→triage→file walk.
Input: a workflow's coverage gate failed twice, then passed after a fix.
Output (abridged):
python3 .agent/skills/run-feedback/scripts/run_feedback.py collect \
--source workflow --kind gate-failure --component vdd-multi \
--message "coverage gate failed 2x: security critic timeout" \
--workflow full-robust --phase "Step 2" --context task=task-089
…then triage, classify as defect, file --dry-run, file.
11. Resources
scripts/run_feedback.py — the CLI (see §4); scripts/feedback_lib/ — the engine modules.
scripts/hooks/ — opt-in Claude Code SessionEnd capture: session-end marker +
auto-mine of the ended session (RUN_FEEDBACK_HOOKS=1 + RUN_FEEDBACK_MINE_ON_END=1). See
references/capture_surfaces.md for the layering (retro step =
portable; hooks/miner = Claude-Code-only accelerators) and the verified PostToolUse limitation
(fires only on SUCCESSFUL tool calls — cannot capture failures; do not wire it).
assets/templates/issue_body_template.md — defect body skeleton;
assets/templates/work_item_body_template.md — work-item body skeleton.
assets/templates/feedback_config_template.json + heal_config_template.json — per-repo
config starters; run_feedback.py init copies them create-only and seeds the prefix map
(§7 Bootstrap protocol; recipe: System/Docs/QUALITY_FEEDBACK_LOOP.md §Setup).
references/finding_schema.md — Finding v1 field-by-field spec.
references/cli_reference.md — full flag reference per subcommand.