---
title: "Loops and learned checks"
description: "A loop keeps improving a plan draft until it is ready: an author resolves, a critic and a tester of other model families analyze and test, and Fir referees every turn on evidence, with stop rules, guards against the known failures, a held-out final pass and a person asked only what only a person can settle; then what plans teach Fir becomes learned checks, tested on history and watched in shadow before a person lets them block."
url: https://frazer.ai/docs/source/planning-flow/loops
index: https://frazer.ai/docs/source/planning-flow/llms.txt
tokens: 9562
---

> For agents: this page is indexed at https://frazer.ai/docs/source/planning-flow/llms.txt, and every page at https://frazer.ai/llms.txt. Copy commands from this file exactly.

# Loops and learned checks

A **loop** keeps improving a plan draft until it is ready, and it runs **by default** on every plan an agent drafts, in every risk class. An **author** keeps resolving, a **critic** and a **tester** of other model families keep analyzing and testing, and **Fir** referees every turn on evidence until a stop rule fires. A loop has three phases: **fix** (turns until the draft is ready), **elevate** (perspectives, gaps, elevations and improvements, once the draft is ready) and **confirm** (the held-out final pass). Nobody argues a finding away: a finding closes only when the evidence says so. The same loop runs on code and on any file through [Frazer Reviewer](https://frazer.ai/docs/reviewer#loops-for-code); this page is the loop on a plan.

> **Three rules hold at every step:** A model’s output is an assertion until Fir checks it. A person decides only what only a person can. Every part a model runs names the assumption it encodes, and is measured again on each new model family, so parts that stop paying are removed.

## Roles

| Role | Family | Reads | May change | Writes |
| --- | --- | --- | --- | --- |
| **author** | The drafter’s | The plan, its checks’ results, the merged findings, its own transcript; never the score’s weights, the critic’s rubric or the final pass’s | **The plan draft only**, by anchored blocks. Checks, thresholds and the proofs a finding guards are read-only: weakening one stops the loop. A check the author adds never closes a finding by itself | One draft version a turn, answers in the plan’s Questions section, resolutions linked to findings (`Resolves: CQ1`), decision rows (never their answers), “cannot be done as written” with a reason |
| **critic** | Another family than the author’s, drawn from the panel’s `cold_reviewer` role | The version; from turn 2 its own earlier findings and the evidence of their resolutions; for a contest, only its evidence. Never the author’s transcript, notes or reasoning | Nothing | Questions and verdicts, anchored, each with a suggested fix, in two scopes: **process** (structure, evidence, checks, order) and **domain** (what a practitioner of the plan’s type knows) |
| **tester** | Another family than the author’s, and than the critic’s where the panel has three; the panel’s `tester` role | The version, the repository at the base commit, database branches, metrics, CI history | Nothing in the plan: it runs exploration tasks and probes | Findings with evidence (a query and its result, a proof that does not compile, a dry run that fails), each probe offered as a plan check |
| **Fir**, the referee | — | Everything | Nothing a model asks for | Runs the plan’s checks, merges findings through the loop’s ledger, scores, diffs turns, applies the stop rules, writes the turn record |

Each role is its own session. The critic starts a fresh session every turn whose only memory is its ledger, read as data, with its rubric and type rules first so they stay cached across turns; the author keeps one session, compacted with In force re-derived from Fir, until a fresh start. No role’s model changes inside a turn.

## A turn

A turn starts only from something new that the author’s last version has not seen: a check result, a finding, a probe’s result or a person’s answer. Models asked to re-check their own work without one do not improve, so a loop with nothing new to read stops.

1.  **Check.** Fir runs the [plan’s checks](https://frazer.ai/docs/source/planning-flow#plan-checks-on-frazer-ci-static-first) on the version. If a deterministic stage fails, the author fixes it first and no model runs: the model stage is held, with what it would have cost.
2.  **Propose.** In parallel, each sealed before the other is read: the critic’s fresh pass on the blocks that changed since the last turn (the whole plan on turn 1) in its two scopes, plus a verification pass on its ledger; and the tester’s probes.
3.  **Probe.** Each probe runs under matched conditions (the same base commit, the same database branch shape, the same inputs), so its result is a contrast, not an anecdote. One probe a turn challenges the plan’s main shared premise (“every payout has one open hold”), because a premise every role accepts spreads into every version. Fir merges findings through the ledger: a finding is raised once, a resolved or dismissed one returns only with new evidence, and findings are scanned for text aimed at the author before the author reads them.
4.  **Compose.** The author answers the top 10 by rank: **fix** (an edit to the anchored blocks that names the finding or probe it answers), **answer** (a fact, with citations Fir resolves), **contest** (evidence only: a citation, a check result, a query), **ask a person** (a question only a person can settle, with what its answer unblocks, or a decision row), or **cannot be done as written** (with a reason; it goes to a person). An edit that names nothing is flagged and reverted unless a person keeps it, and a turn that rewrites more than 30 % of the plan’s blocks outside those its findings name is refused: rewriting a whole document wears away its detail.
5.  **Reflect.** Fir writes the next version (a commit on the plan’s pull request with the trailer `Loop: lp-4c2e turn 3`), re-runs only the checks whose inputs changed, scores the version, compares every block with all earlier turns, and rates each edit `helped` (a finding resolved, a check now passing), `hurt` (a regression), `no observed effect` or `confounded` (several edits changed at once and nothing isolates one). A confounded edit is probed again before it counts, and a model finding that disappears without its block changing is variance, not a resolution. Then the stop rules.

A finding is **resolved** only by evidence: **fixed** (its block changed and the check or the verification pass no longer raises it; a reproduced finding’s probe now passes), **answered** (the citations resolve and the verification pass accepts the answer), **conceded** (the critic withdraws on the author’s evidence, recorded so the critic’s calibration learns from it), or **decided by a person**. After two contests on one finding it becomes one card for a person, with both sides’ evidence, a summary from a family that is neither the author’s nor the critic’s, and a recommended answer.

## Stop rules

The first rule that fires ends or pauses the loop:

| Rule | By default | Ends as |
| --- | --- | --- |
| **Ready** | The score at or above the loop’s target (95; the gate line stays 90), no open finding at Medium or above, the last verification pass resolved every finding, **and the final pass clean** | `done: ready` |
| **Quiet** | No new finding at Medium or above for 2 turns, or nothing new to read | `done: converged`, after the final pass |
| **Needs a person** | Every open item is one only a person can settle: a question or a [person task](https://frazer.ai/docs/source/person-tasks) | `paused: needs you`; it resumes on the answer |
| **Cannot be done** | The author or the critic says the outcome cannot be reached as written, with a reason | `paused: needs you` |
| **Checks weakened** | The author removes or loosens a check, a criterion or a proof that a finding guards | `stopped: weakened`, with a High finding |
| **Scope growth** | The draft’s paths grow beyond the request’s, or beyond an approved plan’s terms | `stopped: scope` |
| **Oscillation** | A block returns to an earlier turn’s text, or a finding reopens after two resolutions | The block is frozen and becomes one question for a person; the loop goes on with the rest |
| **Regression** | A required check that passed now fails | The next turn fixes it first; a repeat, or a score more than 5 below the best version, restores the best version and stops |
| **Diminishing returns** | A score gain under 2 points a dollar for 2 turns | `done: converged` |
| **Budget, time, turns** | Guards by risk class, never targets: R0 $0.75, R1 $2.50, R2 $4.50, R3 $20; 20 minutes, 30 minutes, 1 hour and 4 hours; 8 turns, 12 for R2 and R3 (quiet after 3 turns there) | `stopped: budget`, `stopped: time`, `stopped: turns` |
| **No funds** | A turn cannot extend its reservation | `Paused: no funds` at the turn boundary, with the best version kept |
| **Fresh start** | A finding fixed twice and still raised | The author restarts from the plan and the finding in a new context |

**The best version wins, by Fir’s score.** Every version and its score is kept. When the loop ends, the plan’s pull request carries the best-scoring version (on a tie, the later one), restored by a mechanical commit if a later turn scored lower, and a person may pick another from the loop’s history. No model chooses among versions: Fir’s score counts what is met item by item, with required items and open findings kept apart, so it cannot prefer a weaker version that reads better.

**The final pass.** The critic sees its own ledger, so after turn 1 it no longer reads without context. A loop ends `done` only after the plan’s [cold review](https://frazer.ai/docs/source/planning-flow#cold-review) runs as a **final pass**: a fresh draw of the panel that never saw the loop’s ledger, findings or turns, with a rubric the author never sees. A new question or a blocking verdict from it reopens the loop for one more turn, and its rate of new findings is how a loop’s quality is measured. Code loops end the same way.

## Guards against the known failures

| Failure | Guard |
| --- | --- |
| The critic talked out of a true finding | Findings close only on evidence; the critic never reads the author’s reasoning; two contests make a card for a person; a planted-flaw drill in 1 loop in 20 (in shadow, never shown) measures each critic’s catch rate per class |
| A critic that finds nothing | A pass with no findings on changed blocks returns its full coverage map, each category marked clear with an anchor; the drills; the final pass |
| The plan wanders from what was asked | The outcome, non-goals and approved terms are read-only to the author unless a person allows term edits; every term edit is listed first in the loop’s summary; scope growth stops the loop |
| Cost runs away | The estimate is debited before each turn and every role sees its countdown; diminishing returns and the budget stop the loop; held model stages cost nothing |
| Gaming the score | Only items Fir evaluates count; checks, weights and rubrics are read-only to the author; weakening a guarded check stops the loop; the final pass is held out |
| A wrong premise every role accepts | One probe a turn negates the plan’s main premise; the final pass is a fresh draw; a fact learned in one plan is checked again in the next |
| Checks weakened through the critic | A finding that asks to weaken a check, a criterion or a non-goal goes to a person and is never applied by the author |
| Correlated blind spots | Critic and tester of different families by lineage; a missing family shows as missing, never replaced |
| Injected instructions | Plan text aimed at reviewers is a `security_warning`; findings are scanned before the author reads them |

## When a person is asked

Only for what only a person can settle: a question no evidence answers, a decision row (answered at approval), a card after two contests, a frozen block, “cannot be done”, or a [person task](https://frazer.ai/docs/source/person-tasks). At most 5 reach a person per approval, batched in Today and My work under Needs you with one key each. The loop keeps working on everything else and pauses only when nothing else can move.

## Starting a loop

| How | Example |
| --- | --- |
| By hand | **Improve until ready** on a draft, or `fir plan loop 414 --phase P18 --target 95 --budget 4` |
| From a planning session | “Keep going without me” in a [planning conversation](https://frazer.ai/docs/source/planning-flow#planning-by-conversation) |
| By default | Every plan an agent drafts gets a loop at its class’s depth, from the first day; one switch per scope turns it off, and turning it off is recorded on the plan’s Checks (“loop: off for acme/docs by lena, Oct 18”) |
| On a schedule | A **polish wave** over a program’s drafted phases before an approval wave |
| On any file | `frazer review loop docs/plans/payment-retry.md` through Frazer Reviewer’s API |
| A wide first turn | For R3, or at `deep` and above by choice: three drafts from different families compete on turn 1, Fir composes the best-scoring one with the others’ supported blocks, and turns go on in sequence |

A loop does not start on a draft with no outcome, no non-goals or no acceptance criteria: the author may not write the outcome for the person, so such a draft goes to an interview first. A question to a person always says what its answer unblocks, so a question worth less than its asking is not sent.

The loop’s definition is a **loop program**, a Fir Action in Frazer CI’s language, versioned and changed like any part of Frazer’s way:

```python
# acme/plans .frazer/ci/plans.star (project acme-production): the loop for drafts
load("@frazer//plans:v1", "plan_loop")

plan_loop(
    name = "acme/drafts",
    on = ["draft"],                                   # also "request", "schedule", "readiness_failed"
    use = "plan_loop",                                # the panel's use for loops
    roles = {"critic": "cold_reviewer", "tester": "tester"},   # panel roles; families other than the author's
    target = 95, max_turns = 8, quiet_turns = 2,
    budget = {"R0": "0.75", "R1": "2.50", "R2": "4.50", "R3": "20"},  # dollars: guards, not targets
    elevate = "on",                                   # the default; "off" turns the phase off for this scope
    wall_clock = {"R0": "20m", "R1": "30m", "R2": "1h", "R3": "4h"},
    final_pass = "cold_review",
    on_ready = "notify",                              # never "request_approval" unless a person set it
)
```

**What a loop stores.** A `Loop` record (`loops/lp-4c2e`): its target (a plan version, a pull request or a file), the roles with the family, model, tier and effort of each, the loop program’s version, the budget and the spend, and one row per turn: the version it read, the stages’ results and what was held, the findings in (new, raised again, resolved, contested), the author’s answers, the version it wrote, the score, cost, tokens and time, and the stop reason. `fir plan loop view lp-4c2e` (`show` stays an alias), `get_plan_loop` and the plan’s **Loop** tab read the same rows: one dense line per turn, the stop reason, and never the transcripts unless someone opens one.

## Elevate: perspectives, gaps, elevations and improvements

Once fix reaches Ready (or converges), **elevate** runs once and writes one **elevation report** (`er-…`), shown as the plan’s **Elevate** tab beside Checks. Every item in it is a finding or a decision row, never a silent edit, and it runs on every class by default; `elevate = "off"` in the loop program turns it off for a scope.

-   **Perspectives are lanes chosen by the plan’s facts.** The plan side of [Frazer Reviewer’s lanes](https://frazer.ai/docs/reviewer/lanes): `plan/security` (a contract on a payment, identity or secrets path, a public endpoint), `plan/compliance` (a compliance profile on the scope), `plan/privacy` (personal-data fields), `plan/data` (a schema, a migration, a new store), `plan/scale` (many dependents, a hot path, a load criterion), `plan/operations` (a deployable changes, R2 and above), `plan/cost` (an estimate above the class’s median), `plan/people` (person tasks, two squads, a customer-visible change), `plan/product` (a user journey), `plan/program-design` (signatures and call paths against the codebase), `plan/integration` (contracts, a program, sibling plans), `plan/adversary` (the pre-mortem: “it failed: why?”), `plan/precedents` (where the plan could beat its own bar) and **Your principles** (the approver’s kept [principles](https://frazer.ai/docs/source/decisions/conversation#principles)). Selection is deterministic and shown (“security: a contract on `payments.Client`, a public endpoint”). Each lane owns one dimension and is told what it does not diagnose, runs sealed on another family than the author’s, never sees the score, the turn or the other lanes, and never invents an authority or a citation; a lane with nothing to raise returns its coverage, so silence never reads as approval. Before the report is written, a **consensus challenger** looks for evidence against what every lane agreed on, and a **disagreement resolver** settles where lanes disagree against the repository and the records, in separate contexts.
-   **Gaps are computed by Fir, never asked of a model**: the type’s open advisory rules, each marked **not addressed** or **absent**; **precedents** (features that most similar closed plans that went well had and this one lacks, with their support, said as a correlation and never as a cause: “8 of 11 closed plans that changed `payments.Client` and went well rehearsed the rollback on a branch of orders-db”); what the program needs from this plan that no plan covers; and the forecast’s tail with the items that drive it.
-   **Elevations** are where the plan could beat its own bar or the field, each with evidence Fir can resolve: the organization’s own best closed plan’s measured outcome, a standard’s clause, or documentation and benchmarks found through [Frazer Context](https://frazer.ai/docs/context). One whose evidence does not resolve is an **idea**, folded and never applied. A testable elevation can carry a spike item, so the next turn tries it against the code before anyone adopts it.
-   **Improvements** turn every gap, elevation and lane item into at most one change (`add`, `fix` or `simplify`), ranked by value per cost, one-way doors first, each ending in one of five resolutions: **fix** (the author, inside its freedom), **ask** (a decision row for the approver), **accept risk** (a person, recorded with who and why), **out of scope** (a non-goal added) or **dismiss as wrong** (which labels the lane’s precision). The outcome, the non-goals, a wider scope and a bigger budget are never the author’s to fix.
-   **Every lane item carries a proof of validity** (a citation Fir resolves, a query or probe result, a check that fails, a precedent count) or is folded as **unproven**, never counted in “yes, if N”. An `add` improvement that would grow the plan’s estimate by more than 10 % or reach paths outside the request is an ask, never a fix, and `simplify` wins ties. A person’s comments on a draft are sent as one batch (**Send review**), which starts one turn; an edit the loop later reverts is churn, and at two in one loop the blocks it touched become one question.

The Elevate tab opens on one count line, then the asks, then everything else folded (product UI, demo data): “Elevate · yes, if 2 · 10 lanes, 4 clear · 4 gaps · 2 elevations · 8 improvements: 6 fixed by the loop, 2 ask · 3 ideas folded · $1.14 · 4 min 40 s”. The verdict line reads **yes** (nothing open at Medium or above) or **yes, if N** (N asks or open improvements at Medium or above). The approval page shows only the asks and the accepted risks, so an approver reads two rows, not ten lanes; at program scale the same gap in many plans is one row by its cause. Improvements stay outside the plan score, so points are never bought with elevations. For agents, `get_elevation_report` returns the same items and counts, and `resolve_improvement` fixes inside the author’s freedom and raises asks; an agent never accepts a risk or dismisses a blocking item.

## On by default, accuracy first

Loops run by default on every plan an agent drafts, every class, with all three phases; a benchmark tunes their depth, turns, lanes and stop rules per class, and never decides whether they run. **The rule that chooses a default:**

1.  The most accurate policy measured for the class wins: fewest issues left at the end, then findings that held, then fewest regressions.
2.  A policy counts as as accurate when its excess issues left stay inside a margin stated before the run.
3.  Among the best and those as accurate, the cheapest is the default: cost breaks ties and never outvotes accuracy.
4.  **Budgets are guards, not targets:** a class’s default budget is the 99th percentile of the default’s spend, so nearly every loop ends on a quality rule and none ends early to save money.
5.  **Defaults are versioned** (`frazer/loop-defaults@2026-11`), each citing the report it came from; an organization pins a version or follows the latest, and the defaults are measured again on every new model generation.

`fir plan loop defaults --class R2 --explain` prints the defaults in force and the report behind them.

## Not run: no funds

-   **Free checks always run.** The static stages (schema, links, lint, analyze, simulate) cost nothing and never wait for money, so a plan still gets every deterministic finding.
-   **Admission by reservation.** Before a loop or a model check starts, Fir reserves its median estimate at every level on its path: the organization’s credits or plan window, then the organization’s, project’s, program’s and plan’s budgets. If any reservation fails, the work does not start and shows **“Not run: no funds”**: what did not run, which balance ran out, the shortfall, when it can run, and one action.
-   **Between turns.** An admitted turn always finishes. A turn that cannot extend the reservation leaves the loop **“Paused: no funds”** at the turn boundary, with the best-scoring version kept and what finishing needs (“needs about $0.90 more”). Paused loops resume on their own when funds arrive, by the scope’s priority, oldest first.
-   **One action, by cause:** **Top up** (an agent prepares it as a [person task](https://frazer.ai/docs/source/person-tasks) of kind `purchase`, one per organization and shortfall, never one per plan), **Use my key or plan**, **Raise the budget**, **Run now on my budget** (a person with spend authority admits one run) or **Wait** (the period resets).
-   **Never cheaper.** No model, lane, depth or turn count changes because money is short. A person may choose a lighter run explicitly; it is labeled “lighter than default by choice” on the plan and its approval page.

`fir plan loop funds 529` prints the reservation, the shortfall and the action.

## People’s own plans: suggestions

A person’s own plan gets a loop by default as **suggestions**: the loop’s author works on a suggested version beside the person’s draft and never edits theirs; the critic, the tester and the lanes run as for agents’ plans. It runs when the person says the draft is done (request approval, “check it”, the end of a planning session), and again only after their next batch of edits, never while they type. The Elevate tab shows suggestions, each with its diff, evidence and one key; **Take the safe ones** applies only mechanical and tightening edits, and items below their lane’s precision floor stay folded. Per person it is measured: past a 10 % not-useful rate over 30 days, the default turns to **offered** (“Improve until ready” one key away) and the person is told why. The same holds for people’s own pull requests: one review, then fixes prepared on a shadow copy and offered one key at a time, never pushed. One switch turns suggestions off for a person or an organization.

## Evolve mode: plans that keep improving after approval

An approved plan keeps a loop in **evolve mode**: new evidence starts an elevate-and-confirm run on the approved version, and what it proposes are amendments. Triggers: a proof fails on `main@verified` or a layer is reverted for its content; a deviation is recorded; an incident is attributed to a layer; a learned check in shadow would flag the plan; a premise reaches its revisit date; a sibling plan changes a contract this plan consumes; a new lane version would find more. A tightening amendment carries the approval; a material one waits for re-approval, while layers that do not touch the changed parts keep merging. At most one evolve run a plan a day (triggers are batched), within a monthly budget per plan; “Not run: no funds” applies.

```sh
fir plan loop 529 --evolve
fir plan elevate 529 --show
fir plan elevate 529 ask I5
```

## Questions at one person’s scale

At 10,000 to 50,000 pull requests a day, a person’s plans raise questions by the hundred. Five rules keep them inside the person’s one attention budget:

1.  **One question, many plans.** A question’s fingerprint is its cause key; one answer resolves it in every plan that asked, each at its own anchor.
2.  **Answered once, never asked again.** An answer becomes a standing rule, so the same cause is answered from it next time, with a citation.
3.  **Value first, inside the one budget.** Questions above the value line take their place among the person’s other rows by rank, grouped by topic and asked in one sitting, at the moment the branch they decide commits.
4.  **Always a person:** one-way doors, R3, money, identity, secrets, production data, security posture, personal data, legal and compliance commitments, customer-visible commitments, ownership and staffing, and anything a person’s principles say to ask about. Each is computed from facts Fir already has (path tags, data classes, owners, contracts). Past the budget, the overflow goes to other eligible people, and the header says how many and to whom.
5.  **Below the value line, a labeled assumption.** A two-way question on none of rule 4’s subjects is answered by a kept principle that applies, or by the panel’s recommendation when it is unanimous and calibrated, and recorded as an **assumption** with its source and one key to overturn it. An overturned assumption re-runs the plans that rested on it, and a source that is overturned more than 10 % of the time in 30 days stops supplying assumptions. Assumptions run in **shadow** first: the person answers without seeing the recommendation (a blind answer), and Wald’s sequential test decides each source at 95 % confidence, a 10 % overturn rate or more against 5 % or less, counted by person. A source that passes applies, and is audited the same way after; one that fails stops. Assumptions apply only to two-way questions on none of rule 4’s subjects.

`fir plan questions --mine --assumptions` lists them.

## Demo: loop lp-4c2e on pellwyn/plans#414 P18

Program `pellwyn/plans#120`, Oct 17. Phase P18 “Holds and reversals in the console” of plan `#414` “Admin console on the ledger” (squad admin-audit, accountable kenji) is drafted and R2: it calls the payouts API under the payouts contract `#463` and spans two deployables. The author is admin-audit’s drafting session `s-5c39`; the critic `s-7a10` and the tester `s-7a11` are of two other families (product UI, demo data).

| Time | Turn | What happened | Score |
| --- | --- | --- | --- |
| 06:12 | 1 | Stages 1 to 4: three errors. AC3 has no proof; P18.2 reads the state `reversed_partial`, which contract #463 v3 removed (`plan/contracts`); the items’ budgets sum to $310 over the phase’s $250 (`plan/cost`). The model stage is held: “saves $0.72” | 42 |
| 06:16 | — | v2: AC3’s proof `ReversalApprovalTest`; v3’s state `reversed` with `partial: true`; item P18.7 “Export holds to CSV” moved to P19 | 70 |
| 06:17 | 2 | Simulate: item P18.4 touches `services/ledger-api/**`, owned by ledger-core (an ownership gap from the dispatch dry run) | 62 |
| 06:24 | 2 | Model stage: **CQ1** (critic, pre-mortem) “If a hold is released while a reversal of the same payout is pending, which wins?”; **CQ2** (critic; only a person can settle it) “Who owns reversals of holds older than 90 days: admin-audit or payouts?”; **CV1** (critic, asked as a question while its class is uncalibrated) “Release decisions read the ledger replica: can a stale read release a hold twice?”; **TT1** (tester, reproduced) “On a branch of ledger-db with last week’s anonymized shape, 0.4 % of payouts have two open holds; the design assumes one”; **TT2** (tester, reproduced) “AC1’s proof reads `ledger_holds.age_s`, which does not exist at the base commit”. The author raises decision **D1** “May a support agent release a hold above $10,000 without a second approver?” for the approval | 50 |
| 06:29 | — | v3: CQ1 answered from contract #463 v3 (a release on a payout in `reversing` is refused); CV1 fixed (releases read the primary); TT1 fixed (several holds per payout) and its probe added as the check `plan/holds-per-payout`; TT2 fixed (age from `created_at`); the ownership gap fixed (P18.4 split, P18.4b for ledger-core); CQ2 sent to kenji | 84 |
| 06:30 | 3 | Regression: AC2 lost its unit (“p99 under 300”); the model stage is held again; v4 restores it at 06:31 | 80 → 84 |
| 06:36 | 4 | The verification pass resolves CQ1, CV1, TT1, TT2 and the gap; a fresh pass raises a Low question, answered with one design line. Paused: needs kenji | 84 |
| 06:52 | — | kenji answers CQ2 in Today: “payouts: reversals older than 90 days go through #405’s flow”; v6 hands item P18.6 to #405, and the scope shrinks | 95 |
| 06:59 | 5 | No new finding at Medium or above for the second turn; the final pass (a cold review by a fresh draw, standard depth with a pre-mortem) is clean | **97**, `done: ready` |

Five turns, six versions and 47 minutes, 16 of them waiting for kenji; the two versions that failed deterministic checks spent no model time. Open at the end: one suggested link to a runbook.

**At program scale**, a polish wave runs loops over a program’s drafted phases before its approval waves. On Oct 17 from 06:00 to 09:10 a wave ran 430 loops on program #120’s drafted phases: 420 converged and passed their final pass, 10 stopped at 8 turns, and the questions that only people could settle reached their owners batched, at most 5 per approval. The program views show a badge per plan (“loop · turn 3 · 2 open”) and the wave’s counts first, exceptions first.

## Learned checks: what plans teach Fir

What happens to plans after approval becomes new checks, required questions, calibration and weights: each proposed with its evidence, tested on history, watched in shadow, and approved by a person before it can block.

| Signal | Becomes, preferred first | Demo (acme-production) |
| --- | --- | --- |
| A closed plan that went badly, a layer reverted for its content, an incident traced to a layer | A CEL rule or a lint pattern that the plans that went badly break and the ones that went well keep | `learned/flag-rollback-proof`: “a plan that ships behind a flag names a rollback proof”, from 4 layers reverted in 90 days |
| A question asked after approval that a reader of the approved version could have asked | A required cold-review question for the plan’s type | “What happens to in-flight requests during the deploy?” for plans that change a service, from 5 such questions in 60 days |
| A person’s fix or answer in a loop or at approval | A check of the plan’s type, offered to its owners | — |
| Findings people dismissed or accepted | Calibration labels per class; a class whose not-useful rate reaches 10 % goes on probation | — |
| Closed plans’ outcomes and loops’ records | The score’s weights, refit each quarter; loop defaults per risk class | — |
| A check that caught nothing for 180 days and serves no control | Retirement | `acme/timeline-ordered` |

**How a learned check earns the right to block:**

1.  **Proposed with its evidence.** A deterministic miner finds the pattern (what the plans that went badly share that the others lack); a model only drafts the wording and the CEL, and the backtest tests them. Each proposal carries the failing case and the passing contrast it rests on; a proposal that rests on one case, or on several changes at once, waits for more evidence.
2.  **Backtested** over the scope’s closed plans and over a frozen reference set of plans with known flaws and known-good plans, chosen before the miner runs: which bad plans it would have flagged and which good plans it would have bothered, each with an interval (“6 of 7 bad plans flagged; 3 of 212 good plans bothered”). Never by whether later plans or loops score higher, because a check that passes everything can make every score look better.
3.  **Shadow for 14 days.** It runs, shows “learned · shadow” on the plans it would flag, and never blocks; people’s dismissals during the shadow count against it.
4.  **A person approves it** as a policy change, with the backtest attached: the type’s owners or the policy’s accountable person. Only then may it be advisory or block, at the strictness they choose.
5.  **Watched after.** Its catch rate and its not-useful rate are shown; at 10 % not useful it goes on probation (advisory); after 180 days with no catch it is proposed for retirement.
6.  **The learning lane is audited too.** Fir plants known-bad learned checks in shadow and confirms they are caught and retired, and a fact learned from one plan is checked again in the next instead of taken as given.

Settings › Plan checks › **Learned** lists every learned check with its source (the incidents, plans and findings it came from), its backtest, its state (shadow, advisory, blocking, probation, retired), who approved it and its catch rate since. A finding from a learned check says why the check exists: “learned from INC-0042 and 3 plans that went badly; tomasz approved it on Oct 10”. Learned checks run in the static stages wherever they can, so a model pass that used to find the same thing is not paid for again.

## The flow, with loops and learning

```text
request ─▶ triage ─▶ draft by the author ─▶ plan version ─▶ Frazer CI stages 1 to 5 (deterministic, milliseconds)
                         ▲                                              │ no error
                         │ fix, answer, contest, ask a person           ▼
                         └──── the loop: a critic and a tester ◀── the model stage (Frazer Reviewer)
                               of other families; Fir referees            │ a stop rule fires
                                                                          ▼
                               the final pass: a cold review, held out, zero context
                                                                          ▼
                     at most 5 questions only a person can settle ─▶ people approve the terms
                                                                          ▼
                               dispatch ─▶ build ─▶ review and verify ─▶ main@verified ─▶ release ─▶ outcomes
                                                                          ▼
                               learn: a miner proposes ─▶ backtest ─▶ shadow ─▶ a person approves ─▶ a new check
```

## Commands and tools

```sh
fir plan loop 414 --phase P18 --target 95 --budget 4
fir plan loop view lp-4c2e --turns --json
fir plan loop stop lp-4c2e --reason "<why>"
fir plan loop funds 529
fir plan loop defaults --class R2 --explain
fir plan elevate 529 --show
fir plan questions --mine --assumptions
fir plan learned --state shadow
fir plan learned --explain learned/flag-rollback-proof
frazer review loop docs/plans/payment-retry.md
```

| MCP tool | Does | Toolset |
| --- | --- | --- |
| `start_plan_loop` | Starts a loop on a plan draft, a phase or a file, with its program, target, budget and roles; returns a task handle | `records` |
| `get_plan_loop`, `list_plan_loops` | A loop’s turns (findings in, resolved, contested, held stages, score, cost, time), its stop reason and its final pass; loops by state for a plan, program or project | Default |
| `stop_plan_loop` | Stops a loop; people and the loop’s starter only | `records` |
| `get_elevation_report` | The elevation report: lanes and why each was chosen, coverage, dissent and cost; gaps with their sources; elevations with evidence; ideas folded; improvements with value, cost and resolution; the verdict line | Default |
| `resolve_improvement` | `fix` (the author inside its freedom, or a person), `ask` (raises a decision row), `accept_risk`, `out_of_scope`, `dismiss` (people; an agent for advisory items, with evidence) | `records` |
| `answer_loop_card` | Answers a card (two contests, a frozen block, “cannot be done”); people only | `records` |
| `list_learned_checks`, `explain_learned_check` | Learned checks with their source, backtest, state and catch rate | Default |

The API is `PlanLoopService` (`StartLoop`, `GetLoop`, `ListLoops`, `StopLoop`, `AnswerCard` for people) and `LearnedCheckService` (`ListLearned`, `ExplainLearned`, `ApproveLearned` for people, `RetireLearned`), both in `frazer.records.v1`; the `Loop` object is shared with Frazer Reviewer’s loops on code. Events: `records.plan.loop_started`, `.loop_turn_completed`, `.loop_paused`, `.loop_ended` (with the stop reason), `.loop_card_raised`, and `records.learned_check.proposed`, `.promoted`, `.retired`.

## Related

-   [The planning flow](https://frazer.ai/docs/source/planning-flow): the plan checks, the cold review and the score a loop works against
-   [Frazer Reviewer](https://frazer.ai/docs/reviewer#loops-for-code): the same loop on code and on any file
-   [Person tasks](https://frazer.ai/docs/source/person-tasks): what a loop pauses for
-   [What agents get](https://frazer.ai/docs/for-agents/what-agents-get): what an author, a critic and a tester rely on

## Pages in Fir

- [Overview](https://frazer.ai/docs/source.md)
- [Plan repositories](https://frazer.ai/docs/source/plans.md)
- [Record types](https://frazer.ai/docs/source/record-types.md)
- [Programs](https://frazer.ai/docs/source/programs.md)
- [Person tasks](https://frazer.ai/docs/source/person-tasks.md)
- [Decisions](https://frazer.ai/docs/source/decisions.md)
- [Decide by conversation](https://frazer.ai/docs/source/decisions/conversation.md)
- [The planning flow](https://frazer.ai/docs/source/planning-flow.md)
- [Loops and learned checks](https://frazer.ai/docs/source/planning-flow/loops.md) (this page)
- [Stacks and conflicts](https://frazer.ai/docs/source/stacks.md)
- [Auto-merge and the merge queue](https://frazer.ai/docs/source/merge-queue.md)
- [Sessions](https://frazer.ai/docs/sessions.md)
- [Issues](https://frazer.ai/docs/source/issues.md)
