Skip to content
Back to skills

Reflect

ASecurity

Mine a conversation transcript — and the commit history of the files it touched — for durable learnings, then route Accepted items into concrete skill/code edits via an auto-spawned worktree in the repo that owns the named skill + PR (never merge). Working-style preferences route to the sibling skill automate-me, not a task skill edit. User involvement, forced restatement, "you fucked up/messed up", or the same type of complaint twice is a FAILURE and must route to automate-me. Use when the u...

  • 4 stars
  • 0 votes
  • 0 copies
  • 1 view
  • Added September 19, 2026
ai-agentspythonrustgogit

Works with

  • claude code
  • cursor
  • terminal
  • cli
  • mcp

Security analysis

A100/100

Pro scans all 20 files and shows the line behind each finding

Scanned September 25, 2026

npx -y skills add EdbertChan/catstack --skill reflect --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Reflect?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Reflect
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/edbertchan-reflect/badge)](https://www.skillsdirectory.com/skills/edbertchan-reflect)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
---
name: reflect
description: Mine a conversation transcript — and the commit history of the files it touched — for durable learnings, then route Accepted items into concrete skill/code edits via an auto-spawned worktree in the repo that owns the named skill + PR (never merge). Working-style preferences route to the sibling skill automate-me, not a task skill edit. User involvement, forced restatement, "you fucked up/messed up", or the same type of complaint twice is a FAILURE and must route to automate-me. Use when the user says reflect, after a complex multi-step task lands cleanly and the recipe is worth keeping, when the agent hit dead ends before finding a working path, or when the user corrected the agent's approach mid-task.
disable-model-invocation: true
---

# Reflect

## Write roots (ecosystem)

Accepted **skill prose** lands in the repo that already **owns the named
skill** — not exclusively `corpus/skills/`. Determine ownership first:

- The finding names a `corpus/`, `engine/`, or `product/` skill, or is
  working-style output: the owning repo is catstack. Write under
  `corpus/skills/` (working-style via `automate-me` →
  `corpus/skills/<handle>-mode/`), same as before. Personal mode plus
  catstack engine/corpus/product **always** write only to catstack —
  this never changes regardless of what else the finding touches.
- The finding names a skill owned by another checkout (for example an
  Invoker-only skill): the owning repo is that external checkout. Apply
  there instead, in a worktree of that owning checkout, and never merge —
  external apply is never-merge in the owning checkout only. Never copy
  `engine/skills/reflect` itself into another repo; only the applied
  finding travels, not the reflect machinery.

New mechanical hooks still land under `engine/hooks/` (catstack-only).
See [docs/ecosystem.md](../../../docs/ecosystem.md).

Mine a transcript for durable learnings, then turn the real ones into skill edits — never silently.

Adapted from `pstack`'s `reflect` (cursor/plugins). Transcripts: Claude Code,
Cursor, Codex, and OMP — see [references/transcript-locations.md](references/transcript-locations.md).
Review fan-out uses the harness subagent tool (Claude Code: `Agent`; Cursor:
`Task`; `subagent_type: general-purpose` / `generalPurpose`). The parent
presents findings and fires the Accepted-apply worktree (PR is the gate).

Not every finding belongs in a skill edit here. A finding about the *user's working style or preferences* (not a code lesson) routes to the sibling skill `automate-me` instead — see step 4.

## Always run inside a subagent

Every invocation of this skill — single-transcript or multi-conversation mode — runs inside a subagent, no exceptions. The parent launches it with the harness subagent tool (`subagent_type: general-purpose` / `generalPurpose`: the process reads transcripts fresh from disk and doesn't need the parent's own conversation context) with the user's original reflect arguments/scope, then waits for it to report back. The subagent runs steps 1-4 (locate transcript(s), cost audit, lens fan-out, synthesis) — that's the large part. The parent runs steps 5 and 6 itself, in the main thread, never delegated for the investigation: presenting the Accepted / Backlog / Route-to-automate-me / Rejected list, then **immediately** firing a catstack worktree (+ background agent) to apply Accepted items (PR is the landing gate — do not wait for a second “apply those” turn). This keeps the bulk of the investigation out of the parent's context window — the parent only needs the final synthesized findings list.

The subagent does not end its turn while a task it launched is still running;
it waits for that task and returns once, because a resumed subagent replays its
whole context.

## When to invoke

- The `reflect-on-thrash` Stop/sessionEnd hook fired. Treat the named transcript as the scope; still present the list, then auto-worktree-apply Accepted (step 5) — never merge without a PR.
- The `wrong-check-reflect` Stop/notify hook fired (assistant admitted a prior check/claim was wrong — "Good catch — my earlier check was wrong" or similar). Treat as FAIL. The named transcript is the scope. A Grep/name hit was treated as a check; finish the live correction, then present Accepted / Backlog / Route-to-automate-me / Rejected.
- The `handback-needs-attempt` Stop hook fired (the assistant handed the user a command or step without attempting it, and no permission refusal or human-only blocker was present). Treat as FAIL; attempt the step first or name the real blocker, then present the finding when reflect runs.
- The user said "reflect."
- A complex task (5+ tool calls) just landed cleanly and the recipe is worth keeping.
- The agent hit dead ends, found the working path, and the path generalizes.
- The user corrected the agent's approach mid-task.
- A non-trivial workflow emerged that isn't captured anywhere.
- A session, or a corpus-scan bucket, shows heavy user involvement — many corrections, clarifying answers typed out by hand, repeated manual confirmations — over a short span. That is a **FAILURE**, not a preference ping: the user had to stay in the loop because the agent missed a named constraint. Route to `automate-me` (step 4). Do not write a one-off task-skill edit and call it done.
- The user did a step by hand that the agent could have done: ran a command in their own terminal and pasted the output, edited a file themselves, pasted in the fix, looked something up, or said "I'll just do it." What the user did is the spec for what the agent should have done. Treat it as FAIL; the User-did-it lens ([references/lenses.md](references/lenses.md)) names each step and the agent action that would have replaced it. The `user-did-it` hook flags these live, through the llm-judge inbox. A step that physically needs the person (a password, hardware, an approval dialog, filming) is not this class.
- The user said "you fucked up", "you messed up", "I told you", "you're ignoring me", or equivalent agent-blame. Treat this reflect pass as FAIL. The class is *ignored named constraint*, not the swear word. Product-blame ("the UI is messed up") is not this class.
- The reply conceded the user's suspicion — "your instinct was right", "your hunch was right", "good catch" followed by a correction. Conceding means the user found what the agent's own checks did not, so the miss is the finding, not the concession. Treat as FAIL; the `wrong-check-reflect` detector fires on these shapes.
- The user had to keep iterating, restate requirements, or change product direction because the agent missed something already named. FAIL, then `automate-me`. A genuine mind-change (user learned new facts, then redirected) is not failure. A forced restatement of an already-named constraint is.
- The same *type* of complaint appears in 2+ turns or 2+ sessions (repro-then-fix, UI proof before done, e2e before claiming pass, obey the named verb). That class is a bug. **Must** invoke `automate-me` — not optional, do not wait for the user to say "automate me." `token_audit.py`'s `intervention-must-automate` flag is the mechanical catch; human-message only, never tool_result / skill-injection / `/loop` polls. Run it with `--judge` so a restated existing rule is judged; without it the flag reads `unchecked`, never a clean `no`.
- It's been a while since the corpus-wide pass (`top_sessions.py` + this skill's lenses across the worst offenders) last ran. No fixed cadence and no cron — just periodically worth doing by hand.
- The user asks *why does X keep happening* across a span of time or across machines — that's **multi-conversation mode**; read [references/corpus-scan.md](references/corpus-scan.md).
- The invocation is itself an automated `reflect-ci-*` task with no human in the loop: run the step-3 sibling check unconditionally — concurrent automated dispatches produce exactly the duplicate-work burst a human would otherwise be there to notice.

- The **session-mine worker** marked a cluster `ready_for_headless` in `~/.cache/catstack-session-mine/queue.json` — see [references/session-mine.md](references/session-mine.md). That path uses **headless mode** (step 5b): GitHub PR review is the approval gate; never merge; never skip the repro fixture pair.

Skip when the conversation is trivial, off-topic, or already covered by a skill the parent followed correctly. One-offs are not learnings.

## Process

### 1. Locate the transcript(s)

**Single-transcript mode (default).** Read [references/transcript-locations.md](references/transcript-locations.md) for JSONL paths, message shape, and the Invoker tail caveat.

**Multi-conversation mode.** Read [references/corpus-scan.md](references/corpus-scan.md) for `corpus_scan.py` flags and remote SSH confirm-before-payload.

**Subagent transcripts are in scope, attributed to the parent.** A session's Task-tool subagents (`<session-dir>/subagents/agent-*.jsonl`) are part of that session, not separate conversations: `token_audit.py` folds them into the parent's report under `subagents`, and `corpus_scan.py` lists them on the parent row. Read them, because delegated work is where principle and skill violations happen — a subagent that skipped repro-then-fix, claimed a pass without a test line, or thrashed on one file is the parent's failure. Every lens judges subagent turns by the same skills and principles as the parent's own turns. The one thing that does not carry over: a subagent's `user` turns are the parent's prompts, never the human's, so they are never frustration or `intervention-must-automate` evidence.

### 2. Run the cost audit, then spawn parallel reviewers

Token usage is exact data sitting in every transcript. Don't have an LLM reviewer eyeball the raw JSONL. Run the mechanical counter first, then hand its *output* (small, structured) to the Cost lens — never the raw file.

Read [references/cost-audit.md](references/cost-audit.md) for CLI (`token_audit.py`, including `--out`), thrash detectors, model-tier backtest, `top_sessions.py`, and tests.

If `CATSTACK_REFLECT_LENS_BUDGET` is set to a positive integer token count,
read the audit report's `total` field after step 2. When `total` is over the
configured budget, step 3 uses the ordered required lens set from
[references/lenses.md](references/lenses.md) and omits the other lenses. An
unset variable leaves the historical fan-out unchanged. A malformed or
non-positive value is an error: do not guess at a budget.

### 3. Spawn parallel reviewers

Read [references/lenses.md](references/lenses.md) for the five lenses and the fix hierarchy. Prefer the cheapest check that still catches the mistake — do not write a skill line when a hook or test would do.

Before fanning out, check for sibling passes on the same incident: `git branch --all | grep -E "(reflect-ci|fix-ci)-<job-id>"` for concurrently dispatched fix/reflect branches, and `ls ~/.claude/projects/ | grep -F <incident-keyword>` for a sibling reflect's surviving transcript. A crashed sibling commits nothing — its synthesis lives only in its transcript tail; read that as prior art instead of re-deriving the same facts from zero.

The normal expected set is `Judgment Tooling Cost History Divergent Frustration`.
If the budget gate reduced the pass, launch only the ordered required set and
record that exact set before launching:
`python3 scripts/fanout_complete.py --expected <launched lenses>`. The expected
set is the contract for this pass; it is not silently inferred from returned
agents. If the harness supports per-agent model selection, use the cheaper
configured lens model for the reduced pass; otherwise keep the normal model.
Pass the following status to synthesis when reduced:
`reduced reflect: ran <lenses>, omitted <lenses> (session <N> tokens over budget <B>)`.

### 4. Synthesize

One more `Agent` call, given all reviewers' output, merges overlapping findings, writes each one in three parts (below), and sorts them into:

When the budget gate reduced step 3, synthesis must include the exact reduced
status line above before findings. It must name every omitted lens, even when
all required lenses returned.

- **Accepted** — real, durable, worth acting on. Apply the elimination hierarchy from step 3 before slotting a finding here as a skill edit: if a reviewer proposed a skill/rule fix but a categorical or lint/test fix was actually available, bump it to Backlog with the stronger fix named instead, or split it.
- **Backlog** — real, but the right fix is higher up the hierarchy than a skill edit. Note which tier (1: categorical, 2: lint/test, 3: hook) each backlog item is.
- **Grounding gate for skill prose.** Before an Accepted item becomes skill prose, name the established principle it instantiates — author, title, year, and a checkable URL — or write "no known prior art". An incident-shaped rule with neither goes to Backlog for grounding, not to Accepted. A rule that restates one session's bug in fresh words reads as invented and drifts into an incident log; the field's own name for it (fail fast, invariant, completeness check, reconciliation) is what the skill should say.
- **Rejected** — one-offs, already covered, or too speculative. A rejection
  that says already covered by X names X at file and line; a sentence a hook
  challenged gets evidence or a tag, never deletion.
- **Route to `automate-me`** — real, but it's about how *this user* likes to work rather than a lesson about the code or task. Don't inline these as edits to a task-specific skill; hand the finding to `automate-me`. Same-type complaints (2+ turns or 2+ sessions) and forced iteration / product-direction change after an agent miss are **mandatory** here, not optional. Invoke `automate-me` in the same turn if the user already asked to capture the preference, or name it as the first follow-up with evidence; do not wait for them to re-prompt.

**Three required parts per finding.** Every Accepted, Backlog, and Route-to-`automate-me` finding states all three. A finding missing one is not ready to sort.

- **What happened** — the event, with the turn or quote from the transcript that shows it.
- **Fix** — the change that stops it, at the tier the elimination hierarchy picks.
- **Catch** — what notices if it happens again: a named hook, test, detector flag, or check. Design it here, alongside the fix, not later at summary time. Backtest it against this transcript: run the catch, or trace its rule, over the incident turn and record `fired`, `silent`, or `unchecked`. A catch that stays silent on the case that motivated it is not a catch; one that could not be run is `unchecked`, never `fired`. If no catch is possible, write `catch: none — <reason>` so the gap reads as a gap, not as a complete finding.

### 5. Present findings + auto-worktree apply (interactive)

Present the full Accepted / Backlog / Route-to-automate-me / Rejected list to the user in the same turn.

**Precondition: apply nothing on a fan-out that did not return.** Step 5 starts only when step 3's reviewers reported and step 4's synthesis, including the grounding gate, actually ran. A fan-out that died -- rate limits, a crashed lens, a killed agent -- means the pass is incomplete: re-run the missing lenses, staggered or on a second model, or say plainly that the pass is partial and name which lenses are missing. Unreturned reviewers are not reviewers that passed, and the parent doing the investigation itself is a fallback for a failed worktree spawn, not for a failed review. `scripts/fanout_complete.py --expected <lenses> --returned <lenses>` decides it: exit 0 complete, 1 incomplete with the missing lenses named, 2 unchecked when no expected set was recorded. Only exit 0 opens step 5.

**When Accepted is non-empty, do not wait for a second “apply those” / “make a PR for Accepted” turn.** In that same turn, fire a **dedicated git worktree keyed to the owning repo of each Accepted item's named skill** (per Write roots above) that applies only the Accepted items (fix hierarchy: categorical / lint-test / hook before skill prose):

0. Group Accepted items by owning repo. Catstack-owned skills and any working-style/personal-mode item always group under catstack — never split those into an external worktree.
1. `git worktree add` under `.worktrees/reflect-<short>/` on a new branch `reflect/<topic>-<yyyymmdd>`, rooted in the owning repo's own checkout: the current catstack tip the session is using (stack tip if mid-stack; otherwise `main`) for catstack-owned items, or a fresh worktree of the external owning checkout for externally-owned items. Never place an externally-owned item's worktree inside catstack, and never copy `engine/skills/reflect` into the external checkout.
2. Spawn a background subagent **bound to that worktree** (Cursor: Task with an isolated worktree / `best-of-n-runner`; Claude: Agent with `cwd` set to the worktree). Pass only that repo's Accepted items verbatim — no Backlog, no Rejected, no automate-me routes.
3. That agent commits, opens a PR via `draft-pr` / `make-pr` (title prefix optional `[auto]` when unattended) **in the owning repo**, and **never merges** — external apply is never-merge in the owning checkout only, same as catstack.
4. Tell the user each worktree's path + branch + owning repo + that a PR is opening; they can veto in chat (“drop item 2”, “abort reflect apply”) — if the PR is not open yet, stop the agent and remove the worktree; if it is open, close/leave the PR per their instruction.

Chat approval is no longer the gate that *starts* Accepted application — the PR is. Backlog wait is for process, agents, and workers (durable automation) — not already-named execution, which dispatches immediately: Invoker if MCP/CLI is installed unless the user said not to, else a separate git worktree + PR stack. Do not auto-apply every Backlog row. Same-type / `intervention-must-automate` still invokes `automate-me` in the same turn; other Route-to-automate-me items still wait for an explicit yes. Never silently edit skills in the parent worktree while the reflect-apply worktree is the intended home for those edits.

If the session is an open product incident and synthesis already names a concrete product change, that change is the first Accepted item the worktree implements. Process hooks stay parallel backlog — do not offer only the hook or a proof plan when the named one-liner is what stops the live defect.

Skip the auto-worktree only when the user said reflect-only / “don’t apply” / Accepted is empty / every Accepted item is already landed on an open PR.

If `token_audit.py` flagged `intervention-must-automate: yes`, or synthesis found the same complaint type twice, the first offered action is invoking `automate-me` (alongside any product one-liner). That is not a style note.

### 5b. Headless / session-mine mode

When invoked by `session_mine.py` (or an agent following a `ready_for_headless` queue row), **do not** wait for chat approval. GitHub review is the gate:

1. Dedup: `git branch --all | grep reflect-` plus the cluster hash; skip if an open `[auto]` PR already names that hash.
2. Apply the fix hierarchy from [references/lenses.md](references/lenses.md) — hook/test before skill prose.
3. **Repro gate (hard):** every detector/skill/hook change in the PR MUST include a positive synthetic fixture (fires) and a negative fixture (stays silent), with tests. Run `python3 scripts/ci/check_mine_repro_coverage.py` and `python3 scripts/ci/check_hook_test_coverage.py` when hooks change. Refuse to open the PR if either fails.
4. Draft with `draft-pr` headless mode; title prefix `[auto]`; include cluster hash + bounded paraphrased quotes (no transcript paths, no secrets).
5. Push and `gh pr create`. **Never merge.** Then `session_mine.py mark-dispatched <hash>`.
6. Cap: at most one headless pass per cluster hash per week (enforced by the driver cooldown).

Interactive `/reflect` never uses 5b unless the user explicitly says the PR is pre-approved as the gate.

### 6. Apply Accepted (in the reflect worktree)

Normally the step-5 worktree agent does this. If the parent must apply (worktree spawn failed, or user redirected “apply here”), same rules:

- Before drafting, check whether an earlier reflect pass already drafted the same lesson but never landed it: `git log --all --grep=reflect -i -- <file>`, then `git merge-base --is-ancestor <candidate> HEAD`. A lesson that only exists on an unmerged branch is not in effect — adapt and land the prior draft, naming the duplicate branch in the summary, rather than writing a third divergent copy.
- Trivial edit (a corrected fact, a tightened sentence, a stale example): edit directly in the apply worktree.
- Substantive edit (a new section, a new principle, more than ~10 lines): write it out in full, matching the target skill's existing structure and tone, and land it on the PR branch.
- Commit each applied edit immediately, not batched at the end of the step: a late crash or a blocked closing turn then loses nothing already applied. (Observed: a reflect pass drafted two skill edits, its closing summary was blocked by an unrelated hook with no further turn, and the edits survived only because the transcript did.)
- Backlog item: if it is already-named execution, dispatch it in the same turn (Invoker if installed unless the user said not to; else a worktree + PR stack). If it is process / agents / workers, describe the concrete script/check/test to write and wait for an explicit yes — don't write that as part of `reflect` itself.
- Route-to-`automate-me` item: don't draft it here. Either invoke `automate-me` directly if the user wants it done now, or leave it as a named follow-up in the summary below.

### 7. Summarize

Short list, no preamble. Every applied, backlogged, or routed line carries the three parts from step 4 — what happened, fix, catch — plus the catch's backtest result (`fired` / `silent` / `unchecked`, or `catch: none — <reason>`):

- Edits applied: `<skill path>` — what changed, one line each.
- New skills created: `<skill path>` — one line each (rare).
- Backlogged: `<what to build>` — one line each, tagged with its tier and the evidence that motivated it.
- Routed to `automate-me`: one line each, with the evidence that motivated it.
- Dropped: one line per rejected finding + reason.
- Cost audit: total tokens, cache-read share, and the count of flagged thrash/model-tier items from `token_audit.py` — one line, with the real numbers.
- Feedback-loop check: recurring-failure-signature count and longest no-verify edit streak from `token_audit.py` — one line; call out explicitly if either was non-zero.

Files in this skill

  • SKILL.md20.1 KB
  • baselines/charts/deploy-7d.svg3.9 KB
  • baselines/charts/mttr-7d.svg3.1 KB
  • baselines/charts/rework-30d.svg6.2 KB
  • baselines/charts/rework-7d-spark.svg6.1 KB
  • baselines/charts/rework-7d.svg6.1 KB
  • baselines/dora-ai-history.json13.7 KB
  • baselines/dora-ai-report.md4 KB
  • baselines/dora-ai.json2.2 KB
  • references/corpus-scan.md4.5 KB
  • references/cost-audit.md12.6 KB
  • references/lenses.md8.8 KB
  • references/session-mine.md5.6 KB
  • references/transcript-locations.md4.8 KB
  • scripts/backfill_dora_history.py8.5 KB
  • scripts/capture_dora_baseline.py3.9 KB
  • scripts/cluster_interventions.py7.8 KB
  • scripts/collect_dora_events.py32.8 KB
  • scripts/com.catstack.dora-snapshot.plist.template1.1 KB
  • scripts/com.catstack.session-mine.plist.template994 B

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.