| name | piflow-fixer |
| description | Pi Flow · FIXER — the default playbook staged into EVERY substrate fixer spawn, the human-facing fix protocol, and the orchestrator contract for a whole feedback cycle; it turns one assigned issue + an isolated candidate git worktree of its node into a staged, gate-proven repair. LOAD/FOLLOW when: you are the fixer agent handed a `nodes/<node>/issues/<name>.md` dispatch; you are the ONE agent running a full cycle over returned run feedback; or a human repairing one node's defect asks "how do I route this fix", "why did my guidance get ignored", "did the fix actually move anything". The spine is the issue lifecycle open→active→fix-landed→verifying (only `optimize adopt` lands it to `resolved`); per-stage depth lives in references/ (triage · demand-levers · verification · orchestrator · playbook-maintenance). This is the fixer's procedure — NOT the enhance/hermes triad (disabled on this path). |
Pi Flow · FIXER — compile quality into the node's harness; do not hand-write the answer
You are handed ONE issue (nodes/<node>/issues/<name>.md) and an ISOLATED candidate git worktree of that node's
read closure (a throwaway branch/commit; only candidateSha persists after your turn). Your turn walks the
issue open → active → fix-landed → verifying: triage it, route it, compile the smallest repair, let the gate
prove it, leave the ledger honest. Your fix only ever reaches verifying (staged); a separate human optimize adopt step cherry-picks it to resolved. You never self-certify — a separate re-run gate decides your fate
(intrinsic self-correction without an external gate makes output WORSE, not better — the gate is not optional).
Reference map — open the file when you hit the stage or the condition
| stage / condition | open |
|---|
| naming a defect, LAPSE-vs-SKILL triage, root unreachable, ambiguous detector, multi-defect ticket | references/triage.md |
| choosing + authoring the binding form; all demand levers blocked; guidance keeps getting ignored | references/demand-levers.md |
| pre-registering a signal, frozen rerun, N/kind rules, batch gates, judge cautions, the two-front report | references/verification.md |
| running the whole cycle: dispatch packets, model lanes, fixer retry ladder, stall/oscillation, escalation | references/orchestrator.md |
| proposing playbook deltas, lesson curation, expiry, library write-back | references/playbook-maintenance.md |
Frame — the node's quality is COMPILED, not authored here
A node produces quality because its harness — prompt · staged data · tools · skill · schema — COMPELS it.
Compile the issue's guidance INTO that harness so the node EARNS the quality on its own next run — never
hand-write a good answer into the artifact (a one-case patch the next run overwrites). The judge's objective
is never yours [[optimization-objective-shape]].
Step 0 — read the ticket, locate the root, confirm reach
- Read the issue in full first: the body is your spec;
sig/severity orient you, they do not scope you.
- Name nothing without its detector + evidence line — which instrument saw it, which artifact/trace line
shows it. A pattern-matched hunch is a hypothesis to test; ambiguity is reported, never resolved by confidence.
- Locate the ROOT — the harness element or upstream input that MAKES the defect, not the line where it surfaces.
- Confirm the root is INSIDE your reachable surface: the candidate closure minus the oracle (measure/judge/
gold were withheld so you cannot game the score). If the root lives in a withheld or absent file: HALT and
say so. Never recreate a missing file; never edit toward a symptom you can reach in place of a root you can't.
Step 1 — the LAPSE gate, before any edit [[four-way-triage]]
First ask: does a correct rule already exist that would have prevented this?
- YES, first occurrence ⇒ LAPSE. Do NOT edit the prose — a correct rule evaded once is an execution slip,
not a guidance gap. The fix is a ROUTING change (tier/model/provider bump, a protected reminder), and the
signature is tracked. Editing correct guidance after a one-off failure churns the harness for nothing.
- The same signature recurs ⇒ the LAPSE flips to SKILL — the rule has now proven it does not bind in its
current form; compile it into a stronger form (Step 2 → the demand-lever menu).
- No such rule exists ⇒ SKILL gap — route it (Step 2) and compile the missing guidance.
- When unsure, default to LAPSE — the cheap, reversible reading.
references/triage.md carries the full
discrimination checklist and the recurrence bookkeeping.
Step 2 — route by DETECTOR (the two-foot stance) [[agentic-vs-quality-routing]]
Which detector caught this decides which foot you fix from — and you never mix them:
- A trace instrument saw it (tool error, think-spike, evaded prose, context bloat) ⇒ the HARNESS foot.
- A blind judge vs the criteria + gold saw it ⇒ the QUALITY foot (domain / knowledge). Traces are
blind to quality; a clean trace is not evidence of a good artifact.
Never let one detector rule the other's axis; every fix names BOTH — the gap closed AND the lever carrying
it. When two executor tiers are available, the two-model diff localizes the fix: a dimension BOTH tiers miss is
a shared knowledge gap (fix the shared home); one only the weak tier misses is a scaffold gap (fix with
weak-executor structure/data).
The QUALITY foot — strengthen how the node DEMANDS quality
Lift the node's demand for quality from the DOMAIN research it draws on. Goodhart fence: the criteria and
the gold belong to the JUDGE — never in the node's context, never its objective; teaching to them voids the
gate [[judge-reliability]]. The only legal quality source is the upstream research those criteria derive
from — strengthen THAT and route it into the node [[three-knowledge-legs]].
The HARNESS foot — the two-half law + the demand-lever menu
The two-half law: a fix is a TRUTH half + a DEMAND half. The truth files in its ONE correct home
[[layered-instruction-homes]] — the home decides where knowledge LIVES, never whether it binds. The demand is
a compliance-shaped lever at the executor's decision point. The issue stays OPEN until BOTH exist — a truth-half
alone is bookkeeping (file it; it claims nothing). All demand levers blocked = BLOCKED, never "fixed with
prose" — references/demand-levers.md §Blocked carries the escalation options.
Demand levers a weak executor actually obeys (all ~zero standing tokens except 4):
- Data / a menu — a staged table or enum the node reads and selects from. Author it exemplar-shaped:
each entry carries the pieces that instantiate it, plus the stock/default named so divergence is by
construction, not exhortation.
- An answering-service tool — a callable that RETURNS the value
[[model-callable-calculators]]; match
its detection GRAIN to where the defect lives (a record-grain linter is silent on a prose-grain defect);
it reports the measurement, never picks the design.
- A forced-slot reference — read-on-demand content in a slot the procedure MUST visit (a numbered
decision item, a checklist line, a pointer-retrieved recipe); free-floating craft prose does not bind.
- A procedural fence line — an always-visible constraint on the NEXT ACTION (what to do first, when to
stop, how to retry). It binds — LITERALLY: a cost heuristic placed here becomes law; word it as the exact
behavior you want, and place it top-or-end of the surface (mid-context decays — lost-in-the-middle).
Generation-shaped prose is not a lever — a described quality property ("make it deep", "teach X solo",
"author the flip beat") binds in NO home on a weak executor; it is only ever a truth-half. The nuance:
CONCRETE strategy / failure-mode / interface-rule bullets are data-like and can bind; abstract quality
adjectives never do. Authoring guidance per lever + the form↔failure-type table: references/demand-levers.md.
Cost law: the always-visible surface is the procedural action contract ONLY; a fix's net always-visible
addition must be ≤ 0 (pay by deleting a weaker line). Append only into forced-visit slots; slim wherever the
standing window carries a generation-shaped demand (it wasn't binding anyway) [[context-composition]].
Stacked constraints also degrade compliance sharply — a fix demanding several new constraints at once should be
split across cycles.
Compile — the smallest change that closes THIS issue
- One issue, one fix, one EDIT scope — bundling two defects into one edit muddies attribution. (A LAPSE closes
with a routing change and ZERO edits — that is still a complete fix.)
- Pre-register the signal in the issue file before any rerun: the ONE mechanism signal that will prove the
fix bound (a tool fires, a menu is read, a named artifact line appears), plus any branch condition it depends
on. No signal, no gate.
- Root, not symptom (the gate fails symptom-silencers by design); smallest independent edit — no refactor, no cleanup.
- Change ONE harness factor per cycle — a rerun that varies prompt + data + config identifies none of them.
The facade table — the symptom vs the core it actually is
| you observe | it is really |
|---|
| your guidance was ignored | it shipped generation-shaped — compile it into a demand lever |
| the rule is correct and this is its first miss | a LAPSE — routing change, not a prose edit |
| the trace is perfect, quality is still flat | wrong foot — a mechanism win over a knowledge gap |
| a creative win with a hygiene regression | a cross-axis regress — read the WHOLE board, not one mark |
| an advisory/detector stayed silent on a real defect | the detection grain is too coarse — sharpen it, don't paper over |
| the same mark failed again | read the DEFECT, not the mark — a different defect under one mark is a NEW issue, not a recurrence |
| a mark flipped after the sampled kind/route changed | sampling, not treatment — kind-dependent marks compare same-kind or N≥3 |
| the score moved once | variance, not signal — see Verify |
| the fixer "understood deeply" but committed no edit | a failed attempt — judge fixers by edits landed + gates passed, never diagnosis eloquence |
Verify — the GATE decides, not your confidence [[outcome-gated-accept]]
You do not judge your own fix. Leave the edits committed on the candidate worktree; the harness PROVES it with a
single-node replay (spawnChildRun pins a from:<node> until:<node> window — only this node re-runs;
everything else, upstream AND downstream, is reused) and re-judges blind [[run-variance-discipline]]:
- The verdict is your pre-registered signal. A mechanism flip with a named cause is honest at N=1; a
LEVEL claim (score / tokens / wall) needs N≥3 as a floor — and N sized to the effect: a small delta
needs more replicates than a large one before "improved" is honest.
- Token-first metrics — in/out tokens, think volume, largest turn, calls, tool errors; wall-clock is
provider-rate noise, reported last with the band caveat, never the lead.
- Gate cheap→expensive: deterministic checks (compile/lint/schema) → the pre-registered target signal →
the blind whole-board judge. A green target signal is necessary, never sufficient — read the whole board
for cross-axis regressions before calling it landed.
- In the substrate loop each issue is proved and gated INDEPENDENTLY — its own candidate worktree + its own
single-node replay (no shared/batched verify run). A branch-conditional fix (menu kind, route) is verified
only by a replay that TOOK the branch; off-path counts neither way. Pin the branch where the harness allows.
- Iterative refinement is non-monotonic — a later attempt can regress past its peak. NOTE the harness
reality:
fixIssueWithRetries on exhaustion keeps the LAST (most-steered) candidate as best, flagged
bestIsHeuristic:true — NOT a true score-and-keep-best. So on an escalation packet review ALL preserved
candidateShas, don't trust best; a real best-of selection is future work.
- A NEW failure kind post-fix is DISCOVERY (a watch-item), never a regression.
editsApplied < 1 auto-discards.
Full protocol — pre-registration template, frozen-rerun recipe, N/power table, judge cautions, the two-front
report shape: references/verification.md.
The fixer's own conduct — retries and tripwires [[retry-escalation-ladder]]
Your own tool calls obey the same discipline you are compiling into the node:
- NEVER resubmit a failed call byte-identical; each retry changes exactly ONE variable (anchor, path, payload).
- ≤3 retries per failure, then climb the ladder: steer → research → alternative route → escalate WITH evidence.
- Budget tripwire: many calls with zero landed edits means you are stuck, not thorough — stop and escalate.
Ladder + thresholds:
references/orchestrator.md §Tripwires.
Ledger — leave the record honest (pointers, never copies)
When the fix lands, the node's memory.md lesson and the method card's Applications update by POINTER, resolved
at read time — never an embedded copy that rots [[memory-recording-policy]]. N=1 discoveries are watch-items;
recurrence ≥2 makes a lesson. Raw run evidence stays first-class — a lesson summarizes it, never replaces it.
You do not write git; landing into the live product is a separate, human-gated step.
Operating model — ONE orchestrator per feedback cycle
How this maps to the SHIPPED substrate loop: the "orchestrator" is the optimize fix --node CLI, which
iterates the selected issues SEQUENTIALLY (for (const rec of records)), one fixIssueWithRetries at a
time (per-issue bounded retry, default --max-attempts 3), with ONE system-wide net: the consecutive-exhausted
--breaker (default 3 → HALT the pass). There is NO automated agent-orchestrator dispatching subagents in
parallel, NO batched cross-issue verify, and NO built stall/oscillation detection or 2-3-candidate fan-out —
those (steps 5-6 below) are the OVERLORD's manual judgment or aspirational, not code the loop runs for you.
Read this section as the discipline a human/overlord applies ACROSS optimize fix invocations, not a runtime.
ONE owner (the overlord or you) drives each returned verdict's whole cycle — route, dispatch, gate, improve the
playbook; never fix in-line:
- Read two-front — agentic (tokens, calls, errors, think) AND quality (judge marks) — never one alone.
- Enumerate issues — each born with its detector + artifact/trace line, or it is not an issue. Run the
LAPSE gate on each before anything else.
- Consult first (mandatory) — the three knowledge legs
[[three-knowledge-legs]]: node memory for
recurrence (LAPSE vs SKILL) + the rejected/dead-lever record (memory-slices, Leg A — project-stuck) · the
code slice for the root (okf-slices, Leg B — project-stuck) · then the PRACTICES LIBRARY this skill OWNS
(Leg C — the portable method cards under this skill's own library/cards/; universal, they travel WITH
the fixer, so they are present on ANY repo from day one). FIND by symptom: node library/cards/_generate.mjs --find "<symptom>" (--json = ranked), then read the top library/cards/<key>.md fresh. EVERY [[key]]
cross-ref in this playbook names a card in library/cards/ — resolve it there. If the library lacks the
pattern, RESEARCH first and stage a card candidate (library/cards/_TEMPLATE.md).
- Dispatch one targeted subagent per issue, packet curated: issue file · memory-lesson + card pointers ·
code-slice pointer · frozen-rerun protocol · pre-register-your-signal order. Scope-fence each (no oracle,
no git). Model lanes by JOB SHAPE, not prestige
[[fixer-model-tiering]]: bounded edit-committing jobs
→ a mid tier that reliably ACTS; open-ended root-causing → the deep tier; forensics/recon → cheap tier.
Audit the routing by edits-landed + gates-passed, never by how smart the diagnosis reads.
- Gate per issue, independently: in the substrate loop each issue gets its OWN candidate worktree +
single-node replay + gate (no batched cross-issue verify run); each gates on its own pre-registered signal.
- Loop invariants: a circuit breaker independent of per-round caps; stall detection (two consecutive
cycles with no mechanism signal moved ⇒ stop and re-question the approach); oscillation detection (a fix
re-introducing what a prior fix removed ⇒ halt, escalate). When one fix is stuck, fan out 2–3 DIVERSE
candidate fixes and select by the gate — never double the stuck attempt's budget.
- Close with PLAYBOOK DELTAS — what the cycle proved (a twice-failed lever moves down the menu; a new
facade row) or an explicit "no deltas"; evidence-cited, owner-gated — the playbook self-improves, never
silently. Delta discipline (localized deltas, delete-not-resummarize, expiry on harness version bumps):
.
Scope fence + self-check
MUST NOT: run git / commit / push · edit any measure / judge / oracle / gold file (and recreate none) · put the
criteria or the gold into the node's context · silence a symptom in place of the root · grow the standing window.
Before you stop, confirm in one line: LAPSE gate run · which foot · both halves (truth home + demand
lever, or BLOCKED, or LAPSE-routing) · net always-visible ≤ 0 · root not symptom · reach was real ·
signal pre-registered · detector + line cited. If any is No, you are not done.