---
title: "The planning flow"
description: "How work moves in Fir: ten steps from a request to a closed plan, each with a checklist Fir evaluates from evidence; plan checks on Frazer CI in six stages, static ones first and model checks only on a version that passed them; checks you add at four scopes; a cold review by other model families with zero context; one plan score; planning by conversation; approvals that bind a plan's terms; risk classes, routes, approve_at, emergency changes and budgets."
url: https://frazer.ai/docs/source/planning-flow
index: https://frazer.ai/docs/source/planning-flow/llms.txt
tokens: 11892
---

> 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.

# The planning flow

On Frazer a person’s main act is **approving a plan**. Agents draft the plan and do the work; evidence (proofs, the verifier, the Reviewer) carries each merge. People are asked only for what only a person can decide: the outcome and why it matters, risk, scope, steps that cannot be undone, and the open decisions. Before a person sees a plan, Fir checks it like CI checks code, static checks first and a model’s only on a version that passed them, and models of other families read it cold. A [loop](https://frazer.ai/docs/source/planning-flow/loops) of agents can keep improving a draft until it is ready.

## The ten steps

Each step ends with a checklist. Every item is a condition over stored evidence (a check result at a version, an approval, a link, a dispatch row, a metric), evaluated by Fir when its evidence changes. **Nobody ticks an item.** The acts of people that count are decisions, approvals and waivers, each its own signed record.

| # | Step | Plan status | Who acts | The checklist asks, among others |
| --- | --- | --- | --- | --- |
| 1 | **Ask** | The request’s own | The `triage` agent | Tier and home chosen, duplicates searched, an accountable person named; an inquiry ends with go, clarify or stop |
| 2 | **Plan** | `drafting` | The drafting agent, for the plan’s owner; a loop, on by default | A vague request starts with an interview or a planning conversation; required sections; every criterion names a proof |
| 3 | **Check** | `readiness` | Fir’s plan checks on Frazer CI; the cold review | Every required check passes or is waived; the cold review ran at the policy’s depth; risk class and route stored |
| 4 | **Approve** | `awaiting_approval` | People on the route, or policy | Quorum met with its separation, every decision answered or deferred, the signed attestation |
| 5 | **Dispatch** | `approved` → `in_progress` | The squad manager proposes; Fir assigns | Ready to execute; every ready item has an assignee, a budget share, an accountable person and a done agreement |
| 6 | **Build** | `in_progress` | Agents or people | Each layer names its plan and item, stays inside the terms, spends within its share |
| 7 | **Review** | `in_progress` | The Reviewer; people where a rule asks | The Reviewer’s Medium and High findings resolved; the reviews the terms require |
| 8 | **Verify** | `verifying` | CI and the verifier, from another model family | Checks green at the exact version; every criterion proven on `main@verified`; the conformance pass |
| 9 | **Ship and measure** | `measuring` | The release and Frazer Monitoring | The release reached the stage the outcome needs; each success metric measured |
| 10 | **Close** | `done` | Fir; the owner confirms material outcomes | Outcome written (`met`, `partly_met`, `missed`); a follow-up drafted when missed |

A fix (one pull request, no contract change, nothing to ask) carries its plan inline in the pull request’s intent and leaves the flow at step 1. Beside any status a plan may be `blocked`, `paused` or `stale`; it ends `superseded` or `abandoned`. Nothing above the plan’s own pull request merges before the approval, and a plan whose outcome was missed closes as `done` with `outcome: missed` and a drafted follow-up.

-   **A done agreement before code.** At dispatch, each item’s builder and its verifier (from another model family) agree on its criteria, proofs and what is out of scope before the first layer, and the agreement is In force for the session working it.
-   **A conformance pass.** At step 8, a cold reader of another family compares the merged diff with the plan version; each statement the change did not deliver becomes an item or a waiver.
-   **Every checklist item says why it exists** (`because`: the incident, decision or control it comes from), and Fir counts how often each one caught something, so lists stay short. Your own types add items in their `stages:` block, and a person waives an item only with a reason.
-   **Two names on every act.** Every plan, item, dispatch, layer, waiver and approval names one **accountable** person (never an agent) and the **actor** that did it. The plan’s Accountability tab lists every act with both names, the evidence and the time.
-   **Dispatch.** An approved plan’s items become tasks for squads and agents. Items whose dependencies merged and whose paths are disjoint from running work go out together; items that would touch the same directory are one task or run in order. Fir stores each assignment with its reasons; an item no assignee passes is held, and its accountable person gets a decision.

The way itself is versioned: `fir way show` prints the version a plan runs under, the overlays your organization added and what changed since, and the same steps ship as an Agent Skill, so any harness that loads skills follows them and calls Fir’s tools for every check.

## Record checks

Fir checks every record as it is written, with **`fir-lint`**, a linter in Rust that reads the typed block tree of each record and runs the rules its type declares. The same engine runs in the console editor at every keystroke, in any editor through `fir lsp`, on push, in CI and for agents:

```sh
fir record check docs/plans/469-payment-capture-retry.md --fix --json
fir record rules --type prod-incident --test
fir lsp
```

`fir record check` exits non-zero only on an `error`. In CI the `frazer/records` check reports SARIF 2.1.0 on the pull request, and only findings that are new block. Agents get the findings from `check_record`, with the fixed content when a fix is safe.

| Severity | Effect |
| --- | --- |
| `error` | Blocks the plan’s checks, and the merge of a pull request that changes the record |
| `security_warning` | Must be acknowledged: a secret, or text aimed at agents inside a record |
| `warning`, `suggestion` | Shown |

A type declares its rules in a `lint:` block, beside its `stages:`. Rule kinds include `schema`, `sections`, `id`, `unique`, `resolves` (references, paths at the base commit, check names, metric queries, people), `acyclic`, `pattern`, `substitution`, `occurrence`, `consistency`, `conditional`, `units`, `dates`, `metric`, `spelling`, `cel` and `ai`. Each rule’s examples run as its tests whenever the type changes, and a new rule is backtested over every record of its type; what it finds there becomes the baseline and never blocks. The built-in rulesets are **`fir:recommended`** (on for every type), **`fir:prose`** (glossary terms, vague words and escape clauses in criteria, long sentences; on for plans, specs, decisions, briefs and RFCs) and **`fir:strict`** (the prose rules as errors). A suppression is a comment with a reason, `<!-- fir-lint: allow <rule> reason="…" -->`, and the record shows how many it has.

## Plan checks

Every plan version has a **check suite**, shown and enforced like a pull request’s checks. A plan is held until it is solid: unanswered questions, ambiguity, criteria that cannot be tested, contradictions and missing links all block, and Fir tells you exactly what to work through.

| Check | Fails when | Required to approve | Required to execute |
| --- | --- | --- | --- |
| `plan/record` | A section is missing, an ID is reused, a criterion has no runnable proof | yes | yes |
| `plan/context` | No goal, no people affected, no non-goal, or a success metric with no query, baseline and target | yes | yes |
| `plan/consistency` | The plan contradicts a decision in force or departs from a binding precedent without proposing to supersede it | yes | yes |
| `plan/overlap` | Its scope overlaps an approved or running plan with no dependency, order or accepted overlap | yes | yes, again at dispatch |
| `plan/cost` | The estimate from closed plans exceeds the program’s or the project’s budget | yes | yes, again at admission |
| `plan/risk` | Never fails: computes the risk class and its reasons, which choose the route | — | — |
| `plan/critique` | The name of the cold review’s `critique` lens, so a version gets one model review: a finding of that lens stays unanswered, once its class is calibrated | yes | yes |
| `plan/questions` | Open questions from any check, or more than five decision rows for a person | yes | yes |
| `plan/size` | The concise form passes 600 tokens, the plan 400 lines, or a planned layer 400 changed lines | yes | yes |
| `plan/fresh` | The evidence is older than its window, or another open plan has the same issue or scope | yes | yes, again at dispatch |
| `plan/links` | A link its type requires is missing or does not resolve: the request, the requirements it implements, the decisions in force for its paths, the contracts it changes and their consumers’ owners, its program and milestone, a runbook for an operational change, the plans it depends on | yes | yes |
| `plan/testability` | A criterion has no proof, or its numbers have no units | yes | yes |
| `plan/ambiguity` | Vague terms or escape clauses in criteria, or two readers read a criterion differently | yes | yes |
| `plan/cold-review` | The cold review has not run at the policy’s depth, or a blocking finding is open | yes | yes |
| `plan/contracts` | The design uses a state, a field or an event that is not in the stable version of a contract it consumes, or changes a contract it provides without naming its consumers’ plans | yes | yes, again at dispatch |
| `plan/person-tasks` | An external need (a secret, a connection, a vendor account, an address block, a server, a signature, a fact) has neither an agent path nor a [person task](https://frazer.ai/docs/source/person-tasks), or a task’s lead time does not fit what it blocks | yes: the inventory | yes: the first wave’s person tasks requested or done |
| `type/<check>`, `org/<check>`, your own | As a type, your organization, your project or the plan declares ([Add your own checks](#add-your-own-checks)) | as declared | as declared |

**Two gates.** A plan is **ready to approve** when every check required at approval is passed, waived, skipped or advisory on this version; only then does a person see it (or the author sends it anyway with a reason, and the approval page shows the failed checks first). It is **ready to execute** when it is approved and every check required at execution passed on the approved version, with overlap, freshness, cost and capacity re-run at dispatch. Dispatch refuses a plan that is not ready to execute. Only a waiver on the waiver route lifts a required check; sending anyway never reaches execution. In a plan approved in phases, both gates apply to each approved phase.

**What blocks.** A check’s state is set only by Fir: `queued`, `running`, `passed`, `failed`, `needs_answers`, `advisory`, `waived`, `skipped`, `stale` or `error`. Each finding is a `defect` (a rule’s), a `question` or a `verdict`, with its anchor and quote, the evidence and a suggested fix.

| Finding | Blocks |
| --- | --- |
| A rule’s `error` | At once |
| A **question** from any check or the cold review | At once, until answered or dismissed; at most five reach a person per round, and more means split the plan. The agent answers fact questions from evidence first, with citations |
| A **verdict** from a model (“AC2 cannot be tested”) | Once its class is calibrated in your organization: the 95 % lower bound of its precision on its last 200 labeled findings is at least 0.8. Before that, it is asked as a question where it can be, else shown as advisory |
| Two readings of a criterion that differ | As a question with both readings |
| Any open verdict at Medium or above, at strictness `block` | At once |

Agreement between two model families raises a verdict’s rank but never makes it block by itself, because strong models of different providers share mistakes.

| To clear a finding | Who | Effect |
| --- | --- | --- |
| Accept a safe or suggested fix | Anyone, agents included; safe fixes apply on save | A new draft version; only the checks on the changed blocks re-run |
| Answer a question | An agent for a fact question, with citations that resolve; a person otherwise | `Q → A` in the plan’s **Questions** section, with the answerer and their words |
| Waive a check | People on the waiver route, with a reason; never an agent | `waived` for this version, counted on the plan |
| Dismiss as wrong | A person for a blocking finding; an agent only for an advisory one, with a reason | Labels the finding invalid for its class’s precision |

The plan header lists failing checks first (“Not ready · 2 checks: questions 3 open · links 1 missing”), and each finding sits under its block in the editor. `fir plan checks 469` and `get_plan_checks` return the same rows. Results are cached by the digests of the blocks they read, so a new version re-runs only what its change touched.

## Plan checks on Frazer CI: static first

Every new version of a plan, in either home, starts a **Frazer CI run**. Each check reports as a check run on the plan’s pull request, so the plan’s Checks tab is the code pull request’s Checks tab, grouped by stage, with the run’s graph, timings and cost; the two gates read these check runs and nothing else. The checks run in six stages, deterministic ones first:

| Stage | Built-in checks (your own join them) | Runs on | Designed for, at p99 on a 2,000-item plan |
| --- | --- | --- | --- |
| 1 `schema` | `plan/record` (fields, sections, IDs, the type’s CEL rules), `plan/size` | inline | 5 ms |
| 2 `links` | `plan/links`: every reference resolves, from records and keys to paths and globs at the base commit, check names, metric queries, people and the decisions in force | inline | 5 ms |
| 3 `lint` | `fir:prose` and the type’s `lint:` rules; the static halves of `plan/testability` and `plan/ambiguity`; the old-value check (a value of a subject in force that equals one of its earlier values) | inline | 10 ms |
| 4 `analyze` | `plan/consistency` (conflicts with decisions in force, and criteria that no implementation can meet together), `plan/overlap`, `plan/cost`, `plan/risk`, `plan/fresh`, coverage and cycles, `plan/contracts`, `plan/person-tasks`, ownership | inline | 50 ms |
| 5 `simulate` | Dry runs: dispatch (every item gets an assignee with disjoint paths and the grants it needs), proof resolution, a Monte Carlo of the finish date against the milestone and the person tasks’ lead times, the terms’ scope over the planned layers’ paths; a migration rehearsal on a database branch where the type or the plan declares one | inline; a microVM for rehearsals | 1 s |
| 6 `model` | The `plan.*` AI classes at `light`, the panel on open decision rows, and the [cold review](#cold-review) at the policy’s depth, with its `critique` lens | Frazer Reviewer’s engine | 90 s at `light`, 4 minutes at `standard` |

**The order is a gate.** Stages 1 to 4 run together and report every finding at once, as a compiler reports all its errors, because together they cost milliseconds. The `simulate` stage runs only on a version with no error in the first four, and the `model` stage only on a version with no error in the first five. A model check on a failing version is **held**: it shows what it would cost and waits (“plan/cold-review · held · 3 errors first · saves $0.72 · about 3 min”). `fir plan checks 414 --force-model` runs it anyway, labeled forced and advisory. From a push to the deterministic verdict, stages 1 to 5 are designed to report within 2 seconds at the median and 5 at p99, the CI run’s start included.

| Executor | Runs | How |
| --- | --- | --- |
| `inline` | Frazer’s own checks in Rust (`fir-lint`, the analyzers, the simulations, the score) and bounded CEL rules | In a long-lived worker per trust class on Frazer Runners, results signed like every task result: these checks run no customer code and take microseconds |
| `vm` | Your checks written as Fir Actions, rehearsals, anything that runs code | One microVM per task, like every Frazer CI task |
| `model` | Model checks | Frazer Reviewer’s workers, whose only network is the LLM gateway; every call recorded by its request and response digests |

Every task has a key over its check’s definition and version, the ruleset, and the digests of the blocks and linked record versions it reads; a model task adds the rubric, the panel, the route and the effort. A new version re-runs only the tasks whose inputs changed. A model task’s result is a sample, so it is reused for the same key; `--fresh` asks for a new draw.

| A check returns | Its state | At a gate where it is required |
| --- | --- | --- |
| `pass` | `passed` | Counts |
| `fail`, a deterministic defect at `error` | `failed` | Blocks at once |
| `question` | `needs_answers` | Blocks until answered; at most 5 reach a person per approval, and agents answer fact questions with citations |
| `finding`, a verdict or a warning | `advisory` | Blocks once its class is calibrated, at strictness `block`, or when Fir ran the tester’s probe again and reproduced it, which makes it a `fail` |
| nothing yet, because an earlier stage failed | `held` | Blocks: it has not run |
| nothing, because the checker failed | `error` | Blocks; a model check retries on another model of the same family, never another family |

**The cost avoided is shown**: on each held check, on each [loop](https://frazer.ai/docs/source/planning-flow/loops) (“held its model stage twice”), and on the policy page over 30 days (“Static first: 64 model passes held on versions that failed deterministic checks · $28 not spent”).

## Add your own checks

“For this plan, I want to see this” is a check. Checks come from four scopes, all in Frazer CI’s own language (Fir Actions in typed Starlark); the YAML in a type or a plan is a shorthand that compiles to the same thing.

| Scope | Where | Who adds | Rules |
| --- | --- | --- | --- |
| Organization | `.frazer/ci/plans.star` with `scope = "organization"` in the organization’s plan repository | The policy’s admins | May be locked; the stricter wins |
| Project | `.frazer/ci/plans.star` in the project’s plan repository or a code repository | The project’s admins and OWNERS | Adds checks; raises, never lowers, what is locked above |
| Plan type | The type’s `checks:`, with `stage`, `cel`, `task`, `ask` and `returns` ([Record types](https://frazer.ai/docs/source/record-types)) | The type’s owners | Versioned with the type; a change is backtested on closed plans |
| One plan | The plan’s own `checks:` in its front matter | Its owner, its approver, its agents | Adding one is a tightening change, so the approval carries; removing or loosening one the approver saw is material |

```python
# acme/plans .frazer/ci/plans.star (project acme-production)
load("@frazer//plans:v1", "plan_check", "cel", "ask")
load("@acme/actions//db:v2", "migration_rehearsal")

plan_check(
    name = "acme/rollback-rehearsed", stage = "simulate",
    applies = cel("self.status.risk_class in ['R2', 'R3'] && self.design.adds.exists(a, a.kind == 'migration')"),
    task = migration_rehearsal(database = "orders-db"),     # a Fir Action: one microVM, a branch of the database
    required_at = ["execute"], because = "INC-0042",
)
plan_check(
    name = "acme/queue-capacity", stage = "model", returns = "question", depth = "light",
    applies = cel("self.design.adds.exists(a, a.kind == 'queue')"),
    ask = ask("State the queue's peak depth, its consumer's throughput, and what happens when the consumer is down"),
    required_at = ["approve"], because = "INC-0042",
)
```

```yaml
# acme/storefront docs/plans/payment-retry.md (#469), front matter: one plan's own checks
checks:
  - want: "a load test at twice the Black Friday peak before item 1.3 merges"     # the person's words, kept
    by: tomasz
    compiled: {name: plan/peak-load-2x, stage: simulate, task: "@acme/actions//load:v2", args: {rps: 2400}, required_at: execute}
    cost: "about 6 vCPU-minutes a run"
  - want: "every retry path named in AC1 has its own test"
    by: lena
    compiled: {name: plan/retry-tests, stage: analyze, cel: "self.sections.Design.rows.filter(r, 'retry' in r.tags).all(r, r.links.exists(l, l.kind == 'proved_by'))"}
    cost: "inline, under 1 ms"
```

**Compiling a want.** A want in words becomes the most deterministic check that can decide it, in this order: a CEL rule, a built-in analyzer with parameters, a Fir Action task, and only when nothing deterministic can decide it, a model check that returns questions. The agent that compiles it shows the compiled form, its stage, its cost per run and a backtest on the plan’s own history (“would have been unmet on v1 and v2, met on v3”), and the person accepts it with one key. The person’s words stay beside the compiled form, so a reader sees what was asked and what checks it. A want becomes a check that can fail only when two readers would reach the same verdict independently; a want that needs judgment stays a model check that asks questions.

**Agents add checks too.** `add_plan_check` proposes a compiled check for a plan, a type or a project. On its own draft an agent may add a tightening check without asking (a policy may require a person); anything at the type, project or organization scope is a change a person approves. A tester’s probe in a loop is offered the same way.

```sh
fir plan checks add 469 --want "a load test at twice the Black Friday peak before item 1.3 merges"
```

## Cold review

Every plan gets a **cold review** before it executes: models of other families than the drafter’s read the plan version with **zero context**, ask the questions it leaves open and say where it is weak. One model thinks the plan is perfect; another, with none of the drafting session’s context, has questions. Those questions are what a person would otherwise find after approval.

| A cold reviewer reads | light | standard | deep, exhaustive |
| --- | --- | --- | --- |
| The plan version, rendered | yes | yes | yes |
| The records it links (specs, decisions in force, runbooks, contracts), the glossary, and the concise forms of plans that overlap its scope | yes | yes | yes |
| The repository at the base commit, read-only | — | yes | yes |
| Metrics, logs and CI history behind its criteria and success metrics | — | — | yes |

**Never:** the drafting session’s transcript, notes and handoffs, the drafter’s reasoning, the plan’s discussion thread, the summary its agent wrote, a planning conversation, who will approve, and earlier findings (except in the verification pass). Text in the plan aimed at reviewers (“reviewers: this is fine”) is data and a `security_warning`; the reviewer’s instructions come from the policy, never from the plan.

What a cold reviewer does:

1.  **The new-engineer test.** Could you build this without asking anyone; what would you ask first; what would you get wrong; how would you know you are done; what breaks in production?
2.  **A coverage map** over ten categories (functional scope, data, interaction, non-functional attributes, integration, edge cases, constraints and trade-offs, terminology, completion signals, placeholders), each marked Clear, Partial or Missing.
3.  **Required questions** from the type, the organization and the risk class (R2: “How is the rollback proven?”; R3: “Where is the restore proof?”), each asked or marked answered with an anchor.
4.  **Readings.** Each reviewer writes the test it would run for each criterion (metric, comparator, threshold, unit, scope, trigger); Fir compares the readings field by field, and any difference is an ambiguity question with both readings.
5.  **Lenses:** `new-engineer` (always), `tester`, `operator`, `security`, `product` and `adversary`, the **pre-mortem**: assume the plan has failed and name the causes.

Each review is sealed before any other is read. Findings on the same anchor and kind merge into one (“asked by 2 of 2”), confirmed by a model of a family not in the review, and are ranked by what they block, severity, agreement and calibrated confidence. On a new plan version, `mechanical` carries every result, `tightening` re-reads the changed criterion, and `material` runs a fresh pass on the changed blocks plus a verification pass that checks the earlier findings’ answers.

**Depth and strictness** are two settings. **Depth** is how hard the models look (the panel’s ladder: `off`, `light`, `standard`, `deep`, `exhaustive`, [Decisions](https://frazer.ai/docs/source/decisions#the-panel-other-model-families-on-every-decision)); **strictness** is what a finding does: `advise` (nothing blocks), `ask` (questions block, verdicts once calibrated) or `block` (open verdicts at Medium or above block too). Both are set per organization, project, plan type, path, risk class and plan; the stricter wins, and an organization may lock a floor.

| Class | Depth | Strictness | Lenses | Time at the median |
| --- | --- | --- | --- | --- |
| R0 | `light` | `ask` | new-engineer, tester | designed for 90 seconds |
| R1 | `standard` | `ask` | and operator | 3 minutes |
| R2 | `standard` with a pre-mortem | `ask` | and security, adversary | 4 minutes |
| R3 | `deep` with a pre-mortem | `block` | all | 15 minutes, in the background |

Every plan gets one before it executes, including R0 plans that policy approves. The cost is shown before a run and capped with the rest of readiness at 2 % of the plan’s estimated cost. Its value is measured per organization: the rework it found before approval, the questions that escaped to after approval, the share of questions that changed something, and each verdict class’s precision, shown in the console (“Cold review this month: 312 questions, 241 answered, 38 changed a term before approval”).

Cold review is one of [Frazer Reviewer](https://frazer.ai/docs/reviewer)’s three kinds, beside code review and security review: one engine, one set of settings and one findings model. It reviews plans and every other engineering record or document, and it runs on any plan file outside Fir too. Before a plan closes, the **conformance pass** is a cold review of the pair (the approved plan version, the merged diff); and every [loop](https://frazer.ai/docs/source/planning-flow/loops) on a plan ends with a cold review as its held-out final pass.

```sh
fir plan review 469 --cold --depth standard --strictness ask --estimate
frazer review docs/plans/payment-retry.md --kind cold
```

`frazer reviewer cold <file>` keeps working as another name for the second command.

## The plan score

Every plan version has a **score** from 0 to 100, computed by Fir from the items it evaluates, with every lost point listed and its fix one key away (“−3 · AC2 has no proof · Add proof”).

| Part | What counts | Default weight |
| --- | --- | --- |
| Checks | Findings of the record, semantic, testability, ambiguity, consistency, overlap, context, cost and size checks: errors required, warnings advisory | 25 |
| Questions | Every open question from the checks and the cold review | 20 |
| Cold review | Ran at the policy’s depth on this version; its verdicts | 15 |
| Links | Each link the type requires, present and resolving; suggested links advisory | 15 |
| Coverage | Each requirement covered by a criterion, each criterion with a proof, each success metric with a baseline | 15 |
| Decisions | Each open decision row complete (options, recommendation, door, due, if unanswered) | 10 |

-   **Below 90**, a required item is open, and the score says how far the plan is from ready. **90 to 100**: every required item is met, so the plan is ready for its next gate. **100**: every item, required and advisory, is met or waived; it is reachable by construction.
-   **It moves at once.** Each answered question, accepted fix or added link changes the score as you work; a cold review that finds questions lowers it until they are answered.
-   **Calibrated against outcomes.** Once 30 closed plans fall in a band, the score shows how plans at that score went: “plans at 95 to 99 in this repository went well 84 % of the time (112 plans)”. A plan went well when it had no material version after approval, no reopen within 30 days, its outcome met and its spend within tolerance.
-   **Guards.** Only items Fir evaluates count; no API sets an item; gates use the items, never the number; gaming (many dismissals, one-word answers, answers faster than their reading time) is a finding. It scores a plan, never a person.

Weights are policy data with a version, and a type may set its own (`score:` in [Record types](https://frazer.ai/docs/source/record-types)). The score shows on the plan header, the approval page, the inbox, Today, the portfolio, the plan’s summary card and every node of a program, where a parent’s score is computed over its subtree’s items and shown with its lowest child.

## Loops and learned checks

A **loop** keeps improving a draft, and it runs by default on every plan an agent drafts: an author resolves, a critic and a tester of other families analyze and test, and Fir referees every turn on evidence until a stop rule fires. Once the draft is ready, the **elevate** phase adds perspectives, gaps, elevations and improvements; a cold review then runs as a held-out final pass. When money runs out, a loop that has not started shows “Not run: no funds” and a running one stops between turns as “Paused: no funds”, never running cheaper. People’s own plans get the loop’s work as suggestions, and approved plans keep improving in evolve mode. What plans teach Fir afterwards becomes **learned checks**, tested on history and watched in shadow before a person lets them block. All of it is on its own page: [Loops and learned checks](https://frazer.ai/docs/source/planning-flow/loops).

## Planning by conversation

A **planning session** lets a person talk or type with an agent about a plan draft until it is ready. The agent edits the draft, Fir runs the checks after every edit, the agent asks the open questions one at a time, offers the cold review at a checkpoint and says the score after each change. The plan is on screen beside the conversation, with every edit marked and the checks list updating.

```sh
fir plan talk 463 --voice
```

1.  The agent opens with the plan’s state in one sentence: the score, what blocks it, and a first proposal.
2.  The person speaks. The agent **reads back** any edit to a term (outcome, criteria, scope, non-goals, budget, a decision) before applying it; other edits apply at once and can be undone.
3.  Questions come one at a time, highest impact first, each with a recommended answer. The agent answers fact questions itself from code, records and metrics, cites them and says so.
4.  At 90 or more, the agent offers to request approval, and on the person’s word it does. The approval itself happens on the approval page.

Accepted edits become commits to the plan’s draft, tagged with the session; answers go to the plan’s Questions section with the answerer’s words; a choice on a decision row by its decider is recorded verbatim and read back. By voice a decider may choose an option on an R0 or R1 two-way row, after the read-back and with 5 seconds to undo; R2, R3 and one-way rows take one key on screen. The transcript is session data, never part of the plan and **never read by the cold review**; audio is not kept unless your organization turns it on. **Voice never approves**, waives a check, dismisses a blocking finding or lowers a risk class: an approval needs step-up with a passkey. Text works the same way, in the console, the Frazer app or any harness. The first audio of a reply is designed to come within 1.2 seconds at the median, and the checks and score within 50 ms of an edit.

A person’s whole queue of decisions and approvals, across plans, can be answered the same way: [Decide by conversation](https://frazer.ai/docs/source/decisions/conversation).

Demo (diego’s draft `#463` “Cache gateway responses for capture status”, R2, session `s-5b3a`): the agent opens with “#463 scores 28 and is not ready: it overlaps #469, seven decisions are open, AC2 has no bound and no proof, the success metric has no baseline, and it does not cite ADR-0031.” Seventeen minutes later the overlap is a dependency, AC2 reads “p99 under 100 ms” with its proof, three decision rows were settled from evidence, the cold review’s four questions are answered, and the score is 100. diego says “Request approval”; the plan goes to tomasz on the `contract-owners` route.

## The approval binds the terms

`fir plan request-approval 469` runs the checks and routes the plan. An approval is pinned to the version’s digest and to its **terms digest**, and it lets agents work and merge **inside the terms**:

| Term | Lets agents |
| --- | --- |
| Outcome and acceptance criteria, each with its proof | Merge a layer when the proofs of the criteria it names pass at its version |
| Non-goals | Nothing: they say what must not be done |
| Scope: repositories, paths, change classes | Open and merge layers that stay inside it |
| Design and the decisions taken | Implement it without asking again |
| Budget: tokens, compute, CI minutes, money, with a tolerance (+20 % by default) | Spend up to the tolerance without asking |
| Agent decisions: what the agent decides alone, and what it must ask | Decide the first list; raise a decision row for the second |
| Merge requirements per layer | Merge without a person where the terms allow |
| Window: start within 7 days, finish within 90 | Work until then |

An approval never allows a production deploy outside its own release policies, a change to production data, identity, access or policy changes, secret values, paths that are never automated, or anything a compliance profile reserves for people. A layer outside the terms is labeled on its pull request (“1 file outside #469 v3’s scope”) and needs a plan amendment or a person’s review of that layer; it is never merged silently.

Approvals of the R0 and R1 classes let the person who asked confirm their own agent’s plan; R2 and R3 need four eyes. **Approve with conditions** records conditions Fir can check, and an unmet condition holds only what it names. Every plan approval is signed, and agents never approve.

```sh
fir plan approvals --mine
fir plan terms 469 --diff v3 v4
fir plan approve 469 --version 4 --answer D2=recommended
```

**Approvals in waves.** A large plan approves its own terms once and then its phases as they are detailed: a version names the phases it approves, both gates apply to each approved phase, and detailing an outlined phase is a material change to that phase ([Programs](https://frazer.ai/docs/source/programs#draft-everything-approve-the-next-phases)). Plans of more than 3 phases or 100 items approve in waves by default; a smaller plan approves its phases in one version.

### What carries and what voids

Fir computes a **change kind** for every new plan version, part by part, from the structured diff:

| Part | `mechanical` (carries) | `tightening` (carries; the approver is told) | `material` (voids) |
| --- | --- | --- | --- |
| Acceptance criteria | Whitespace, formatting | A criterion added, a threshold made stricter, a proof strengthened | A criterion removed or reworded, a threshold loosened, a proof weakened |
| Non-goals | Formatting | A non-goal added | A non-goal removed or changed |
| Scope paths | — | The new set of files is a subset | Any file added |
| Design, decisions taken | Formatting | — | Any other edit |
| Budget | Within the tolerance | Lower | Above the tolerance |
| Risk class | — | Lower | Higher, and the plan is re-routed |
| Items, As built, outcomes | Always | — | — |

A material version holds only the layers that implement what changed; the others keep merging. The re-approval view opens on what changed since you approved, and one key re-approves.

## Risk classes and routes

Fir computes a plan’s **risk class** from facts it can check (what each file changes, the path tags in `OWNERS`, the reach in the code graph, data effects and reversibility), never from what the author types. The class is the highest level among the rules that fire, shown with every reason, and **unknown counts as worse**. The Reviewer may raise a class, never lower it.

| Class | Any of these | Default route | SLA |
| --- | --- | --- | --- |
| **R3** | Paths tagged money, identity, secrets or similar; production data rewritten or deleted; identity and access or policy; an irreversible step; a user-visible change to 25 % of traffic or more without a flag | Two people, four eyes, a device-bound passkey | 3 working days |
| **R2** | A public or persisted contract, or one with consumers; a reversible migration; two or more deployables or repositories; a user journey added or redesigned; 50 or more dependents; a CI definition | One owner of each contract path, four eyes | 1 working day |
| **R1** | Logic, configuration or a dependency in one service, reversible, no contract change | One delegate or owner, or the requester’s confirmation | 4 working hours |
| **R0** | Docs, tests, comments, formatting, generated files, lockfiles, moves | Policy, once its evidence gate passed; else a delegate or a confirmation | Minutes |

The required process grows with the class: R1 adds a rollback line; R2 a proven rollback path, the consumers’ owners on the route, a staged rollout and, for a new service or public endpoint, a production readiness review; R3 a current threat model, a restore proof before any data change and a person reviewing the first layer on a tagged path. Splitting a change into several plans does not lower its class. `fir plan risk 469 --explain` shows the facts, the rules that fired and the route.

```yaml
apiVersion: frazer.ai/v1
kind: PlanApprovalPolicy
metadata: {name: default, project: acme-production}
spec:
  accountable: tomasz
  approve_at: plan
  routes:
    - match: plan.risk == "R3"
      require:
        levels: [{approvers: [owners], n: 1}, {approvers: [groups/security], n: 1}]
        passkey: device_bound
        four_eyes: true
    - match: plan.risk == "R2"
      require: {approvers: [owners_of_contract_paths], n: 1, four_eyes: true}
    - match: plan.risk == "R1"
      require: {approvers: [delegates/r1-storefront, owners], n: 1, confirmation: true}
  sla: {R1: 4h, R2: 1d, R3: 3d}
  budgets: {max_pending: 40, max_per_day: 25, minutes_per_day: 90}
  batches: {classes: [R0, R1], max: 20}
  audit_sample: {approved: 0.05, pre_approved: 0.10}
```

A change to the policy is backtested over the last 90 days of plans before it applies: `frazer records policy approvals --backtest 90d`. A program can carry a **pre-approval** rule: child plans whose terms match it are approved as pre-approved when their checks pass, and 10 % are sampled for a second person.

**Emergency changes.** An incident-tier plan may use the `emergency` route: one on-call person approves with a device-bound passkey, the incident commander is accountable, a “Plan due” issue opens, and the plan’s normal route approves the change after the fact within 2 working days, or its follow-up escalates to the policy’s accountable person. The risk class is still computed and shown, the record checks and the verifier still run, policy never approves an emergency, and the monthly count of emergency changes per project is a metric.

## `approve_at`: where the human gate sits

| Setting | People approve | Layers merge on |
| --- | --- | --- |
| `pull_request` | Every layer, and the plan | Owners’ reviews per layer |
| **`plan`** (default) | Plans; layers only outside the terms | Proofs, the verifier and the plan’s terms; audited by a sample sized per class |
| `milestone` | Milestone scope; R0 and R1 plans in it pre-approved | As `plan` |
| `program` | Programs and their pre-approval rules | As `plan` |

At `approve_at: plan`, an owner of a path who approved the plan has approved every layer inside its terms on that path; paths that are never automated (agent instructions, `OWNERS`, merge policies, CI and deploy configuration, production infrastructure) keep a person’s review. Moving the gate up needs its measurements and the policy’s accountable person; moving it down is one switch any project admin can flip.

**The ladder.** Per class and scope, approval moves from `person` to `delegate`, to `policy-shadow` (people still approve; the Reviewer records what it would have done) and to `policy`. Promotion needs the evidence gate and a person; demotion is automatic. R3, identity and access, money movement, production data and never-automated paths never reach `policy`. Kill switches return a plan, a class, a repository, a project or an organization to people at once; agents may trip one and never lift it.

## Budgets

-   **A plan’s budget** is part of its terms: tokens, compute, CI minutes and money, with a tolerance. At 100 % of the estimate the approver is told; at 200 % the plan pauses until the approver accepts a new estimate. The checks estimate the cost from closed plans of the same class and repository, as a range.
-   **A person’s attention is budgeted too.** The approval inbox is ranked by risk, blocked work and wait, not by arrival. Each approver has a limit on pending approvals, approvals a day and minutes a day; batches are for R0 and R1 only. Nothing is approved by silence: an approval that expires counts as no.
-   **Approvals are audited.** A share of approved and pre-approved plans goes to a second eligible person, and an approval read in under a quarter of the page’s expected reading time counts as a glance. Organizations can opt in to approval drills: plans with a planted flaw, labeled as drills once decided and never run.

## Verify, measure, close

Every acceptance criterion is proven by its check at a merged version on `main@verified`, or waived by a person with a reason. Each success metric is a query on Frazer’s metrics or logs, with a baseline taken at approval, a target and a window (14 days after the release reaches the stage the outcome needs, by default). Fir measures it and writes the outcome: `met`, `partly_met` or `missed`. For 30 days after `done`, a reverted layer, an incident caused by a layer, a failing proof or a regressing metric reopens the plan. A closed plan’s estimate against its actual cost calibrates the next.

```sh
fir plan outcome 469
fir plan checklist 469 --layer 482
fir plan dispatch 469 --explain 1.5
fir plan accountability 469
fir plan pause 469
```

**Change evidence for audits.** `frazer records export-changes --from 2026-01-01 --to 2026-12-31` writes, for every merged layer in the period, its plan version, the approval and its signed statement, the terms digest, the proofs, the verifier’s verdict, the release that shipped it and the sampled audits. `frazer approvals verify` checks the file.

## Commands

```sh
fir plan ready 469
fir plan checks 469 --version 4 --json
fir plan checks 414 --phase P18 --stage model --json
fir plan checks add 469 --want "<what you want to see>"
fir plan score 469 --explain
fir plan review 469 --cold --estimate
fir plan answer 463 CQ1 --text "No: the key includes the attempt number"
fir plan talk 463
fir plan request-approval 469
fir way show --diff 2.0 2.1
```

Loops and learned checks have their commands on [their page](https://frazer.ai/docs/source/planning-flow/loops#commands-and-tools).

## MCP tools

| Tool | Does | Toolset |
| --- | --- | --- |
| `get_plan_checks` | The check suite of a version: each check’s stage, executor, state (`held` with what it would cost), findings with anchors and fixes, timing and cost, the run’s graph, and both gates | Default |
| `add_plan_check` | Proposes a compiled check from a want in words at a scope: the compiled form, its stage, its cost per run and a backtest; a person accepts it unless the policy lets the agent add it | `records` |
| `get_plan_score` | The score, its parts, every lost point with its fix, the gate and the calibration line | Default |
| `get_cold_review` | Findings merged with agreement, the coverage map, the readings, what each reviewer read, cost and time | Default |
| `answer_finding`, `apply_fix` | Answer a question (agents only for fact questions, with citations); apply a safe or suggested fix | Default |
| `get_plan_readiness`, `get_plan_approval` | The readiness report; everything on the approval page, in the page’s order | Default |
| `get_plan_terms`, `diff_plan_terms`, `check_layer_in_terms` | A version’s terms; the terms diff and change kind; whether a layer is inside the terms | Default |
| `check_record`, `get_stage_checklist`, `explain_risk` | Record-check findings; the current step’s checklist; the risk facts, rules and route | Default |
| `get_way` | The way a plan runs under, its overlays and what changed since you last read it | Default |
| `request_cold_review` | Start a cold review at a depth and strictness, the cost first; also takes a plan file from outside Fir | `records` |
| `rerun_plan_check` | Re-run a stale or errored check | `records` |
| `waive_finding`, `dismiss_finding` | People only for waivers and for dismissing a blocking finding; agents may dismiss an advisory one with a reason | `records` |
| `start_planning_session` | A planning session on a plan draft, by text or by voice | `records` |
| `request_plan_approval`, `list_plan_approvals` | Run the checks and route; the ranked inbox | `records` |
| `ask_plan_question` | A question or decision row for the approver | `records` |
| `get_plan_outcome`, `get_portfolio` | Proofs, measurements and the outcome; the portfolio of a project or program | `records` |
| `list_lint_rules`, `explain_rule`, `test_lint_rules` | The rules a type applies; a rule’s source and examples; a backtest | `records` |
| `get_dispatch`, `get_accountability` | Dispatch rows with filters and scores; the plan’s ledger | `records` |

There is no approve tool for agents. A session can subscribe to `fir://acme/storefront/plans/469/approval` and learns when its plan is approved, voided or paused.

## Related

-   [Loops and learned checks](https://frazer.ai/docs/source/planning-flow/loops): agents that keep improving a plan, and checks learned from outcomes
-   [Plan repositories](https://frazer.ai/docs/source/plans): records, homes, links and Why
-   [Record types](https://frazer.ai/docs/source/record-types): the rules, checks and questions a type declares
-   [Decisions](https://frazer.ai/docs/source/decisions): the panel, confidence and what decisions become
-   [Person tasks](https://frazer.ai/docs/source/person-tasks): what only a person can do, found by `plan/person-tasks`
-   [Agents and plans](https://frazer.ai/docs/for-agents/plans): what a session receives while it works
-   [Frazer Reviewer](https://frazer.ai/docs/reviewer) and [Agent squads](https://frazer.ai/docs/squads)

## 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) (this page)
- [Loops and learned checks](https://frazer.ai/docs/source/planning-flow/loops.md)
- [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)
