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.
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.
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.
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 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 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.
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.
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. 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 for JSONL paths, message shape, and the Invoker tail caveat.
Multi-conversation mode. Read 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 for CLI (token_audit.py, including --out), thrash detectors, model-tier backtest, top_sessions.py, and tests.
3. Spawn parallel reviewers
Read 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. (Observed on an Invoker CI incident: one failing job accumulated three near-identical unmerged fixes and four full reflect fan-outs in a two-hour window, none aware of the others.)
4. Synthesize
One more Agent call, given all reviewers' output, merges overlapping findings and sorts into:
- 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.
- 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.
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.
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):
- 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.
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.
- 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.
- 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.
- 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:
- Dedup:
git branch --all | grep reflect- plus the cluster hash; skip if an open [auto] PR already names that hash.
- Apply the fix hierarchy from references/lenses.md — hook/test before skill prose.
- 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/check_mine_repro_coverage.py and python3 scripts/check_hook_test_coverage.py when hooks change. Refuse to open the PR if either fails.
- Draft with
draft-pr headless mode; title prefix [auto]; include cluster hash + bounded paraphrased quotes (no transcript paths, no secrets).
- Push and
gh pr create. Never merge. Then session_mine.py mark-dispatched <hash>.
- 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:
- 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.
1---2name: reflect3description: 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.4---56# Reflect78## Write roots (ecosystem)910Accepted **skill prose** lands in the repo that already **owns the named11skill** — not exclusively `corpus/skills/`. Determine ownership first:1213- The finding names a `corpus/`, `engine/`, or `product/` skill, or is14 working-style output: the owning repo is catstack. Write under15 `corpus/skills/` (working-style via `automate-me` →16 `corpus/skills/<handle>-mode/`), same as before. Personal mode plus17 catstack engine/corpus/product **always** write only to catstack —18 this never changes regardless of what else the finding touches.19- The finding names a skill owned by another checkout (for example an20 Invoker-only skill): the owning repo is that external checkout. Apply21 there instead, in a worktree of that owning checkout, and never merge —22 external apply is never-merge in the owning checkout only. Never copy23 `engine/skills/reflect` itself into another repo; only the applied24 finding travels, not the reflect machinery.2526New mechanical hooks still land under `engine/hooks/` (catstack-only).27See [docs/ecosystem.md](../../../docs/ecosystem.md).2829Mine a transcript for durable learnings, then turn the real ones into skill edits — never silently.3031Adapted from `pstack`'s `reflect` (cursor/plugins). Transcripts: Claude Code,32Cursor, Codex, and OMP — see [references/transcript-locations.md](references/transcript-locations.md).33Review fan-out uses the harness subagent tool (Claude Code: `Agent`; Cursor:34`Task`; `subagent_type: general-purpose` / `generalPurpose`). The parent35presents findings and fires the Accepted-apply worktree (PR is the gate).3637Not 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.3839## Always run inside a subagent4041Every 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.4243## When to invoke4445- 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.46- 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.47- The user said "reflect."48- A complex task (5+ tool calls) just landed cleanly and the recipe is worth keeping.49- The agent hit dead ends, found the working path, and the path generalizes.50- The user corrected the agent's approach mid-task.51- A non-trivial workflow emerged that isn't captured anywhere.52- 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.53- 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.54- 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.55- 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.56- 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.57- 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.58- 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).59- 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.6061- 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.6263Skip when the conversation is trivial, off-topic, or already covered by a skill the parent followed correctly. One-offs are not learnings.6465## Process6667### 1. Locate the transcript(s)6869**Single-transcript mode (default).** Read [references/transcript-locations.md](references/transcript-locations.md) for JSONL paths, message shape, and the Invoker tail caveat.7071**Multi-conversation mode.** Read [references/corpus-scan.md](references/corpus-scan.md) for `corpus_scan.py` flags and remote SSH confirm-before-payload.7273**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.7475### 2. Run the cost audit, then spawn parallel reviewers7677Token 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.7879Read [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.8081### 3. Spawn parallel reviewers8283Read [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.8485Before 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. (Observed on an Invoker CI incident: one failing job accumulated three near-identical unmerged fixes and four full reflect fan-outs in a two-hour window, none aware of the others.)8687### 4. Synthesize8889One more `Agent` call, given all reviewers' output, merges overlapping findings and sorts into:9091- **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.92- **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.93- **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.94- **Rejected** — one-offs, already covered, or too speculative.95- **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.9697### 5. Present findings + auto-worktree apply (interactive)9899Present the full Accepted / Backlog / Route-to-automate-me / Rejected list to the user in the same turn.100101**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):1021030. 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.1041. `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.1052. 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.1063. 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.1074. 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.108109Chat 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.110111If 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.112113Skip 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.114115If `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.116117### 5b. Headless / session-mine mode118119When 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:1201211. Dedup: `git branch --all | grep reflect-` plus the cluster hash; skip if an open `[auto]` PR already names that hash.1222. Apply the fix hierarchy from [references/lenses.md](references/lenses.md) — hook/test before skill prose.1233. **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/check_mine_repro_coverage.py` and `python3 scripts/check_hook_test_coverage.py` when hooks change. Refuse to open the PR if either fails.1244. Draft with `draft-pr` headless mode; title prefix `[auto]`; include cluster hash + bounded paraphrased quotes (no transcript paths, no secrets).1255. Push and `gh pr create`. **Never merge.** Then `session_mine.py mark-dispatched <hash>`.1266. Cap: at most one headless pass per cluster hash per week (enforced by the driver cooldown).127128Interactive `/reflect` never uses 5b unless the user explicitly says the PR is pre-approved as the gate.129130### 6. Apply Accepted (in the reflect worktree)131132Normally the step-5 worktree agent does this. If the parent must apply (worktree spawn failed, or user redirected “apply here”), same rules:133134- 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.135- Trivial edit (a corrected fact, a tightened sentence, a stale example): edit directly in the apply worktree.136- 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.137- 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.)138- 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.139- 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.140141### 7. Summarize142143Short list, no preamble:144145- Edits applied: `<skill path>` — what changed, one line each.146- New skills created: `<skill path>` — one line each (rare).147- Backlogged: `<what to build>` — one line each, tagged with its tier and the evidence that motivated it.148- Routed to `automate-me`: one line each, with the evidence that motivated it.149- Dropped: one line per rejected finding + reason.150- 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.151- 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.