# Changelog

## 0.33.2

No behavior change. Finishes the cleanup begun in 0.33.1: every remaining local machine path, host
name and private provider name in the docs, notes, formal run logs, scripts, tests and fixtures is
replaced by a neutral example (`/work/...`, `bench`, generic provider names). Recorded fixture ledgers
whose bytes changed have their hash chains recomputed. Plans and ledgers from 0.33.0 replay unchanged.

## 0.33.1

No behavior change. Removed local machine paths and private provider names from the docs, notes,
formal run logs and test fixtures; examples now use `/work/...` and generic provider names. Plans and
ledgers from 0.33.0 replay unchanged.

## 0.33.0

Plans and ledgers from 0.32.0 replay unchanged. New, optional ledger kinds
`stage`, `accept`, `reject` and `overturn`, an optional `via` field on entries
recorded by a fork's A act, and an optional plan mapping `drive.delegate` are
added; no existing kind, field or enum changes, and a plan without
`drive.delegate` behaves as in 0.32.0. Readers older than 0.33.0 refuse a plan
that sets `drive.delegate`, so upgrade every reader before recording one, and
before anyone records a stage. Per-version snapshots rebuild on first use.
Restart upgraded drivers and pi sessions to use the new runtime behavior.
Delegate forks need pi-durable-subagents >= 1.3.0 (`run --origin-session`,
feature-detected once per binary); with an older dsa every wake is delivered as
in 0.32.0. Net source growth since 0.32.0 (`ba0e896`): 15 files under `src/`,
1677 insertions and 122 deletions (+1555 lines).

- **Transient dsa reads never halt (HT).** Read-only dsa calls (describe,
  status, leases, the `--help` feature probe) made by the driver are retried
  up to three more times after 1 s, 3 s and 9 s when they time out or end by a
  signal. A read that still fails logs one driver line
  `dsa read unavailable: <call>: <reason>` and is decided again on a later
  pass; only a read failing continuously for 10 minutes halts the node with
  `dsa reads failing for 10 min: <call>: <last error>`. The read timeout is
  15000 ms (was 2000 ms for the attest status poll), overridable with
  `OWED_DSA_SHORT_MS` (integer 1000-120000; other values are ignored with one
  warning line). Write calls keep their timeouts. An attest error whose
  measurements are already in the ledger continues from them and logs
  `attest result taken from ledger #<seqs>` instead of halting. Before a halt or
  repair caused by a writer call, the driver checks the call's generation: a
  newer running or queued generation suppresses both
  (`waiting: <call> generation <n> is running`), and an older halt is shown in
  status and `owed why` as `stale halt: generation <n> is running (<call>)`.
- **Stages and acceptance (SG).** `owed stage <node> --file BUNDLE.json`
  (tool `owed_stage`) records a validated decision bundle: ruling text,
  optional decide parameters, optional plan patch with before/after sha256,
  optional appends and evidence, `read_through_seq`, touches, class A/B/C and
  a summary. Anyone may stage, including subagent calls. `owed accept S<n>
  [--amend TEXT]` (tool `owed_accept`, parent or owner) re-checks the stage,
  refuses a stale one (`stage S<n> is stale: … — fork again`) or one that
  overlaps an older open stage, then applies the plan patch, the decision
  (ruling suffixed ` [stage S<n>]`), the `accept` entry and the appends in one
  step; a retry after a crash is safe. `owed accept --all-b` accepts the open
  class-B stages in order and stops at the first refusal. `owed reject`,
  `owed stages [--all] [--json]` and `owed overturn` complete the set; status
  adds `Stages: <n> open (<k> stale) → owed stages` and `owed why` lists a
  node's open stages.
- **Fork principal and A acts.** A dsa call labelled `owed_fork=<node>` is a
  fork of that node. It may stage for any node and, only when the plan's
  `drive.delegate.a_acts` enables them, perform two acts on its own node:
  `preferred-answer` and `infra-resume`. They are recorded as the owner act
  they are with `via: "fork:<call>"`. Every other parent or owner act stays
  refused, with a message naming `owed stage`. `owed overturn <seq> --note`
  disables that A act kind until a later plan lists it again.
- **Wakes go to exact forks (DL).** When the plan's `drive.delegate.on` lists
  a wake's kind (`question`, `writer-stop`, `review-asks`, `infra-halt`,
  `needs-owner`), the pi session that follows the driver launches one exact
  fork of itself for the node (`dsa run --origin-session`, agent `delegate`,
  `context: "exact"`) instead of taking the wake as a turn. At most `max` forks
  run at once and later wakes queue in order. The fork stages and stops; one
  batched `owed delegate:` message per settle lists each stage with its
  `owed accept S<n>` command, or the original wake text when a fork ended
  without a stage. A node has at most one fork, and is not forked again while
  a stage made by its fork is open (not accepted and not rejected); such a
  wake is delivered as a normal turn. Wakes are never lost: a launch refusal
  delivers the original wake at once, and forks pending at session quit are
  delivered as their original wakes on the next follow.
- **Full questions, retried refusals, recorded plan hash (UX).**
  `owed question <node> [qid] [--json]` prints a node's open writer questions
  unclipped; wakes and `owed why` lines that clip a question point to it. The
  recording pi tools (decide, answer, resume, plan, edit, amend, rule, stage,
  accept) retry once after an optimistic-concurrency refusal and say
  `retried once after: <first refusal>`. `owed_plan`, `owed_edit` and
  `owed_amend` end with `recorded plan #<seq> blob <sha> file sha256 <sha>`;
  `owed plan` now refuses (`plan file changed; retry`) when the plan file
  changed between the read and the ledger lock, so an older plan version is no
  longer recorded.

## 0.32.0

Plans and ledger formats are unchanged from 0.31.2; no new ledger kind,
field or enum is added. Per-version snapshots rebuild on first use of 0.32.0.
Restart upgraded drivers and pi sessions to use the new runtime behavior.

- **Bounded tool results (TB).** Status calls had grown session files by
  megabytes per call. Every owed pi tool now bounds content to 48 KiB UTF-8
  and details JSON to 8 KiB. Cut text keeps the outcome first and ends with
  an omitted-byte count and a pointer to the full read-only CLI form when
  a valid target is available. Details are compact summaries, not full state
  or follow-up bodies. The successful `owed_dispatch` packet, its args and
  its submit line come first byte-exact and are exempt; only the trailing
  status is bounded. CLI output and `--json` are unchanged.
- **Visible attest (MV).** With an owner pi session and a dsa that supports
  command runs with `--session`, driver attests run as command calls named
  `owed attest <node>`, visible in `/subagents` and dsa restart refusals.
  Request ids are deterministic; live calls attach and completed results
  are reused, with bounded deterministic retry ids for old non-results.
  Exit 0 remains accepted, 1 measured failure and 4 obligations outstanding;
  75 means busy and retries on a later pass. Without the required session
  or capability, or after an unsupported launch refusal, the driver falls
  back to the legacy path with one explanatory driver line. Merge measurement
  stays in-process.
- **Quiet parking (PK).** `owed decide` and `owed answer` now accept
  `--expect report` (tools: `expect: "report"`) to park the writer as
  `owed resume --expect report` does: report findings, wake once, then remain
  quiet until a relevant parent act. `--no-resume --expect report` refuses.
  Writer packets say not to ask for confirmation of stop, pause or wait
  rulings. The finished-ruling-follow-up halt names the parking form.

Source grew by 374 lines net across 12 TypeScript files: 428 insertions and
54 deletions since 0.31.2.

## 0.31.2

Automatic driver notifications now go only to the owning pi session. Opening
another session in the same repository, querying status or why, receiving a
refusal, or reusing another session's driver does not subscribe that session
or transfer ownership. Matching owners still discover live drivers across
repositories and reconnect when resuming the same session.

Plans and ledger formats are unchanged from 0.31.1; no new ledger kind,
field or enum is added. CLI `DSA_SESSION`-first identity semantics and dsa
requirements are unchanged: pi-durable-subagents >= 1.0.27 for the driver,
>= 1.1.0 for durable-writer waits and lease options, and >= 1.2.0 for
structured asks. Per-version snapshots rebuild on first use of 0.31.2;
old snapshot bytes are not reused. Restart upgraded drivers and pi sessions
for the notification routing behavior.

- **Pi starts use the current session.** Tool starts carry the validated pi
  context identity into the driver launch. When context identity is unavailable,
  they fall back to validated `DSA_SESSION`, then validated `PI_SESSION_ID`.
  Missing or unknown ownership never broadcasts; sessions inside dsa calls
  never attach automatically. A reused driver keeps its original owner.
- **Queued notifications stay with their session.** Changing context identity
  discards the previous session's followers and queued wakes. An automatic
  follower does not reconnect to a replacement owned by another session or
  read its log; it preserves the owned driver's terminal notice. Busy buffering,
  delivery revalidation and per-follower deduplication remain.
- **Idle reminders respect ownership.** A session can retain idle observation
  after its owned driver exits; a successful explicit `owed_init` may also arm
  that session's idle watch. Opening the repository or querying owed does not
  take over an absent owner.
- **Explicit observation remains available.** `owed watch` is an explicit CLI
  opt-in independent of automatic pi attachment and does not transfer ownership.
  Its cursor and timeout semantics are unchanged. Same-session attachment can
  still replay current wakes from complete lines in the last 256 KiB of the
  drive log. Historical `Done:` labeling, replay boundary handling and memory
  behavior are unchanged by this release.

Source grew by 74 lines net across 7 TypeScript files: 134 insertions and
60 deletions since 0.31.1.

## 0.31.1

Plans and ledgers from 0.31.0 replay unchanged; snapshot caches rebuild on first use;
no new ledger kinds or fields are added. `owed check try --record` writes an
ordinary `note` entry. Snapshot format and validation are unchanged, but the
old `snapshot/state.v8` is no longer read, costing one cold replay on first
use. The dsa requirements stay as in 0.31.0: pi-durable-subagents >= 1.0.27
for the driver, >= 1.1.0 for durable-writer waits and lease options, and
>= 1.2.0 for structured asks.

- **Missing candidate modules do not make trunk red.** CJS and ESM
  `Cannot find module '<path>'` diagnostics now qualify for
  `probe not applicable: trunk lacks <path>, which the candidate adds`.
  The path must resolve inside the probe checkout to a candidate addition
  absent from trunk; bare module names without `/` do not qualify. The probe
  records `error`, and the failed merge check still refuses the merge.
- **Different unnamed failures keep the node's own refusal.** When neither
  the merge failure nor its trunk probe names failed tests, owed compares
  their last error-like lines, falling back to the last non-empty line.
  Measurement checkout paths become one token and whitespace is folded.
  Different lines produce `probe inconclusive: trunk fails differently (<line>)`
  with an `error` verdict; the merge keeps its `merge failure output:` detail.
  Equal or missing lines, or unreadable logs, keep the previous classification.
- **Merge-tree advice checks which files trunk changed.** The lead remains
  `merge tree does not pass <inv>[, ...] (trunk passes)`. When paths overlap it
  continues `; trunk moved <N> commits since the slot base and changed files this node changes (<paths>): rebase → owed rebase <node>`
  (at most two paths, then `+<remaining>`). Without overlap it continues
  `; trunk moved <N> commits since the slot base but changed none of the files this node changes, so the node's own change is the likely cause: owed decide <node> --writer --text "<repair instruction>" (rebase → owed rebase <node> only reproduces it on the current trunk)`.
  If Git cannot compute the paths, the existing rebase hint remains.
- **Try an ad-hoc command with owed's measurement rules.**
  `owed check try --run '<command>' (--green|--red) <rev> [--min-tests N] [--timeout <seconds>]`
  uses the disk plan's setup and execution settings on an exact commit tree.
  Defaults are one required test and 900 seconds; zero-test or unknown-count
  successes do not pass by default. Choose exactly one of `<node> <check>`,
  `--invariant <id>` or `--run`; `--min-tests` and `--timeout` apply only to
  `--run`. Red expects `fail`, green expects `pass`, and `error` meets neither.
  The `owed_check_try` tool exposes the same options.
- **Record diagnostic measurements for later citation.**
  `owed check try <node> <check> --green <rev> --record` appends one
  informational receipt per measured side, including failures and errors.
  Output identifies `(<commit12>, tree <tree12>)` and adds `receipt #<seq>`
  when recorded. Receipts retain the measured plan, commit, tree, counts and
  log reference. Node checks associate receipts automatically; invariant or
  ad-hoc measurements use `--record --for <node>` to associate a plan node.
  `--for` requires `--record`, and recording requires an initialized ledger.
  Without `--record`, logs remain but no ledger entries are appended.
  Receipts never satisfy gates, change keys or votes, or direct the driver.
- **Find measurement receipts in why.** `owed why <node>` lists associated
  receipts newest first, at most five; `owed why <node> --full` lists all.
  Lines read `receipt #<seq> <target> on <commit12> (tree <tree12>): <verdict> <pass>/<tests> tests, log <path>`;
  unknown counts use `?`. Cite `receipt #<seq>` for a ruling-ordered measurement
  instead of copying raw logs or handwritten tree proofs into the repository.
- **Writer packets show invariants that measure their writes.** Manual and
  driver packets list `- inv:<id>: <reads>` using the attempt's slot-base file
  inventory, with at most five specific invariants and a remainder count.
  Invariants covering at least 90% of slot-base files are grouped as
  `- broad: inv:a, inv:b (every change)`.
  Literal new write paths count; globs do not predict future files. No overlap
  or unavailable inventory means no section. The packet points to
  `owed check try --invariant <id> --green <your commit>` before merge-tree
  measurement at merge.
- **Plan feedback names invariant coverage before submit.** `owed plan`
  and `owed edit --dry-run` (including their tool forms) print
  `note: <node> writes files inv:<id> measures (<reads>); before submit: owed check try --invariant <id> --green <commit>`
  for added nodes or changed writes overlapping non-broad invariants.
  At most ten notes appear, then `note: +k more`; these are advice, not
  warnings or refusals, and add no ledger entries.
- **Packets make the commit-before-measure order explicit.** Every writer
  packet says `Checks, owed check try and your own preflight see committed HEAD only: commit code, then evidence, then measure, then report.`
  For `check try`, `--red` and `--green` select committed revisions; uncommitted
  edits are never measured, and requests for a dirty tree are usage errors.
- **Large ledgers load and report faster.** On one large ledger (about 21,700
  entries), cold submit fell from 301.7 s to 104.1 s; with a snapshot, submit
  fell from 9.48 s to 5.72 s, status from 6.99 s to 3.82 s, and why from
  7.04 s to 3.54 s. Lookup optimizations preserve replay and output bytes.
  Each version uses `snapshot/state-<version>.v8`, so different versions no
  longer overwrite each other's snapshots. Best-effort cleanup keeps the
  current file and the two newest other snapshot files by modification time.
- **Attaching sessions receive still-current earlier wakes.** Session start,
  tool calls and registry discovery replay complete lines from at most the
  last 256 KiB of the current drive log, revalidate them like live wakes,
  then follow from the end. Older lines outside that tail are not replayed.
  An existing follower does not redeliver an already delivered wake; busy
  sessions hold delivery until idle. Sessions inside dsa calls do not attach.
- **Start reports followers only for a running driver.** `owed start` and
  `owed drive --detach` print `followed by pi session <first 8 characters>`
  or the no-session note only while the reported driver is running. An
  already exited driver keeps its exit output without a follower line;
  CLI JSON reports `followedBy: null`.
- **Watch timeout cursors preserve pending merge messages.**
  On timeout, `owed watch --timeout <s>` exits 5 with `no wake within <s> s` and
  `next: owed watch --after <offset>:<ino>`. That cursor retains pending
  ride-along lines such as merge `Done:` messages for the next watch;
  reported or stale lines are skipped when no earlier ride-along awaits.
  Without a cursor, watch still starts at the current log end.
  `owed watch` reads the drive log; writes no ledger entries and takes no
  driver lock. Revalidation may write the state snapshot cache.
- **An isolation-save error preserves the batch refusal.** Refused members
  remain isolated in memory if saving their runtime isolation fails. The
  original retry report gains `(isolation not saved: <error code>)`; a
  restart cannot rely on isolation that was not saved.
- **Status previews respect saved batch isolation.** The batch preview
  excludes open candidates matching the driver's isolation file, so those
  members merge alone. Changed candidates can join batches again; missing,
  malformed or unreadable isolation state means no remembered isolation.

Source grew by 203 lines net across 14 TypeScript files: 313 insertions and
110 deletions since 0.31.0.

## 0.31.0

Plans and ledgers from 0.30.0 replay unchanged; no new ledger kinds or fields
are added. New reviewer launches default to the packaged `owed-reviewer` when
no `drive.reviewer` agent is set. Keep a custom reviewer by explicitly setting
`drive.reviewer.agent` globally or on a node; recorded launches keep their
agent. By default, accepted attempts waiting to merge no longer consume `max`,
refused batch candidates stay isolated across driver restarts, and top-level
pi sessions discover their drivers across repositories. The dsa requirements
stay as in 0.30.0: pi-durable-subagents >= 1.0.27 for the driver, >= 1.1.0 for
durable-writer waits and lease options, and >= 1.2.0 for structured asks.

- **Owed tools attach the session to the target driver.** Every owed tool call
  in a top-level pi session follows the driver in its target repository after
  the call, including refusals. Following the same driver again preserves the
  pending wakes. Sessions inside dsa calls do not attach.
- **Session drivers are found across repositories.** A top-level pi session
  discovers live drivers belonging to it at most once every ten seconds,
  regardless of its working directory. Discovery uses the starting
  `DSA_SESSION`, with `PI_SESSION_ID` as fallback outside dsa calls; stale
  registrations are ignored and unavailable discovery never fails a driver or
  tool. Attachment starts at the log end: a wake written before attachment is
  seen again only when repeated or at the idle re-wake.
- **CLI start says who follows.** `owed start` (including reuse) and
  `owed drive --detach` append `followed by pi session <first 8 characters>`
  when the driver's session is known. Otherwise the line is:
  ``note: no pi session follows this driver; its wakes reach only pi sessions opened in this repository. To be woken elsewhere, run `owed watch` (for example as a dsa command call).``
  JSON adds `followedBy: <id> | null`; tool replies keep their form.
- **Wait for wakes without a pi session.**
  `owed watch [--after <offset>[:<ino>]] [--timeout s] [--json]` reads driver
  wakes and revalidates them against the ledger and dsa, without taking the
  driver lock or writing files. Without a cursor it starts at the current log
  end. Exit 0 delivers a wake batch or driver exit, followed by
  `next: owed watch --after <offset>:<ino>`; pass that cursor to continue.
  A different inode resumes the current log from zero; bare offsets remain
  accepted. Malformed cursors give a usage error naming `<offset>[:<ino>]`.
  Exit 3 prints `no owed driver is running in <repo>; start one with owed start`
  with no `next:` line. Exit 5 prints `no wake within <s> s` and the next
  cursor. Timeout seconds must be a nonnegative integer; omission waits until
  a wake or exit. JSON is `{lines, next, exit}`, with `next` a cursor string.
  Re-running with an old cursor can show a repeated wake line again.
- **The default reviewer can record its verdict.** The package includes
  `owed-reviewer`, which inspects the candidate, may run checks in its own
  scratch checkout and must run `owed review`. It may not edit the writer's
  worktree or main checkout. Explicit global or node reviewer agents remain
  selected; the new default changes no acceptance key or vote rule.
- **Missing reviews have usable recovery commands.** After a `review-missing`
  halt, `owed resume <node>` gives the same sealed reviewer run one more
  verdict follow-up; another seal without a review halts again. Rejected or
  pruned runs cannot take follow-ups, so their remedies omit resume. For the
  builtin read-only profile, guidance next offers
  `owed edit --drive --set reviewer.agent=owed-reviewer --note "<why>"`,
  affecting a new run of a later candidate, then
  `owed review <node> --ok|--block --rank <1-3> --note "<note>" --as owner:<id>`
  and `owed abandon <node>`. The edit never replaces the existing run.
- **Direct tools return in resumed sessions.** At session start and every
  agent start, owed restores missing registered direct tools while keeping
  existing active tools. Unsupported or throwing host tool APIs are left alone.
- **Tool guidance includes the CLI alternative.** Deferred descriptions give
  the exact `owed <command>` fallback, including multiword commands such as
  `owed check try`. Parent-facing tool-name hints also give their CLI form.
- **Merge waits leave capacity for active work.** `max` counts open attempts
  whose phase is not accepted; accepted attempts are `waiting to merge`.
  Total open attempts are capped at `2 × max`, and accepted slots still reserve
  their writes. With pending merges, status shows
  `active <a> of max <m>, waiting to merge <p>`. Dispatch skips explain either
  `ready, not dispatched: <a> active of max <m> (<p> waiting to merge)` or
  `ready, not dispatched: <o> open attempts reach the cap 2 × max = <2m> (<p> waiting to merge)`.
- **Refused batch candidates merge alone across restarts.** After a batch
  refusal, its members stay out of driver batches even when trunk or partners
  change. The report says `batch merge refused (<reason>); <members> merge alone until merged or resubmitted`.
  Isolation ends when the candidate commit changes, the node merges or its
  slot closes; re-submitting the same commit does not lift it. Missing or
  unreadable isolation state means no remembered isolation. Status's batch
  preview uses only ledger facts and can still propose isolated members.
- **Review acknowledgments accept lists.** `--ack-rulings` accepts `latest`,
  one ruling number or `--ack-rulings <n>,<m>`, optionally containing `latest`. A list
  selects its maximum; `latest` selects the latest applicable ruling. The
  tool's `ack_rulings` also accepts a nonempty integer array. Invalid forms
  refuse before recording; all forms retain one numeric watermark. Read the
  applicable rulings before acknowledging them.
- **Incomplete genesis refusals name the remedy.** Adoption and merge refusals
  for missing initial invariant observations now give `owed attest --genesis`,
  matching status guidance.
- **Edit driver settings directly.**
  `owed edit --drive --set <path>=<value> [--unset <path>] --note "<why>"`
  (tool `owed_edit` with `drive: true`) edits or creates the top-level `drive`
  mapping. Paths such as `max`, `repairs`, `merge_batch`, `writer.agent` and
  `reviewer.model` use the plan parser's keys and types. It preserves comments
  and layout outside simple targets and follows ordinary plan authority,
  downgrade and dry-run rules. Node, nodes, invariant and node-new targets,
  and rule/resume options, cannot be combined with drive edits.
- **Clipped messages keep readable boundaries.** Bounded questions, review
  asks and driver evidence prefer a late sentence ending, then whitespace or
  a CJK character boundary, with `…` when clipped. Long writer replies keep
  their original ending. Clipped structured details append
  `(full: pi-durable-subagents describe --key <rid> --json)` to retrieve the
  full question; text that fits stays unchanged.

Source grew by 273 lines net across 14 TypeScript files: 367 insertions and
94 deletions since 0.30.0.

## 0.30.0

Plans without the new fields keep their existing behavior, keys and replay.
Upgrade every ledger reader before using allow caps: `writes_syntax: 3` is
refused by owed < 0.30.0. External `tracked_by: ext:<id>` trackers still require
owed >= 0.29.2; earlier readers refuse them. Structured asks need
pi-durable-subagents >= 1.2.0; earlier runners retain plain asks.

- **Environment signatures guide who is asked first.** Optional top-level
  `env_signatures: {match: [<regex>], unless: [<regex>]}` changes no acceptance
  key, check definition, candidate or downgrade. `match` must be non-empty;
  `unless` is optional. Each regex is a non-empty string of at most 300
  characters compiling as a JavaScript RegExp without flags; unknown keys refuse.
- **Suspect failures stay failures.** A failing ordinary node check gets an
  `environment-suspect: <line>; ` note when a retained log line matches `match`
  and no line matches `unless`, after ANSI CSI and carriage returns are removed.
  The excerpt replaces semicolons with commas, trims and clips to 160 characters;
  verdict, counts, failed tests, signatures, log and keys stay unchanged. Ordinary
  checks with red/mutant specifications qualify, but red/strength executions,
  checks with repeat, invariants and trunk probes do not.
- **Measure suspect failures again before asking the writer.** For an
  unsatisfied check blocked on the current candidate's key, the driver measures
  attribution using the failure's plan and logs one notice without waking the
  parent. Two suspect failures halt the node without a repair or repair-budget
  charge. Full recovery guidance is in `owed why <node> --full`: fix the
  environment, run `owed attest <node> --rerun`, then `owed resume <node>`;
  for a real code failure use `owed decide <node> --writer --text "<instruction>"`.
  A resume after this halt restores ordinary routing. A pass retains the flaky
  owner need; a non-suspect fail or error retains its usual repair/backoff route.
  Satisfied or effectively waived checks do not trigger this routing.
- **Checks can run a pinned script.** Node checks and invariants accept
  `run_file: {path: <path>, sha256: <64 lowercase hex>}` instead of `run` or
  `template`, with optional `args` (at most 64 strings, no NUL). Each normal
  repository-relative path segment uses only `A-Z a-z 0-9 . _ @ + , = -`,
  excluding `.` and `..`; absolute paths, empty segments and trailing slashes
  refuse. Owed derives `exec ./<path>` with individually single-quoted arguments.
  An explicit `run` must equal that command; `args` alone refuses. Edit pins
  with `checks.<id>.run_file.path`, `checks.<id>.run_file.sha256` and
  `checks.<id>.args` (omit `checks.<id>.` for invariants).
- **Script admission verifies the declared bytes.** Before recording a plan,
  owed reads each uncached pin from the current worktree's repository root, or
  the commit selected by `owed plan --rev`, checks its SHA-256 and stores it.
  A missing file or hash mismatch refuses without ledger entries or replacement
  of the edited plan file; verified blobs may remain cached. Replay never reads
  the source file, and a cached pin needs no source reread.
- **Every execution installs the pinned script.** After setup and overlays,
  owed verifies and installs the stored bytes with mode 0755 before each run,
  including red, strength, invariants, attribution, probes and repeats. Candidate
  script changes are overwritten; target links are replaced, and symlinked
  parents or paths leaving the tree refuse. Missing/corrupt blobs and install
  failures are environment errors, never successful red results. The script
  needs a usable shebang; loaded helpers are not pinned and still follow reads
  and closure. Shell, wrapper, environment and timeout behavior stays the same.
- **Pins and arguments participate in execution keys.** Check/invariant, red
  and strength keys include `run_file` and `args` only for pinned checks; those
  fields also belong to the check definition used for superseding blocks.
  Checks without pins keep their existing keys.
- **Pinned checks have clear plan feedback.** Shell lint skips their derived
  command while still checking setup, wrappers and timeout diagnostics.
  Plan/edit replies identify `check <node>/<id>: run_file <path> (sha256 <first 12>)`;
  invariants use `inv:<id>`.
- **Try a check before recording it.** `owed check try <node> <check> --red <rev>
  --green <rev> [--plan <file>]` (or `owed check try --invariant <id> ...`, tool
  `owed_check_try`) reads the plan on disk and needs at least one revision.
  Each side uses a fresh exact-commit tree without closure replacement or test
  overlays, with normal setup, wrapper, counts, timeout and repeat behavior.
  Even `red: true` uses ordinary verdicts: red expects `fail`, green expects
  `pass`, and `error` meets neither. No ledger entries are recorded; output
  retains logs and unmet expectations include the last 20 lines. `--json` gives
  per-side results. Exit codes are 0 for all met, 1 for unmet, 2 for usage and
  75 for a busy lease; `--no-lease` or plan `lease: false` disables the lease.
- **Selftests guard check authoring.** Optional check/invariant
  `selftest: [{commit: <40 or 64 hex>, expect: pass|fail}]` accepts 1–8 fixtures.
  Recording a plan runs fixtures for new checks, changed execution definitions
  or changed fixture lists, outside the ledger lock under the shared lease
  unless disabled. Unmet expectations, errors or unknown commits refuse with
  no ledger entries or edited plan replacement. Success adds a `selftest/v1`
  note in the same append after the plan; unchanged checks are not rerun.
  New/changed checks without fixtures warn `not self-tested`; previews only
  announce pending selftests. Fixtures affect no key, downgrade, candidate
  invalidation or block supersede, and their results are not acceptance evidence.
- **Cap allow grants per node.** Optional `allow_only: [<entry>, ...]` limits
  grants from `allow.writes`; it neither grants permission itself nor narrows
  the node's own `writes`. Literal entries match exact paths, while globs use
  syntax-2 matching (`dir/**` for a directory). Omission is unbounded and `[]`
  permits no allow grants. Caps constrain paths, not functions or diff hunks.
- **Caps apply throughout writes enforcement.** Grant admission and replay,
  effective writes, executor measurements and merge rechecks enforce the cap.
  Previously granted paths excluded by the current cap cease to confer
  permission while their receipts remain. Refusals say `outside allow_only`
  and suggest `owed edit <node> --set 'allow_only=[...]'`, not a wider allowance.
- **Changing a cap remeasures writes.** Only the writes key gains `allow_only`
  when present, including `[]`. A cap change carries the open candidate through
  the usual plan-update path and requires fresh writes evidence; uncapped keys
  and check/review keys are unchanged.
- **Widening a cap needs the owner.** Adding a cap or provably narrowing it
  needs parent authority without a downgrade. Removing it, widening it or an
  unproved containment change is `allow_only widened: <node>`; allow rules
  cannot authorize that owner-only downgrade. Containment uses equality, exact
  paths matched by a prior glob or an enclosing trailing `**` glob.
- **Older readers cannot silently ignore new caps.** New plans with any
  `allow_only`, including `[]`, record `writes_syntax: 3`; uncapped plans keep
  2. Syntax 3 retains syntax-2 path matching and enables caps. Historical
  syntax-1/2 plans keep ignoring a valid `allow_only`; a malformed historical
  field is refused by 0.30 replay.
- **Decide and edit expose allow caps.** `owed decide <node> --allow-only p1,p2
  --text "<limit>"` (tool `allow_only: string[]`) replaces the cap alongside
  optional writes/ruling/resume changes in one append. `--allow-only ''` sets
  `[]`; `owed edit` also accepts `allow_only`. Multi-node decisions refuse a
  mix of changed and unchanged nodes; split those decisions, or include actual
  writes additions. Dispatch packets show the cap beside pre-authorized scopes.
- **Structured ask fields are validated without coercion.** Runner questions
  retain `decision_needed`, `options`, `facts`, `tried` and
  `classification_candidates`. Invalid option members discard the entire
  options field to preserve numbering; invalid optional properties are dropped
  independently, and multiple preferred options lose all preferred markers.
  Invalid facts/tried or candidate members are removed instead of displayed
  as object text.
- **Questions lead with the decision.** Structured asking notices and wakes
  show `DECISION:`, numbered options with scope/cost/risk and a preferred marker,
  context, details and the existing answer command with
  `answer "option <n>; <limits>"`. Option fields are bounded to 160 characters,
  context to 600 and details to 300. Existing needs/status answer-retry text
  preserves this structure; no new unanswered-question membership is added.
  Each question keeps its own answer command; plain questions stay unchanged
  and the driver never chooses an answer.
- **Writer guidance asks for actionable decisions.** Packets and follow-ups
  ask for one-sentence `decision_needed`, numbered options with scope, cost,
  risk and one preferred choice, facts, attempted work, and candidate
  explanations with evidence and what remains unproven. Tools without ask
  fields use text starting `Decision needed:`. Writers must still ask before
  exceeding their scope and never submit a candidate they believe fails checks.
- **Edit several nodes together.** `owed edit a,b --set review.count=2
  --note "<why>"` or tool `owed_edit {nodes:["a","b"], ...}` applies the same
  sets/unsets, deduplicates in order and validates only the final plan. One
  plan computes carries once, with one optional rule naming all targets and
  one requested resume per node. Unknown targets, missing unsets, authority,
  resume and pre-write append refusals leave ledger and plan file unchanged;
  dry-run shows one diff and downgrades. Exact existing CLI IDs and
  `--node-new` targets stay literal even with commas; use tool `nodes` or
  separate edits for a colliding batch. `nodes` is exclusive with `node` and
  `invariant`; creation permits only one distinct node. Existing post-append
  file-replacement recovery still applies.
- **Answers report the observed call state.** After successful `owed answer`
  or answering `owed decide`, including tools, owed describes each run once
  with a 10-second bound outside the ledger lock. Each answer reports
  `running`, `queued`, `asking (answer not yet taken, qid <q8>)`,
  `asking again (qid <q8>)`, `sealed (<status>)` or `unknown (<reason>)`.
  Only the selected question's `to = <wid>/<key>` identifies the call; a
  closed-question retry without that address reports unknown rather than
  borrowing another call. Missing phase or malformed matching question IDs
  also report unknown; the original qid still open means the answer is not
  yet taken. JSON/tool `answers` are runtime facts only. A describe failure
  never resends an answer or appends an extra ledger entry.

Source grew by 421 lines net across 20 TypeScript files: 585 insertions and
164 deletions since 0.29.2.

## 0.29.2

No ledger, entry kind, entry field, plan or key change. `tracked_by` may now hold
`ext:<id>`, which owed < 0.29.2 refuses on replay: upgrade every reader of a
ledger before recording an external tracker.

- **`--remeasure` re-measures the node's own blocked check.** After
  `owed decide <node> --remeasure` (or `owed resume <node> --remeasure`), the
  driver runs the attribution of every active execution block of the node whose
  failure precedes that resume, including blocks on the current candidate's
  key, before it sends any repair follow-up. Each such block gets at most one
  non-error attribution per resume: a fail clears the block and the failure
  stays the writer's; a pass leaves a flaky block that needs
  `owed waive <node> --accept-risk <seq>`; an attribution error follows the
  existing back-off and halt rules. A plain resume keeps 0.29.1 routing, and
  merge-tree invariant remeasure is unchanged. Before, the node check stayed
  blocked and the writer got a repair follow-up until someone ran
  `owed attest <node> --rerun` by hand.
- **The `--remeasure` reply says what will be measured again.** One line per
  obligation: a failed merge-tree invariant as before; a blocked node check as
  `remeasure queued: <node> check <id> (failure #<seq>) is measured again on
  its key before any repair; a pass leaves a flaky block: owed waive <node>
  --accept-risk <seq>`; a check that already failed again on its key as
  `<node> check <id> already failed again on its key (#<seq>); --remeasure does
  not measure it again: owed attest <node> --rerun`; otherwise `nothing to
  remeasure: no failed merge-tree invariant and no blocked check of <node>`.
  The misleading "(the next merge measures as usual)" is gone. `owed resume
  --remeasure` now builds its reply from the state that includes the new
  resume entry.
- **Waivers can be tracked outside the plan: `--tracked-by ext:<id>`.**
  `owed waive … --tracked-by` (CLI and the `owed_waive` tool) accepts, besides
  an unmerged node of the current plan, an external tracker `ext:<id>` with
  `<id>` matching `^[A-Za-z0-9][A-Za-z0-9._:/#-]{0,127}$`. It is recorded in
  the existing `tracked_by` field and validated on append and on replay; a
  tracker that names a node of the plan in force when the waiver was recorded
  keeps its local meaning. An external tracker never settles: summary debt,
  full status, report and the waived node's `owed why` show
  `tracked by ext:<id> (external)` for as long as the debt exists. Malformed
  ids and a bare `ext:` are refused with nothing recorded.
- **Per-command help.** `owed help <command>`, `owed <command> --help` and
  `owed <command> <args…> --help` print only that command's forms (for example
  every `attest` form and its exit codes), the flags it accepts (including the
  global `--json`, `--as`, `--i-am-owner`) and the `Usage:` line, exit 0, and
  work outside a repository. The text comes from the existing help and flag
  tables. `owed help <unknown>` exits 2 and names the known commands; `owed
  help` and `owed --help` still print the full help.

## 0.29.1

No ledger, entry, plan or CLI-flag change; durable-writer waits still need
pi-durable-subagents >= 1.1.0, as in 0.29.0, and old ledgers replay identically.

- **Writers can wait on their own durable runs during repair turns too.** In
  13 h of field use on 0.29.0, 4 of 9 writer halts fired while the writer's
  own `--wake caller` run was still pending during a repair turn on a node
  with a current candidate. The 0.29.0 wait now applies whether or not the
  node has a candidate: a writer sealed ok (or pruned) with outstanding
  durable wakes no longer gets the "finished repair follow-up … without
  submitting a new candidate", repairs-exhausted, writer-ruling or rebase
  no-submit halt. Due follow-ups are still sent first; a non-ok seal still
  halts with `; awaiting durable <request>`; when the woken generation seals
  without a new candidate and without outstanding wakes, the halt fires as
  before. Status and why show `writer waiting on durable <request>` for a
  node with a candidate only after a repair or ruling follow-up went to its
  writer after that candidate; otherwise the node keeps its reviewing,
  measuring or accepted phase.
- **The writer-stop halt leads Needs you; no owner ok over an unresolved
  block.** For a node halted because its writer stopped (finished or stopped
  without submitting, sealed non-ok, repairs exhausted, worktree not
  inspectable), the halt need comes first, followed by the node's obligation
  needs; other halts keep their order. While an active review judgment block
  sits on the current candidate's key, no need for that obligation offers
  `owed review … --ok`, and the "repair #… submitted; #… clears when …" line
  appears only for a repaired block with no newer active block on the
  current key.
- **Writes already granted by an allow rule no longer refuse a decision.**
  `owed decide --writes` records its decision when some or all paths are
  covered by an allow rule of the current plan: the ruling text ends with
  ` (writes already granted by allow rule, plan #<seq>: <paths>)` and
  uncovered paths are added as before. `owed amend` with only such paths
  records nothing, exits 0, names `allow rule (plan #<seq>)` and adds
  `; owed decide <node> --writer delivers a limit`.

## 0.29.0

Fewer needless interruptions. No ledger format, entry kind, entry field,
enum value or plan key changes. Old ledgers and snapshots replay identically;
version-bound disk snapshots are rebuilt on upgrade. pi-durable-subagents
>= 1.1.0 enables durable-writer waits, seal diagnostics and the new lease
options; older dsa keeps 0.28 writer-wait and lease behavior. The other
improvements below do not require that runner upgrade.

- **Writers can wait on their own durable runs.** A successful sealed or
  pruned writer with no current candidate and outstanding durable wakes gets
  no submit reminder or stopped/no-submit halt, including after repair,
  ruling and rebase follow-ups. Due follow-ups still run; non-ok seals keep
  their halt/recovery route and name outstanding durable requests. Writer
  packets give `pi-durable-subagents run --request owed-<project>-<node>-<attempt>-<name> --wake caller -- <command…>`:
  start the long command, then end the turn without submitting. dsa resumes
  the writer with the command's exit status and output tail; owed recognizes
  the wake's generation without replaying its earlier follow-up. A live
  driver's status and why show `writer waiting on durable <request>` under
  In progress, without a wait-generated Needs you or parent wake. Without
  a live driver this runtime fact is unknown. Age uses the described seal
  time, or the first driver observation of that generation when unavailable;
  that approximation resets on restart and affects display only.
- **Stopped-writer quotes keep the substance.** Halts flatten the whole
  final reply, quoting up to 600 Unicode code points in full; longer replies
  keep the first 200 and last 380, retreating to nearby spaces. Up to three
  omitted commit IDs (up to 12 hex characters) and three durable IDs are
  appended in first-appearance order. JSON, control and bidi escaping remain; old recorded halts are not
  rewritten. When dsa reports processes it killed at seal, the halt adds
  their total and at most three pid/command descriptions, commands bounded
  to 80 code points. The quote is context, not acceptance evidence.
- **Held by ruling is progress, not a resume request.** After a halt,
  `owed decide <node> --text "<decision>" --no-resume` leaves it stopped as
  `held by ruling #N; owed resume <node> releases it`. Status, brief, `/owed`
  and receipts show the hold without asking the parent to resume; why keeps
  the release command in Next. The held halt no longer wakes the parent,
  and obsolete queued needs are dropped at delivery revalidation. Other
  obligations, review decisions, questions and debt remain actionable;
  prospective/global rulings and rulings before the halt do not hold it.
- **Scheduled sweeps stop when their policy changes and yield the lease.**
  Changing the recorded sweep definition or `every`, or removing `every`,
  cancels an in-flight scheduled sweep without a verdict or interruption;
  an atomic append already begun completes. Driver activity reports
  `sweep aborted: plan changed`. Scheduled sweeps request `--yield`; a lease
  yield aborts measurement, records nothing and retries later like busy.
  Unsupported options fall back only after a separate no-op hold confirms
  dsa's unknown-option refusal; the measured command is never run twice.
- **Cancellation cleans up tagged process trees before releasing leases.**
  Every setup, check, invariant and sweep command gets a fresh `OWED_RUN`
  token, outside evidence keys. Abort/timeout ends the command group and,
  on Linux, escaped descendants proven to carry that exact token in readable
  `/proc/<pid>/environ`, using TERM, grace, KILL and rescans bounded to five
  seconds including scanning. Unreadable or untagged processes are not
  individually signalled or waited for: leaving the group and clearing the
  token or making environ unreadable escapes this containment. Other
  platforms retain group-only cleanup; normal exits are unchanged.
- **Sweep preparation no longer occupies the machine lease.** Replay and
  the first tree checkout run before acquisition. The sweep acquires its
  lease immediately before the first setup or measurement command and
  retains it through command and tree cleanup.
- **Lease-wait timeouts are environment errors, not failed checks.** A
  check or invariant, red runs included, exiting 75 with last non-empty
  output matching `hold: … not running the command (exit 75)` or
  `hold: … the command was stopped (exit 75)` records an environment error
  explaining that the lease was not granted or yielded. It uses existing
  backoff, never counts as a failed check, red pass or toward the two-errors
  halt. Other exit-75 output, including trailing spaces after `(exit 75)`,
  keeps its ordinary verdict.
- **Acceptance measurements request priority 10.** Driver and standalone
  attest/merge machine holds pass `--priority 10`; a confirmed unknown-option
  refusal retries once without it and remembers the fallback in that process.
  Lease opt-outs and acceptance gates are unchanged.
- **Candidate-only files do not make a trunk probe trunk-red.** Automatic
  merge probes and `owed attest <node> --probe-trunk [--check <id>] [--runs <n>]`
  classify a failed probe naming a missing candidate-added file absent from
  the probed trunk as `error`, with
  `probe not applicable: trunk lacks <path>, which the candidate adds`.
  Outside-checkout absolute paths are ignored.
  The failed merge check still refuses the merge; this is not a pass or a
  waiver. Merge checks run on a synthetic merge commit, with HEAD^1 the
  trunk tip and HEAD^2 the candidate: checks must not pin the candidate base.
- **Anonymous merge-check failures show their last output line.** When no
  failed test names were parsed, the merge refusal and newly recorded
  merge-check need/notice details include the last non-empty output line,
  escaped and bounded to 160 characters. Unmarked historical notes retain
  their old rendering; anonymous invariant failures keep their existing
  refusal diagnostics rather than receiving the new check marker.
- **Housekeeping is a hint, not automatic deletion.** Compact/full status
  and merge replies show
  `Housekeeping: <n> finished attempt worktrees; owed gc reclaims them`
  at five registered, recorded closed attempts.
  Open attempts, unrecorded and measurement worktrees do not count; a failed
  listing shows no hint. Use `owed gc --dry-run` before `owed gc`: the hint
  neither deletes anything nor promises dirty/locked worktrees can be removed.
- **W14 replay.** On a production ledger copy (17147 entries, 1130 sampled
  states, both engines in one replay), 0.29 reports (a)/(b)/(c) = 0/0/0 and
  uncovered 715, unchanged from 0.28 (588 suppressed obligations, 107
  repository items, 20 node items without a single command). Of the 9 sampled
  halts held by a ruling, 0.28.0 offered `owed resume <node>` as the primary
  command for 3; 0.29 offers it for 0. Status, decide and why rendering time
  is unchanged from 0.28.
- **Source growth stays within budget.** Against 0.28.0, `src/*.ts` has
  331 insertions and 109 deletions across 14 files, net +222 lines, within
  the 0.29 budget of +300.

## 0.28.0

One authority for outstanding needs. No ledger format, entry kind, entry
field, enum value or plan key changes. Existing ledgers and snapshot states
replay identically; version-bound disk snapshots are rebuilt on upgrade.
CLI and driver guidance changes below. Mixed 0.27.x/0.28 processes are not
safe once 0.28 records a waiver citing only active merge failures on a merge
key distinct from the candidate key: older readers reject that entry, so
upgrade all readers before recording such a waiver.

- **One need, one command.** `needs()` supplies parent and owner work per
  obligation, with the required risk citations, candidate pins, reasons,
  notes, roles and ruling acknowledgments. Resolving one obligation does
  not hide another. Where no single existing command resolves the work,
  owed says `no single command`, rather than inventing a fix node or
  suggesting `decide`, `rule` or `resume` on trunk. A submitted changed-key
  repair does not clear its old review block: why names the original
  reviewer, sufficient rank and current-key requirement without promising
  a new reviewer run. Same-key guidance does not turn a block into a
  recheck, and execution and flaky blocks retain their own clearance routes.
- **Consistent status, why and next steps.** Status, brief, why, receipts,
  owner notices and the pi extension render the shared needs; batch
  eligibility and unresolved-ruling resume refusals use the same guidance.
  Why lists same-need alternatives as `or:` lines, and trunk or invariant
  cards with outstanding obligations no longer say merged. Node attest
  exit 4 starts with `measured; not accepted yet:` and its need or activity;
  exit codes are unchanged. `owed rebase <node>` without `--apply` first
  says that the slot base moved and the writer must rebase and resubmit.
  Missing error extraction distinguishes an exact failed-test-name mismatch
  from a matching block without an error field; neither counts as error text
  for automatic remeasurement. Git-derived merge checks, drift and rebase
  waits remain supplemental diagnostics, outside the pure need selection.
- **Halts show the action that clears them.** An open halted slot first
  needs any unresolved review ruling, then its parent or owner obligation
  work, otherwise `owed resume <node>` with any dependency wait explained.
  Only in that last case does a stopped writer instead offer
  `owed decide <node> --text "<ruling>" --writer`, with resume as an
  alternative. Structured wakes carry need identities; pi followers with
  the revalidation hook refresh them before delivery and drop vanished
  needs without dropping surviving siblings. Hookless followers retain
  recorded-need delivery, and old unstructured logs retain compatibility
  parsing.
- **Measurement activity without duplicate owner requests.** Attest
  invocations publish separate queued/running runtime records, clean them
  up after completion or abort cleanup, and readers remove dead-process
  records. Progress shows obligations, start time and attest pid. Only a
  running candidate-tree attest matching the node, current candidate and
  obligation suppresses that obligation's owner need and notice; queued,
  other-tree and old-candidate runs suppress nothing. Records are not
  ledger entries, acceptance evidence or predictions of driver actions;
  measurement selection, lock order and acceptance gates are unchanged.
- **Merge-failure waivers cover the matching merge key.**
  `owed waive <node> <obligation> --accept-risk <risks> --reason "<reason>"`
  records the prospective merge key when every cited risk is a failing
  merge observation of that node and obligation on that key. An
  independently satisfied candidate can then reuse that waiver without
  remeasuring the covered merge key; a distinct failing or unmeasured
  candidate key and uncited blocks remain uncovered. All-active stale-key
  or conflicting-tree citations fall back to the candidate key; mixed
  candidate/merge citations retain their prior behavior. Inactive citations
  retain their existing refusals and CAS order. The receipt names candidate
  or merge scope, including the trunk used for a merge key. Waivers remain
  debt, not measured passes.
- **Ruling-only follow-ups can finish without a change.** A sealed writer's
  ruling follow-up with no `writer: true` ruling permits `no change: <why>`
  instead of a redundant ruling-follow-up-without-submit halt. Existing
  repair, failure and recovery routes still apply; the reply neither accepts
  a candidate nor clears a halt. Writer-change rulings still require submit,
  and live reviewer ruling messages ask for review, not a writer commit.
- **Resume dependencies trigger automatic rebase.** Once dependencies
  named by `owed resume <node> --after <deps>` merge, the driver uses the
  same automatic rebase route as for plan dependencies; the existing
  rebase and resubmit requirements remain.
- **One-line need text and cheaper quarantine-debt views.** Need text
  stays on one physical line: recorded halt reasons and item details escape
  control characters, with a newline shown as `\n`. When no quarantine is
  active, `owed status` and `owed why` no longer load historical plans or
  scan observations for quarantine debt. With active quarantine, observations
  are collected once, lazily, and irrelevant observations are skipped before
  plan lookup; the quarantine-debt output is unchanged.
- **Historical command-surface comparison.** From a source checkout, run
  `node test/perf/needs-replay.ts <ledger.jsonl> --blobs <dir> --halts --every 25 --baseline-src <0.27.2-src>`
  to compare status, compact why and emitted owner notices with 0.27.2.
  This test-side tool is not shipped in the npm package or exposed as an
  `owed` CLI command. It separates (a) command disagreements, (b) invalid or
  incomplete commands and (c) wrong-obligation commands, and reports uncovered
  work, missing blobs, unavailable surfaces and notices not emitted rather
  than counting them as zero errors. On a production ledger copy W14, with
  17147 entries, sampling 464 halt sequences plus every 25th sequence gives
  1130 samples. 0.28 reports (a)/(b)/(c) = 0/0/0 and uncovered 715: 588
  parent/owner obligations not listed by the retained membership rules, 107
  repository needs and 20 node needs with no single command; missing blobs 0.
  The 0.27.2 column reports (a)/(b)/(c) = 1781/1251/9, uncovered 3302 and
  unavailable 2260, the private output surfaces that cannot be compared.
  That column uses a measurement-only copy of 0.27.2 with only the
  quarantine-debt short-circuit above added, because the unchanged version
  could not complete the full replay. On a W14 prefix, the copy's compared
  output is byte-identical to the original 0.27.2 output.
- **Subtraction target not reached.** 0.28 aimed for a net source reduction
  but `src/*.ts` grows from 16282 lines in 0.27.2 to 16684, a net +402
  (R-028-73): removing superseded selectors did not offset the new need
  vocabulary and corrections, command validation and identities, measurement
  runtime, waiver-key behavior and driver integration. The code was not
  compressed just to meet the metric.

## 0.27.2

Two misjudgment and memory fixes. No ledger format, entry kind, plan key,
CLI flag or gate change, and no upgrade order: 0.27.x readers, drivers and
executors can run side by side.

- **Free text is no longer read as a pytest summary.** The fallback that
  counts pytest output used loose matching for numeric count words and
  summary delimiters, so a custom script that printed `ENOTEMPTY=0 failed=[]`
  after three passing runs was counted as zero tests and halted as a setup
  failure. The fallback now accepts only pytest's terminal summary line,
  either bounded by `=` runs or ending in pytest's duration (`in 0.12s`,
  optionally followed by the `(0:01:15)` clock pytest prints for runs of a
  minute or more). Its body must be comma-separated `<n> <word>` items using
  only recognized pytest count words. The zero-test forms `no tests ran`,
  `collected 0 items` and jest's `No tests found` are line-anchored, allowing
  leading whitespace and `=` runs; the pytest forms must occupy the whole
  line, with an optional duration and clock for `no tests ran`.
  ANSI SGR color codes are ignored by this fallback. Warnings, reruns and
  deselected tests are not counted as tests. TAP, cargo, owed-counts and
  jest/vitest `Tests:` parsing are unchanged. A normally completed check
  exiting 0 with no recognized count follows the documented rule for
  unrecognized output: it passes without `min_tests`, and is an error with
  `min_tests`.
- **A fallback zero-test result names its parser and counted line.** The
  observation note of a check counted as zero tests by the pytest or
  jest/vitest fallback names the parser and quotes the counted line (escaped,
  at most 160 characters). The setup-failure notice uses a normalized
  signature from the full counted line instead of an unrelated warning:
  timestamps, paths, identifiers and numbers are masked, whitespace is
  collapsed, and the signature is bounded to 300 characters. Failures on
  several nodes within 30 minutes with the same normalized signature group
  into one notice per trunk commit.
- **`owed verify` bounds parsed-plan retention.** It still parses every plan
  version from its YAML and compares existing entries in the persistent plan
  cache, but its local LRU keeps at most 8 parsed versions and re-parses
  evicted versions from YAML when the replay needs them. Each live replay
  state also retains up to four historical versions plus its current plan;
  eight is the LRU limit, not a total held-plan limit. On a ledger with
  16,724 entries and 658 plan versions (about 950 MB of plan YAML),
  `owed verify` now completes in about 1.1 GB of memory in about 5 minutes,
  with the same output and the same resulting cache. 0.27.1 ran out of a
  2.8 GB heap there, and with a 14 GB heap it took 6.3 GB in about
  3.5 minutes: the bound costs re-parsing time. This removes the known limit
  listed under 0.27.1.

## 0.27.1

Bounded plan memory. No ledger format, entry kind, plan key, gate or output
change, and no upgrade order: 0.27.0 and 0.27.1 readers, drivers and
executors can run side by side. Replay snapshots are validated against the
owed version, so the first load after upgrading replays the full ledger;
parsed-plan retention during that replay is now bounded too.

- **Bounded parsed-plan caching for long-running processes.** The
  process plan cache holds at most 8 resolved plan versions, keyed by ledger
  directory and plan hash (least recently used first out); a long-running
  `owed drive` no longer retains every parsed plan version through this
  cache. The on-disk plan JSON cache is written atomically.
- **Replay and `owed report --metrics` resolve plan versions only when an
  entry needs them**, instead of loading the whole plan history up front, and
  a state loaded by owed retains at most four historical plan versions
  besides the current one. Plans named by an operation that is about to
  append (for example the new plan of `owed init` or `owed plan`) are still
  resolved before any ledger entry is appended, so a missing plan with no
  usable cache entry refuses the operation without recording an entry, as
  before.
- On a ledger with 16,724 entries and 658 plan versions (about 950 MB of plan
  YAML), `owed report --metrics` now completes in about 1.2 GB of memory
  with the same output, where 0.27.0 exceeded 4.7 GB without finishing.
- **GitHub Releases.** After a `v*` tag is published to npm, a second job
  creates the GitHub Release for the tag, with that version's section of this
  changelog as its notes, and records a deployment in the `npm` environment
  that links the published npm version. The publishing job itself is
  unchanged.

Known limit: `owed verify` still parses and keeps every plan version, so on
very long plan histories it needs memory proportional to that history.

## 0.27.0

No new ledger entry kind and no gate weakened. New plan keys
(`quarantine_sets`, `quarantine_set`) and optional entry fields
(`obs.quarantine_passed`, `waive.tracked_by`, `ignorable`) need an upgrade
order: **upgrade every reader (CLI, drivers, every pi session loading owed)
to 0.27 and restart it before any 0.27 executor measures** (attest,
genesis/trunk, merge, adopt or a trunk probe) and before a plan with a
quarantine set or a tracked waiver is recorded. 0.26 refuses
`obs.quarantine_passed` and refuses `ignorable` where it refuses unknown
fields; it silently ignores `quarantine_sets`/`quarantine_set` (so a 0.26
executor would measure without the shared names) and ignores
`waive.tracked_by`. Ledgers and plans without these additions replay with
identical states and keys (SPEC Compatibility).

- **A flaky block says what is known.** A flaky exec block names
  its pair wherever it is shown (status, wake, `owed why`, waive hint, repair
  text): the commit, the failing observation and its tests, the same-key
  rerun that passed, and that a pass does not erase a failure, with the
  `--accept-risk` command. A candidate block on an older key also says how
  the current candidate did on that obligation. Until the current candidate
  has a counted executor pass on its own key, such a block is held: status
  shows `waiting for <obl> on candidate <commit12> before deciding flaky #<F>`;
  no owner request or driver repair is raised for that held block. Error-only
  measurements keep the hold; a counted pass exposes the original risk, and
  a current failure takes the normal repair path while the older flake stays
  held. Same-key and merge-key flakes remain immediately actionable. The
  block still counts for acceptance and merge, and an owner waiver is
  accepted at any time (SPEC §7.15a, §9.9).
- **`owed waive` leads with its effect.** The first line is
  `waived <node>/<obl> #<seq>: debt recorded` (with accepted block risk when
  present), then what the waiver does now, then the compact `owed why` card;
  `--full` (tool: `full: true`) restores the full receipt after the first two
  lines. A waiver that accepts a counting block, merge keys included, says
  `in effect now: --accept-risk accepts #<B>`; it no longer says
  "the waiver is not needed" when the candidate key happens to pass (SPEC
  §9.9).
- **Quieter plan admission.** `owed plan` and `owed edit` check
  admission for invariants and checks only when they are new or their
  `run`, `reads` or `timeout_s` changed. A check's closure warning recurs when
  its reads or the closure changes; near-timeout warnings recur only when
  their text changed. General check-less and loop warnings keep their own
  rules. A fresh init lints everything and `owed report --cost` stays the
  full view (SPEC §9.7).
- **`--remeasure` says what will be remeasured.** `owed decide …
  --remeasure` and `owed resume <node> --remeasure` name each invariant whose
  latest merge-tree observation failed and its merge key, or say that trunk
  or the candidate moved and the next merge builds a new merge tree (SPEC
  §12.5).
- **Trunk probes on demand.** `owed attest <node> --probe-trunk
  [--check <id>] [--runs <n>]` runs node checks on the current trunk commit
  as trunk-probe observations under the machine lease and node attest lock.
  It requires an open slot with a candidate. Without `--check` it probes
  every check with an active or flaky exec block; `--runs` (1–10, default 1)
  runs sequentially per check and stops that check at its first non-pass.
  Existing same-key probes do not skip an explicit run. Probes never change
  blocks, candidate keys, verdicts or counts; a probe can mark a merge-tree
  check block trunk-red or clear that mark, as the merge-time probe does,
  without clearing the failed merge check. Red/mutants checks and checks
  whose tests are new in the candidate are refused. Probe mode cannot combine
  with `--rerun`, `--remeasure`, `--trunk`, `--genesis` or `--all`. Exit 0 all
  passed, 1 a non-pass or moved plan/candidate/slot/trunk CAS (nothing recorded
  for that run), 2 usage, 75 Busy (SPEC §7.27).
- **Failures that are not this node's.** When complete test-only failure
  evidence also exists in trunk probes of the same node check definition and
  closure, before or after the node's failure, with matching signatures (or
  no signature on either side), the block reads
  `<obl> block #<B> also fails on trunk <sha12> without <node>`, with the
  `owed waive … --tracked-by <fix>` command or `(add a fix node first)`;
  the writer is told to ask the parent instead of changing code outside its
  writes. Invariant matching keeps its earlier-observation rule. Combined
  diagnoses (needs, wake, `why`, waive hint, repair text) give trunk-red
  precedence over shared-check attribution, with its parent guidance
  (including the `owed waive … --accept-risk` alternative); the applicable
  diagnosis retains the full flaky same-key pair (SPEC §9.9, §8.3.3).
- **Tracked waivers.** `owed waive … --tracked-by <fix>` (tool:
  `tracked_by`) links the debt to another unmerged node of the current plan;
  missing, merged and self trackers are refused. Debt reads
  `waived — tracked by <fix>`; once `<fix>` merges, default status, brief and
  summary omit it and `--full`/`owed report` show `settled by <fix> (merge
  #<seq>)`; `owed why <fix>` lists the debt it tracks. A tracker that leaves
  the plan unmerged shows the debt again. Re-waiving the same failure
  replaces its tracker; different failures keep separate debt lines.
  Presentation only: the waiver and its gate effect are unchanged, and the
  text never says the flake is gone (SPEC §9.9, §10–11).
- **Shared quarantine lists.** Plan top level
  `quarantine_sets: { <name>: [{test, fix, note?}] }`; a check or invariant
  sets `quarantine_set: <name>`, alongside its own `quarantine`. Adding an
  entry, changing its fix or adding or changing a reference is an owner-only
  downgrade; removal is free. Checks without a set keep their keys. Eligible
  check/invariant jobs whose keys commit to active quarantine names get
  `OWED_QUARANTINE_FILE`, a private per-run file of the sorted names outside
  the measured tree, removed afterward, so scripted runs can skip the same
  list instead of a hand-kept skip file. Delivery alone excuses nothing and
  never rewrites exit status; a run with no native protocol says
  `quarantine file: <n> names (owed excused nothing)`. Native Node/Cargo
  failures refused excusal, including Cargo-shaped scripts, keep
  `quarantine not applied: <reason>`. Names containing CR/LF cannot be
  delivered; all other runs owed starts have the variable removed from their
  environment. A container wrapper must make the host file visible itself.
  With sets defined, a new or changed check or invariant that runs `cargo
  test` or `node --test` with neither `quarantine` nor `quarantine_set` gets
  an admission warning (SPEC §3.6, §7.16, §9.7).
- **Native Cargo quarantine.** A plain `cargo [+toolchain] test …
  --no-fail-fast` run, with optional leading environment assignments and only
  allowlisted tokens, can be excused when it completes with exit 101 and no
  timeout, signal, spawn error or truncated output. Counts must be Cargo,
  not mixed; every announced test block needs exactly its announced result
  count and one matching summary. Only test-failure rerun and target-count
  error lines are allowed, and their counts must match failed blocks; every
  reported unsuccessful process exit must be 101. Failing names must be
  distinct, occur in exactly one block, agree with counts and all be active
  quarantine names; remaining tests must still meet `min_tests`. Excusal
  excludes quarantined failures from test/failure counts and records exit 0
  for Cargo; native TAP excusal retains its observed exit 1. Ineligible failures stay
  unexcused with `quarantine not applied: <reason>`. Known limits:
  `-q`/`--format terse` and more than 50 failures are not excused (SPEC §7.16).
- **Quarantined tests that pass again.** Complete trunk and merge-tree runs
  recognized natively record which active quarantine names passed; status
  debt shows `quarantined <test> passed on trunk <sha12> (#<seq>)` or identifies
  the merge tree of the node (or batch members), with a hint to remove it if
  it stays green. Explicit `--probe-trunk` runs use the same scope;
  candidate-only passes do not record recovery feedback. No effect on
  verdicts, keys or counts (SPEC §7.16).
- **Display-only fields no longer force readers first.** An entry may
  list display-only fields in `ignorable`; a 0.27 reader treats listed unknown
  fields as known without interpreting them, while known listed fields are
  still interpreted and validated. This owed lists `waive.tracked_by` and
  `obs.quarantine_passed`. Unlisted unknown fields keep existing refusal rules;
  unknown kinds, channels and enumerated values remain refused. The marker
  does not cover plan keys or nested receipt fields. Readers before 0.27
  still refuse `ignorable` where they refuse unknown fields (SPEC §5).
- **Long parent commands without a waiting agent.** The driver reference
  describes running a long command outside the driver (a manual attest or a
  full suite) as a pi-durable-subagents ≥ 1.0.45 command call: no model, no
  slot, a wake on exit. Owner and parent acts such as a manual merge are
  still refused inside it.
- **Docs.** `owed rebase <node>` without `--apply` records the slot-base
  move and prints the writer's `git rebase --onto` command; the docs now say
  so.

## 0.26.0

No new ledger entry kind, plan key or gate, and no gate weakened. One new
optional ledger field, `obs.infra`, and one widened validation (a setup-only
automatic remeasure) need an upgrade order: **upgrade every reader (CLI,
drivers, every pi session loading owed) to 0.26 and restart it before any
0.26 executor measures** (attest, genesis/trunk, merge or adopt). 0.25
refuses a ledger with `obs.infra` and refuses a setup-only `auto_remeasure`
receipt. Other additions are runtime or result data only (SPEC
Compatibility).

- **A stopped writer is a question, not a missing submit (WS).** A writer
  that sealed without a candidate and left nothing new (worktree HEAD is one
  the driver handed it, clean) is no longer told to submit: the driver halts
  at once, the wake, `owed why` and `owed status` quote `writer says: "<its
  last paragraph>"` and give one command, `owed decide <node> --text
  "<ruling>" --writer`. A writer with new work still gets one submit nudge.
  The same rule now covers a writer sealed after a rebase follow-up, which
  could previously wait silently. After such a halt, the decision is
  delivered as exactly one ruling follow-up; a further seal without a new
  candidate halts naming it, with no submit nudge. Every writer message says
  to ask the parent instead of submitting a candidate it believes fails.
  `owed rule` on a node whose known writer call has finished now says the
  ruling was recorded but not sent, that the driver delivers it on its next
  pass, and, for a halted node, to run `owed resume <node>` (or `owed
  decide`, which records and resumes in one step); `rule` itself still
  sends no follow-up (SPEC §9.3, §12.5.4).
- **Asks say what is asked (AK).** A needs-parent review block shows `review
  asks: <the ask>`, not the head of the reviewer note. Trunk-conflict and
  trunk-red owner needs name the failing tests and the real exits instead of
  the `owed plan <plan.yaml>` placeholder. Old ΔO⁻/downgrade lines appear only
  with `--full`; a merge-waiting line is no longer counted as outstanding
  (SPEC §9.2, §9.6).
- **`--ack-rulings` is checked before anything is recorded (AK3).** A value
  below the required ruling refuses with the number to use;
  `--ack-rulings latest` / `ack_rulings: "latest"` records the computed
  number (SPEC §9.2).
- **`owed start --restart` (AK6).** Stops a running driver gracefully, waits
  for it and starts again; a graceful stop signals each driver process at
  most once, so repeating a restart is safe (SPEC §10.2, §12.8).
- **Setup failures belong to the environment (IF).** A setup failure is a
  failed run of a counted check or invariant (its spec has `min_tests`, its
  output parsed into a counts format, or an earlier run of it was counted)
  that asserted no test result: no count, no failed name, not a timeout or
  incomplete run. A single-node merge-tree or trunk invariant setup failure
  gets one automatic remeasure on the same key, even without
  `remeasure_on`; a node check's setup failure is retried after
  `owed decide <node> --remeasure`. The next counted pass on that key that
  meets the original spec records `infra: <seq>` and resolves the
  consecutive setup failures before it: no conflict, flaky block, waiver or
  debt. Hints say `setup failure (no tests ran)` instead of suggesting a
  rebase, and the same setup failure on two or more trees within 30 minutes
  raises one repository notice. `why --full` labels resolved setup failures
  `infra`, and `report --metrics` counts infra receipts per obligation.
  Anything with a test result keeps the existing conflict rules; batch
  observations and trunk probes are not paired (SPEC §7.26, §9.4).
- **Where the machine time goes (CO).** `owed report --cost [--since]
  [--json]` lists machine time per check and invariant (runs, total,
  median/p90, failures, lease wait), how often each invariant was measured
  per merge and which path prefixes changed its key, with a `narrow reads`
  advice. Plan lint warns when `reads` covers almost the whole repository or
  a check reads a closure path; `owed status` warns when an invariant's trunk
  pass took 80 % or more of its timeout (SPEC §9.7).
- **Ready nodes say why they wait (WA1).** `owed why` and `owed status` show
  `ready, not dispatched: writes overlap <node> (<paths>)` with the
  `--allow-overlap` command, or the open-attempt limit, from the same
  predicate the driver uses (SPEC §9.8).
- **Writes widening is forgiving (WA2).** In `decide`/`amend`, a bare path
  means `+path`; a path the node's writes already cover is reported as
  `already covered by <entry>` instead of refused, and a decision whose paths
  are all covered records no plan entry (`writes unchanged`) (SPEC §8.3.1).
- **Quieter status and safer batches (WA3, WA4).** The default status omits
  conflict lines whose paths are all `none`. A batch merge is refused for a
  plan change during the batch only when that change affects the batch's
  members, invariants or guard inputs (SPEC §7.21, §9.2).
- **Smaller fixes (NT).** `owed --version` / `owed version`; HELP lists every
  `rebase` flag; the in-progress recovery hint names `git cherry-pick --abort`
  / `git revert --abort`; a refused driver send survives a driver restart; a
  question text can no longer hijack its answer command; `report
  --counterfactual` hides an incomplete decision group like other readers;
  the ledger lock treats a symlinked repository path as the same holder
  (SPEC §7.23, §7.25, §9.4, §10, §12.5.3, §12.8.3).
- **Docs.** SPEC, README and the skill describe all of the above; the skill
  core stays under 5 KB, and the Compatibility reference gives the upgrade
  order. The concurrent-adopt test no longer depends on which adopter wins
  the merge lock (test only).

## 0.25.0

No new ledger entry kind, plan key or gate, and no gate weakened. The only new
optional ledger field is `genesis.path` (the plan source recorded by `owed
start`); owed 0.24 ignores it, so no upgrade-before-write order is needed.
The omitted `drive.answer_grace_s` default changes from 900 to 600 seconds;
a recorded value keeps its meaning. Restart pi to load the new tool profile
and the CLI/driver to get `owed start` and the quiet wake text (SPEC
Compatibility).

- **A small tool surface (SF1).** pi declares seven core tools directly:
  `owed_status`, `owed_why`, `owed_decide`, `owed_drive`, `owed_start`,
  `owed_submit` and `owed_review`. Every other owed tool stays registered as
  deferred and is found with the host's `tool_search`. At session start owed
  activates `tool_search` when the host offers it; when discovery is
  unavailable or cannot be activated, owed registers the deferred tools
  directly instead, so no tool becomes unreachable. `OWED_TOOLS=all` declares
  every tool directly (SPEC §11.2).
- **One start command (SF2).** `owed start [--plan <file>] [--max N]
  [--stay]` / `owed_start` runs a read-only preflight (Git identity, trunk
  ref, plan parse and lint, driver lock), initializes a missing ledger as the
  delegated owner, records a changed plan and starts the background driver.
  It prints three lines (what it did, the driver state, `Next:`), plus one
  line per lint warning. A second start with an unchanged plan and a running
  driver says `already running` and records nothing. Refusals name the failed
  preflight and its fix; under `OWED_CONFIRM=owner` a fresh start refuses with
  `owed init <file>` instead of opening a dialog (SPEC §10.2).
- **Dispatch prints its packet once (SF3).** The subagents arguments refer to
  the packet above; the structured `subagents.task` still carries the full
  task (SPEC §11.2).
- **Wakes say what is needed (QT1).** Each need is one line, `Needs you:
  <node> — <what> → <one command>`; every open question keeps its own line
  and answer command; repository notices (shared failure, trunk red or drift,
  sweeps) keep their full text. Merges collapse to `Done: <nodes>`. Raw driver
  lines stay in the driver log and `owed why --full` (SPEC §12.8.3).
- **A why card with only what is outstanding (QT2).** `owed why <node>`
  shows the outcome, then one line per unsatisfied obligation or active block
  with its reason and next command, plus the ruling to forward, the measuring
  and merge-waiting lines and the risk-derived review requirement.
  Observations, history, superseded entries, ruling texts and ΔO⁻ appear with
  `--full`; JSON is unchanged (SPEC §9.6).
- **Repair follow-ups list only new failures (QT3).** A follow-up to a
  writer names the failures new since the last message and the next step, and
  points to `owed why <node> --full` for the rest (SPEC §12.5.3).
- **Merge receipts in one line (QT4).** Single and batch merges print
  `<node> merged (<commit12>); measured <n>, waived <n>, debt <n>`, one line
  per batch member; debt counts only deferred obligations on this merge's key
  (SPEC §8.3.4).
- **Answered but still asking (QT5).** A question whose answer was recorded
  at least `drive.answer_grace_s` (default now 600 s) earlier while the runner
  still lists it as open reads `<node>: answered at #<seq> (HH:MM), still
  asking after <m>m: retry with owed answer …` with the exact retry, not a new
  question. `owed status` shows it only while a driver runs (SPEC §12.8.4).
- **Docs rewritten around the loop.** The owed skill core is under 5 KB: what
  owed is, the core tools, the start → wait for the wake → decide → merged
  loop, the three roles and six hard rules; everything else moved to
  `skills/owed/references/*.md`. The README opens with what, why, a
  quickstart with `owed start`, the current version and the delegated owner
  default; the rest is reorganized under reference headings. Release notes
  for 0.20–0.25 are folded into SPEC (Compatibility (0.20–0.25) and the
  sections cited above; the skill is described in §11).

## 0.24.0

No new ledger entry kind and no gate weakened. New optional plan keys:
`sweep.every`, `risk`, `closure_neutral`. New optional ledger fields:
`sweep.boundary` and `escape.sweep`. **Upgrade order:** upgrade the CLI, every
driver and every pi session that loads owed (restart them) before a 0.24
sweep records its first confirmed boundary or automatic escape, and before
the plan sets `sweep.every`, `risk` or `closure_neutral`. Older owed refuses
the new ledger fields and a plan with `sweep.every`. It ignores `risk`, which
can raise or lower review requirements, so a merge by either version under a
different requirement makes the other version refuse the ledger;
`closure_neutral` alone is only conservative (SPEC Compatibility).

- **Scheduled sweeps that find their own culprit (SW).** `sweep.every:
  {merges, minutes}` makes a running driver start the advisory sweep in the
  background when either threshold is reached, alongside node work, under the
  shared machine lease. `owed sweep --due` measures only when due, so it can
  run from cron. A head that fails twice, with a confirmed first failing owed
  merge (two failed samples) and a passing predecessor, records a `missing`
  escape by `executor:owed` with one notice naming the merge, the broken tests
  and a coverage hint; an existing escape for the same merge and names
  suppresses a duplicate. Flaky heads, inconsistent bisects and errors only
  report. Sweeps still never gate a merge (SPEC §3.9, §7.15b).
- **Review effort follows the diff (RK).** The optional plan list `risk`
  (`{paths, review: {count, min_rank}}`, first match wins per changed path)
  derives each candidate's review count and rank from its diff; classes may
  lower or raise the node's review. `closure_neutral` (`{path, json_ignore}`)
  lets a closure file such as package.json change only the named JSON
  properties (for example a version bump) without a closure review. A
  candidate whose closure changes are all neutral counts as not touching the
  closure: it may join a batch merge, and ruling delivery does not wait for
  a closure reviewer.
  Lowering either policy needs the owner, even under a review allowance.
  `why`, status and reviewer packets show the derived requirement; `owed
  report --metrics` counts reviews by the writer's own model
  (`reviews.sameModel`) (SPEC §3.8, §6.2a, §7.21, §9.4, §9.5).
- **Hotfixes land while the driver runs (AD).** `owed adopt --commit <c>
  --note …` fast-forwards trunk to an off-ref owner hotfix under the merge
  lock: a running merge finishes first, the changed invariants are measured,
  and the ref is advanced before the adoption is recorded. A hotfix that is no
  longer a fast-forward is refused before measuring, naming the new trunk. A
  commit already in the ledger trunk is refused with `nothing to adopt`
  (SPEC §8.3.2).
- **Retrying an act is safe (ID).** Repeating an identical `review`,
  `evidence`, `approve` or `waive` on the same open candidate records nothing
  and succeeds with `already recorded as #<seq>`. `why` and `report` fold runs
  of identical historical acts into one line with `×<k>` (SPEC §8.3.3).

## 0.23.0

No new ledger entry kind and no gate weakened. New optional fields:
`submit.facts.carry`, `submit.facts.rebased`, `defer.allowance`, `decision`,
rule `answers` and `lease_wait_ms` on `obs`/`merge`. New plan permission:
`allow[].defer`. **Upgrade order:** upgrade the CLI, every driver and every pi
session that loads owed (restart them) before the first 0.23 `attest`, `merge`
or executor rebase. Leasing is on by default, so the first 0.23 measurement
that takes the machine lease records `lease_wait_ms`, and owed 0.13–0.22 then
refuses the ledger with code `version`. owed <= 0.22 also ignores review
carry, which is conservative and means a new review, and refuses executor
rebase submits (SPEC Compatibility).

- **Shared failures wait instead of halting (FS).** A test-only failure whose
  every failed name also failed on a measurement without the candidate (trunk,
  or another node's merge tree) is *shared*: evidence that the failure is not
  the node's, never a pass. Names must match with the same signature, or by
  name alone when neither side recorded one. The driver parks an accepted
  candidate, without halting it or messaging its writer, when every merge
  refusal is shared invariant debt on its actual merge key, or an automatic
  trunk-key owner conflict whose key provably equals its merge key; any other
  refusal halts as before. The node shows `waiting: shared failure …` or
  `waiting: trunk red …` and is reconsidered without a resume when trunk, the
  plan, the candidate or its defers change. Eligible shared invariant failures
  get one automatic same-key remeasure within the existing retry budget;
  `repeat` invariants and failures already red on the latest earlier trunk key
  are not remeasured. One repository notice per condition replaces per-node
  halts. An accepted, non-halted node whose title, brief, check or test list
  names a failing test merges first, and parked nodes show `waiting: fix
  <node>`. Shared failures with the same names on three merge keys of one
  attempt halt as before. New plan permission `allow[].defer` lets owed defer
  a shared, intermittent invariant failure as visible debt during a
  single-node merge, only when the same merge key also has a pass and every
  shared match has a recorded matching signature (SPEC §3.4, §7.24).
- **Clean rebases need no writer turn and no new review (ER).** When a rebased
  candidate's tree equals the clean merge of the reviewed candidate onto the
  new base, its reviews carry over. Checks, red evidence, approvals and
  invariants are not copied; they keep their exact keys, so they are measured
  again only where the key changed. When the writer run is recorded and
  sealed, the worktree is clean and HEAD is the candidate, the driver runs the
  rebase itself and submits as `executor:owed`. A conflict aborts the rebase
  and falls back to the writer. `owed rebase <node> --apply` does the same on
  demand (SPEC §7.25).
- **One decision, one act (DC, DF).** `owed decide <nodes> --text …` and
  `owed_decide` record a ruling (or a writes amend), answer open questions,
  defer an obligation and resume halted nodes under one ledger lock. Nothing
  is recorded unless every part is valid. The reply is one line. Halt and
  Needs-you hints suggest `decide`; the old commands keep working. Unlocked
  readers hide a decision until its ruling is written; its trailing resumes
  may land a moment later, so a node can briefly still show halted. Locked
  loads and `verify` refuse a decision missing its ruling. The hint names
  `owed answer --rid --qid` per question when a node has several open
  questions. Metrics count each answer of a decision (SPEC §8.3.1, §9.4, §10,
  §11).
- **Measure what changed (MX).** `owed report --metrics` adds decisions, acts
  per decision, interruptions by class, per-phase latency medians and p90
  (measure, review, merge wait) and the median machine-lease wait. `owed
  report --counterfactual` replays recorded halts through the 0.23 policies
  and estimates the parent acts they would have saved. It is read-only and
  unlocked (SPEC §7.14a, §9.4).
- **0.22.1 leftovers (LO).** Merge-wait reasons store structured times and
  show local time when displayed. A node whose attest or merge is running
  shows `measuring` or `merging (since HH:MM)` instead of a stale wait label.
  A wait on a driver job that has finished is hidden. `owed rule` replies with
  one line `Recorded #S ruling (<nodes>); delivered k/n`, plus one line per
  delivery failure. Its JSON message is no longer double-escaped (SPEC §9.3).

## 0.22.1

Patch release. No new ledger entry kind, no new field, no gate weakened;
upgrade drivers, CLI and pi plugin together as for 0.22.0.

- **Refuse candidates that already contain newer trunk commits (SB1, SB3).**
  `owed submit` (also `--commit`) refuses, recording nothing, a commit whose
  history contains trunk commits the slot base does not (a writer merged trunk
  or rebased onto it without `owed rebase`). The message names the slot base,
  the latest contained trunk commit and the fix. Such candidates recorded
  before 0.22.1 fail `writes` with one line naming the cause and grant nothing,
  instead of listing other nodes' merged paths as outside writes (SPEC §6.1,
  §6.2).
- **Rebase commands never replay trunk commits (SB2).** The command in the
  `owed rebase` packet, driver rebase/drift/repair follow-ups and the SB1
  refusal uses `git merge-base HEAD <new base>` as upstream when that descends
  from the recorded upstream, so it replays only the node's own commits
  (SPEC §12.5.2).
- **One-line answers (SB4).** A successful `owed answer` / `owed_answer`
  prints one line, plus one more only when the ruling reached or failed to
  reach other running calls; answer and follow-up texts are not echoed
  (SPEC §12.5.1).
- **Local times (SB5).** Every `HH:MM` shown in text uses one local-time
  formatter, with the local date when not today; ledger and JSON fields stay
  ISO UTC (SPEC §9).

## 0.22.0

Compatibility first: 0.22 adds no new ledger entry kind, only optional fields
on existing entries and optional plan keys. Upgrade every driver, CLI and pi
plugin together; enable `drive.merge_batch`, request explicit batch merges,
and rely on writes `waits_on` only after every driver, CLI and plugin runs
>= 0.22. The same upgrade requirement applies to writes globs and
`remeasure_on`: older readers can misjudge batch guards, interpret globs
literally or refuse new fields. String-template `exec.wrap` changes measurement
keys only when set; absent wrappers and legacy argv wrappers keep their keys.
Versions 0.20.1 and 0.21.0 were never released separately; their work is folded
into 0.22.0. No acceptance gate is weakened (SPEC Compatibility, §7.22).

- **Quote and attribute failure evidence (SG).** Versioned `v2|` signatures
  include the masked first error, innermost repository test frame and masked
  expected/actual values. Historical untagged signatures replay but never
  equal new signatures, including for defer carry. `known intermittent`
  requires same-key pass/fail proof and a matching signature when known;
  a same-signature trunk failure alone is not intermittent. Current trunk
  failures show the start of their uninterrupted run and merge/adopt provenance.
  Refusals quote up to three failed tests (SPEC §5, Attribution, §7 RC3).
- **Retain timeout diagnostics (SG5).** Timeout logs retain the last 256 KiB
  of measurement output, a timeout marker and a best-effort pre-kill process
  tree. Check/invariant `on_timeout` runs once with a separate 30-second limit.
  Evidence freezes at timeout; diagnostic output cannot change counts,
  signatures or verdicts, and diagnostic-only edits change no measurement key
  (SPEC §7.18).
- **Resolve model warnings without changing gates (MD).** Direct selections
  and nested dsa pool members compare by `provider/id`, ignoring thinking
  suffixes; unresolved membership remains unknown. Global/node
  `drive.same_model: accepted` hides only the advisory same-model warning in
  compact why; full/JSON retain it. JSON `modelWarnings` admits acknowledged
  objects as well as strings. Acknowledgment preserves candidate commits and
  keys, and independent review blocks remain binding (SPEC §12.2, §12.5.2).
- **Retry dispatch quietly and explain merge waits (DQ).** Dispatch CAS
  refusals retry with fresh state and completed rollback, at most three total
  attempts; exhaustion says `retry next pass` without a halt. Status names the
  blocking merge job, its UTC start and accepted merges ahead; JSON adds
  runtime-only `mergeWaiting` job/ahead fields (SPEC §8.1, §9).
- **Match writes patterns and preserve their dialect (WG, NT).** Node/allow
  writes and adopt scopes accept `*`, `?` and whole-segment `**`; `dir/**`
  equals `dir/`. Prefix behavior remains, containment and overlap are
  conservative, and unproven widenings still need owner authority. New plans
  record `writes_syntax: 2`; historical entries retain their literal dialect.
  Plan edits may preserve invalid legacy entries at their original positions,
  but new invalid entries refuse. Runtime copies preserve the dialect without
  changing plan JSON or its hash (SPEC §3.7).
- **Grant eligible files and wait for owners (WG4–WG5, NT).** Independently
  eligible paths receive exact grants even when other paths refuse. Structured
  `grant_refused` drives hints without parsing old failure text. Overlap-only
  `obs.waits_on` failures wait for named slots to merge or abandon, then the
  driver remeasures writes once on the same key; the later observation
  supersedes that overlap-only failure. Mixed refusals retain normal rules;
  waiting cycles halt with a repair action. Without a driver, attest after
  slots close (SPEC §3.4, §7.19).
- **Yield merges to waiting adoption (AW).** `owed adopt --wait SECONDS`
  supplies one total lease/lock acquisition budget, default 60 seconds;
  finite nonnegative fractional values are allowed. Pending adopters let the
  running merge finish and defer new merges to the oldest adopter. Busy text
  identifies holder activity and its UTC start; measurement timeouts remain
  separate (SPEC §8.4).
- **Lint shell commands before admission (PL).** New/changed commands in
  init/plan/edit/amend, including dry runs, expanded templates, setup/slot
  setup and timeout diagnostics, receive `bash -n` checks. Statically quoted
  nested Bash scripts are checked without executing expansions. Unchanged
  legacy commands remain loadable; missing Bash warns, syntax errors refuse,
  and computed scripts remain a known limit (SPEC §3.7).
- **Remeasure eligible invariant failures once (RO).** Invariant
  `remeasure_on` matches literal case-sensitive raw failure diagnostics;
  every failure must match. Missing/incomplete diagnostics, errors and
  timeouts do not authorize retry. Eligible measurements get one durable
  same-key automatic retry. Both votes remain: repeat pools them, otherwise
  pass plus fail requires the owner. Policy edits change no key or exhausted
  budget, and a node-specific defer cannot resolve trunk conflict or authorize
  unrelated merges/adoption (SPEC §7.20).
- **Merge accepted nodes on one measured final tree (MT, NT).**
  `drive.merge_batch: N` accepts 1..8, default 1/off; explicit
  `owed merge A,B,C` requests a batch independently. Accepted members follow
  merge-queue order, have non-overlapping writes, touch no closure and owe no
  remeasurement; approval gates still apply. Invariants and member checks run
  on the final tree, trunk advances once, and every member compares inherited
  debt with pre-train trunk. Conflicting members drop out for individual
  rebase/merge. Per #2270, failed batch votes never block a member or carry
  `remeasure_match`/`auto_remeasure`; fallback merges measure each member's own
  tree with its own eligible automatic retry. The failed set is not batched
  again until trunk moves. Replay rejects batch retry metadata. Planned batches
  exclude waiting, later, halted, parked and reported nodes; running members
  identify their leader. Torn-tail recovery includes oversized non-final
  member lines and requires the final trunk ref/history; adopt cannot close
  an incomplete batch (SPEC §7.20–§7.21).
- **Wrap measurement commands with explicit timeout semantics (RX).**
  String-template `exec.wrap` substitutes shell-safe `{cwd}`, `{timeout}` and
  required `{cmd}` for setup, checks/red/mutants/invariants/probes/sweeps and
  timeout diagnostics. Git, worktrees, ledger and leases remain local.
  `exec.wrap_grace_s` defaults to 30; evidence freezes at timeout while the
  wrapper gets shutdown grace. Wrappers must propagate output/exit status,
  avoid test-protocol noise, bound remote work and propagate termination;
  local cleanup does not prove remote termination (SPEC §7.22).
- **Enforce the ledger lock boundary (LK).** Command/job runners refuse to
  spawn while their async context holds the ledger lock; destructive/worktree
  Git operations are guarded too. Short recording locks retain CAS and stale
  result checks. The sole Git exemption is `advanceTrunk`'s ff-only advance of
  an already measured commit; owed never detaches trunk. Known limit: merge
  hooks still run under the ledger lock and can delay writers, and the guard
  cannot detect an external process's lock (SPEC §7.17, §7.23).
- **Give actionable review and conflict guidance (UX).** Active blocks suggest
  writer-action rulings and repair; owner review is labelled an override.
  Zero-call ruling deliveries list affected slots and exact follow-ups.
  Status/why show conflict files against trunk and other candidates; cached
  probes are diagnostics, not acceptance evidence. Missing ruling
  acknowledgments warn with a candidate-pinned re-record command while still
  recording the verdict; `--ack-rulings` remains a numeric watermark
  (SPEC §9.2).
- **Wake stalled repositories without starting a driver (WD).**
  `owed idle [--json]` exits 0 for idle, 3 for not idle, 2 for usage/missing
  ledger and 70 for internal errors. Open work, no live driver/held lock or
  machine holder/matching labelled active agent, and sufficiently old progress
  are required. `drive.watch_idle_min` defaults to 15 minutes; 0 disables it.
  Pi checks every 120 seconds, wakes only a settled agent and atomically claims
  one notification per host/repository/idle period across sessions. Detection
  is advisory: unknown subagent activity uses age and other conditions, failed
  installed machine inspection stays quiet, and unlabelled calls or a crash
  between claim and delivery can hide activity or a wake (SPEC §12.8.2).

## 0.20.0

Compatibility first: 0.20 adds no new ledger entry kind, only optional fields
on existing entries and optional plan keys. Upgrade every driver, CLI and pi
plugin together: older versions can ignore, misread or refuse these additions.
Versions 0.18 and 0.19 were not published; their work is folded into 0.20.
Without the additions, keys and replay remain as in 0.17 except for pooled
repeat across several observations on one key, described below. No acceptance
gate is weakened. Origins: field feedback and the parent's ledger metrics:
merge-tree invariant refusals accounted for 28 of 70 driver halts, and 16
test-path widenings were still amended manually although already covered by
`allow`.

- **Trust same-key measurements (measure, MS1–MS5).**
  `owed attest <node> --remeasure [--all]` remeasures current prospective
  merge-tree invariants only, under the shared machine lease, on the same
  keys; `owed attest --trunk --remeasure` targets current trunk invariants.
  `owed resume <node> --remeasure` requests that measurement before the
  driver's next merge. Nothing else is invalidated. Without repeat, pass
  and fail on one key remain an owner conflict. With `repeat {runs:N,
  need:K}`, every run of every non-error observation on that key is pooled:
  pass iff `passes*N >= K*total_runs`, otherwise fail, with no window or
  cherry-picking. An old multi-observation repeat conflict can therefore
  replay as pass or fail. Refusals that reuse an earlier failing observation
  show its seq, age and exact remeasure command.
  Owner-only `quarantine: [{test,fix,note?}]` on checks/invariants excludes
  exact failing names while the fix remains unmerged; its own prospective
  merge is measured without that quarantine and must repair the tests.
  Exemption is fail-closed: only a plain allowlisted `node --test` invocation
  completing with exit 1, one complete TAP plan, all failures exactly named
  and quarantined, and enough remaining tests can pass. Quarantined tests
  count neither passed nor failed. Cargo, templates, wrappers, red/mutants
  runs, crashes and timeouts are not excused. Status, brief and report list
  quarantine debt; `why <fix>` lists what the fix's merge lifts.
- **Attribute the actual break (attrib, AT1–AT5).** A merge-tree invariant
  failure with passing current trunk, after trunk moved at least one commit
  since the slot base, makes the first sentence of refusals, driver halts
  and Needs-you reasons lead with `merge tree does not pass ... (trunk
  passes)` and the rebase remedy, not a trunk-red waiver/sweep/adopt hint.
  Checks whose declared test files are new use the slot base, not trunk,
  as their red reference. New submits are attested before old-candidate
  merge blocks can park or re-halt them; submission is not a waiver.
  Intermittency hints require same-key pass/fail evidence or matching
  normalized error signatures on trunk, not just the same name on another
  tree. AT5 defer carry remains debt and is fail-closed: deferred, current
  trunk and current merge failures must all be test-only with complete
  names/counts; current names must be a non-empty subset of deferred names,
  already failing on trunk before defer, with identical signatures on all
  three observations and the current `min_tests` met. Missing proof, new
  failures, crashes or timeouts leave the defer bound to its old merge key.
- **Show actionable state (views, NY1–NY7).** Repair follow-ups or newer
  submits show `repairing (block #<seq>)`, but NY1 removes the review item
  from Needs you only when `ownerNeeded(s,node)` is undefined. Rank-3,
  non-driver-authored rank-2 and flaky/owner-conflict cases stay actionable
  even while repairing. Guidance precedes the explicitly risk-labelled
  owner override. Owner approval stays waiting until dependency/resume
  waits clear; only a review owner conflict says an owner decision is
  needed. Plan replies lead with their recorded receipt, wake reasons and
  questions appear once, and `why --full` restores quiet confirmation
  history. Worktree ancestry drift shows `rebase pending`; the driver sends
  one follow-up per attempt/base, with a computed upstream and commit-first
  rebase command.
- **Serve the critical path with honest receipts (driver, DR1–DR4).**
  Merge-ready nodes outrank background trunk attest/sweeps, ordered by
  unfinished transitive dependent count, then continuous acceptance age.
  The driver preempts only its own trunk attest/sweep, never external
  measurements or node attests. Plain status shows both `merge waiting for
  <reason> since <HH:MM>` and DR2's `measure waiting for <reason> since
  <HH:MM>`; accepted age starts at acceptance. Submit/review accept `--model`
  (pi `model`) and record actual models in optional fields; why reports
  matching/mismatched-model warnings without changing gates. Non-prospective
  rules immediately notify running/asking roles; answers notify other roles
  while answering the selected call. DR4 request ids bind ruling seq, rid
  and exact message-body digest: identical retries are idempotent, changed
  bodies get new ids. Receipts retain delivered/failed results and exact
  answer retry commands; delivery failures never trigger a retry loop or
  acknowledge rulings.
- **Remove unnecessary parent round trips (parentops, PO1–PO7).**
  An amend wholly covered by current `allow.writes` refuses as unnecessary,
  recording nothing: writers edit task-needed covered paths freely and owed
  grants them at measurement, rather than requiring a manual amend.
  `owed resume A,B,C` (pi `node` array) validates the whole batch under one
  lock and records one resume per node, or nothing on refusal. Metrics add
  per-node attribution, attributed acts per merged node, pending and
  unattributed acts; batches count once. Plan/edit dry-run warn about
  dependency-free write overlaps on the critical path, without new gates.
  PO5 attest exits 1 if any observation recorded by that action failed,
  including known-red/probe failures; otherwise 0 means complete and 4 means
  obligations remain. Usage 2, internal error 3, refusal 1 and busy 75 retain
  their meanings. The driver treats 0/4 and measured failure 1 as completed
  work, 75 as busy, and internal/other errors as halts, never silent retries.
  PO6 omitted ruling acknowledgments derive only from a qualifying driver
  reviewer launch packet's largest byte-exact complete prefix of applicable
  pre-launch rulings, never the maximum isolated match; later rulings remain
  owed. Brief edits preserve multiline, colon-bearing and equals text while
  empty values still refuse.
- **Version and count check templates (checktpl, CT1).** Plan loading expands
  `cargo-tests`, `node-tap` and `evidence-json` into frozen v1 commands;
  expanded `run`/`min_tests` determine keys and appear in packets/receipts.
  Unknown templates, missing parameters or combining `run` and `template`
  refuse. `min_ok` counts successful tests only: Cargo sums its summaries;
  TAP uses final summaries and subtracts successful empty file wrappers only
  when the top-level name matches an actual input file and has no subtests.
  Real tests with file-like names still count; skips/ignored tests do not.
  Missing summaries, insufficient successes and runner failures still fail.
  JSON evidence checks own-field presence, not truthiness, so `false`, `0`,
  empty strings and `null` remain valid present values. Templates infer no
  reads/closure and install no dependencies; quarantine does not exempt them.
- **Tolerate an append in flight, not corruption (tornread, TR1–TR3).**
  Unlocked `Ledger.read()` ignores only a final fragment without a trailing
  newline, returning preceding complete entries with the chain verified.
  Reads under the lock still reject a torn tail, and malformed complete
  lines always fail. This adds no entry kind or plan key.

### Not in 0.20

A unified `decide` verb, a lighter release flow, prospective merge-tree
invariant measurement before review, and stopping surplus reviewers remain
future work. Cargo quarantine exemption/completeness proof and MS4 allowlist
hardening against `PATH`/`NODE_OPTIONS`, `--require`, `--import` and
`--test-isolation` are backlog; quarantine is not a sandbox. TR covers only
`Ledger.read()` on the same lock-tracked instance, not every unlocked reader.
Fix merges refresh trunk/merge quarantine keys, not an open candidate's stored
submit check keys, and remeasure targets invariants only. Pooled repeat is the
explicit old-ledger replay exception, not an unchanged-replay promise.
Historical submit/review entries are not retrofitted with model metadata;
unavailable dsa status leaves `model unknown`. See `docs/SPEC.md` §7.17.

## 0.17.0

This release delivers the merged nodes grant (GR), trunkpark (TP),
parentacts (PA) and granthint (GH), removing pre-authorized writes and
trunk-red coordination round trips while counting the parent's work honestly.
Origins: production ledger metrics (2026-10-13 22:2x: rulings per merge rose
1.36 → 2.93, with widenings 1.38 and trunk-red 0.86 per merge) and the
three-model product panel (2026-10-13). Parent rulings pin the behavior
below; no acceptance gate is weakened.

- **Grant task-needed writes under owner allowances (grant, GR1–GR8).**
  Shared attest paths record an executor-only `grant` before a passing
  writes observation when every new out-of-writes path is covered by the
  current owner's `allow.writes`. Replay validates the open attempt,
  candidate commit, sorted exact changed paths and allowance plan sequence;
  closure paths, another open slot's writes and duplicate grants refuse.
  A refusal grants nothing and leaves writes failing with its first reason.
  Effective writes combine plan prefixes with exact attempt grants for
  measurement, merge recheck and dispatch overlap. Grants survive plan or
  allowance changes, but a new attempt starts with none; writes keys do not
  change. Packets show owner pre-authorization and ask reviewers to block
  grants outside the node's task. Receipts show `Granted writes`, and
  report, brief and receipt ΔO⁻ label grants `granted by owed under allowance
  (plan #S)`. Uncovered failures suggest a deduplicated parent-directory
  allow rule in receipts, why, merge refusals and driver repair/halt text;
  hints record nothing, and a ruling never accepts out-of-writes paths.
  Open writer slots retain dispatch-plan write ownership after their nodes leave the plan, preventing overlapping dispatches and grants until those slots close.
- **Park entirely trunk-red candidates (trunkpark, TP1–TP5).** When all
  binding blocks, including flaky binding debt, are trunk-red on current
  trunk, the driver records no halt and sends no writer follow-up, launches
  no reviewer and performs no attest; questions still notify. Mixed blocks
  retain the existing behavior, and merge refusals retain every gate.
  One repository notice per trunk commit, obligation and sorted failed-name
  state lists waiting nodes and fix/adopt, optional sweep or owner-waiver
  options. Status and brief show one `Needs you` line per red state and
  `waiting: trunk-red (<obligation> on trunk <trunk12>)` in node progress;
  queued notices recompute waiting nodes at delivery using the full key.
  Trunk movement resumes the existing measurement/rebase route without a
  resume entry. Parent rulings #1813/#1814 limit deduplication to the
  existing per-process once set: a restarted driver may print the repository
  notice once again, but never records a parked-node halt.
- **Measure parent coordination acts (parentacts, PA1–PA4).**
  `owed report --metrics` adds `parentActs: {total, perMergedNode, byClass}`
  and `grants: {total, perMergedNode}` while retaining every old JSON field
  and value. Non-driver owner/parent decisions count once per entry within
  the window, excluding dispatch, launch and send transport; classes cover
  writes, answers, trunk work, resumes, rules, plans, owner reviews and
  remaining act kinds. Writes-only plan downgrades and amend rulings count
  as writes; amend recognizes the existing `writes of <node> widened by
  plan #<seq>: <paths>. Limit: <note>` marker and counts the plan and ruling
  separately. Earlier history supplies plan and last-halt context, never
  entries after `until`. Rates divide by merges in the same window, or are
  `null` with none; grants are zero on older ledgers. Text leads with
  `parent acts per merged node` and top classes, then grants, and labels
  the old interruption rate `driver halts + answers per merged node
  (partial)`. `parentActs.perMergedNode` is the 0.17+ gate metric for parent
  coordination cost, not a weaker acceptance condition.
- **Make grant recovery hints lead to acceptance (granthint, GH1–GH3;
  grant review #1850).** After a writes key failed, adding a covering allow
  rule and following attribution with `attest --rerun` on that same key
  creates a pass/fail owner conflict, not acceptance. The hint instead says
  to submit a new commit (any change, e.g. an amended commit message) so
  writes are measured again under the new allow rule. A mixed trunk-red
  merge refusal also carries the uncovered-path allow hint once, without
  changing refusal precedence or any gate.

### Compatibility

Owed 0.17 adds exactly one ledger entry kind, `grant`. Owed 0.16 refuses a
ledger containing one, so every driver, CLI and pi plugin sharing a ledger
must upgrade together before the first grant is recorded. A ledger without
grants replays exactly as in 0.16, retaining its states and obligation keys
apart from the derived views described above. TP and PA are derived state,
views and driver behavior, with no new ledger kinds or fields; GH changes
only hints. No acceptance gate is weakened.

## 0.16.2

This release delivers the merged node refusalnotice (FG, TD), showing distinct
failure causes and the trunk-vs-merge-tree delta in merge refusals without
weakening any gate. Origins: field feedback on owed 0.16.0 in
`/tmp/dsa/owed-requirements.md` (2026-10-13 15:1x): AQ-Q halt #7285 listed
11 failures with the same `exact-source host required` precondition, but
finding the cause required a blob lookup; the invariant had passed on trunk
698517a8 (#7271) about ten minutes earlier without that context in the notice.

- **Group failing tests by error (refusalnotice, FG1–FG3; field report
  AQ-Q feedback).** New measurements parse node TAP YAML `error` and
  `location` diagnostics and both cargo panic formats, grouping tests by
  normalized message, ordered by frequency then first appearance. TAP
  quoted/plain scalars are decoded; block scalars show their first non-empty
  source line. `subtestsFailed` parents are omitted from groups when another
  failing point has an error. Messages strip ANSI, collapse whitespace,
  replace semicolons with commas and stop at 160 characters with `…`.
  Shared locations retain `file:line`, drop the column and shorten absolute
  paths relative to the run cwd, otherwise to their last three segments;
  a location appears only when every name in its group shares it.
  Single-run and repeat-union `obs.note` prefixes show failed and distinct
  error counts, at most three error groups and three names, and remainder
  counts: `11 failed, 1 distinct error(s): "exact-source host required" ... ×11`.
  Unparsed names form a `(no error text)` group; with no parsed errors,
  the 0.16.0 names-only segment stays byte-identical. The grouped segment
  contains no semicolon, preserving the existing prefix parser and
  `; last output:` tail. `obs.failed` retains its capped union of up to
  50 names; full evidence remains in `owed why` and log blobs. Refusals,
  driver halts and `owed why` quote the stored note without re-extraction.
  Review fixes #1758/#1788 retain multiline legacy cargo messages through
  their closing quote/location while bounding parsing per panic record:
  an unfinished message cannot consume the next panic header, failed-test
  line or stdout record, steal another thread's cause/location, or masquerade
  as a new-style location. LF and CRLF logs retain independent records.
- **Show the trunk-to-merge delta (refusalnotice, TD1–TD3; field report
  #7271/#7285 feedback).** A failing merge-tree invariant measured for the
  refused node shows the latest pass of that obligation on the current trunk:
  `inv:<id>: pass on trunk <trunk12> (#<seq>) → fail on merge tree <commit12> (#<seq>)`.
  This appears even without failed-test names, once per obligation. Append
  `; node diff touches <p1>, <p2>, <p3> (+<N> more)` from the current
  candidate when changed paths exist. Without a current-trunk pass, omit
  the delta; existing trunk-red hints still apply. Merge refusals, driver
  merge-refused halts, `owed why` merge-block hints and merge-check details
  share the delta, after failure text and before intermittent/fix hints.

### Compatibility

Owed 0.16.2 shares a ledger with 0.16.0 and 0.16.1. No new entry kind or
field is added; replayed states, obligation keys and acceptance gates are
unchanged. Only new measurements' `obs.note` text and refusal/halt/hint
presentation change; stored historical notes are not rewritten.

## 0.16.1

This release delivers the merged nodes stalesend (SR, HA), reviewdedup
(RR) and compactreply (CR), keeping rejected sends from stranding recoverable
attempts, reporting questions while halted, avoiding redundant reviewers after
unchanged-key resubmits and shortening parent act replies, without weakening
any gate. Origins: field feedback on owed
0.16.0 in `/tmp/dsa/owed-requirements.md` (2026-10-13 13:4x, a forced dsa
1.0.39 orchestrator restart; 14:3x, a halted writer's hidden question;
14:4x, CMP-META attempt 2's evidence-only resubmit and redundant reviewer).
The parent's rulings below pin the behavior where the original asks changed.

- **Recover attempts after a rejected send (stalesend, SR1–SR4; field report
  forced-restart feedback).** A rejected send of the open attempt is
  superseded by a later ledger entry: a resume or submit of that same
  node/attempt, or another send of the attempt to the same rid that is not
  itself rejected. That later send need not be confirmed applied. The old
  send is skipped, never re-sent and cannot halt or block later rows.
  Existing rejected ruling-steer, waiting-notice and ruling-notice exemptions
  remain. A rejected steer to a run now sealed or pruned is also skipped so
  sealed-run rows decide; a writer sealed `unknown` gets the `interrupted`
  follow-up, which then supersedes the steer. At action time a rejected
  steer records its refusal and returns `rejected` without halting; the next
  pass applies these rules. A rejected steer to a still-running, asking or
  queued run with nothing later still halts; rejected follow-ups and notices
  retain their action-time behavior. Rejected-send halt text says the attempt
  is not lost: continue the run (e.g. a dsa follow-up), then
  `owed resume <node>`; a later resume, submit or send to this run supersedes
  the send, with `owed abandon <node>` only as the last option. That resume
  prevents the same send from re-halting. Rejected run requests remain fixed
  for the attempt and still require fixing the cause and abandoning it.
- **Report questions even while halted (stalesend, HA1–HA2; field report
  14:3x feedback).** The driver still reports asking writers and reviewers
  of the current candidate with the existing question text, answer/ruling
  hint, rid/qid/rev and once/quiet semantics. The slot action does not clear
  the halt, send anything or take another action. Ruling #1743 limits that
  no-send rule to slot actions: PR2 prospective notices still reach halted
  attempts. Ruling #1746 refines notice precedence: a non-quiet report with
  a concrete qid, or an overdue answer retry, precedes a pending prospective
  notice once per rid/qid/rev in that driver; a later pass may send the
  notice. Reporting alone does not quiet an unanswered question. A generic
  asking notice with no question content/qid does not preempt PR2, and
  other notices retain their precedence. `owed status` and brief stay
  ledger-only; the driver's asking notify is the question channel.
- **Reuse reviewers across unchanged review keys (reviewdedup, RR1–RR3;
  14:4x field feedback, CMP-META #7204/#7208/#7209/#7211).** The current
  candidate's review epoch starts at the earliest submit of the open attempt
  whose required `review` and `closure-review` keys remain unchanged through
  the current candidate. Reviewer launches after that epoch start remain
  live for questions, fence/ruling steers and rows 12–15, and count toward
  required reviewer runs; an unchanged-key resubmit does not launch a
  duplicate. A changed key starts a new epoch, and changing then restoring
  a key does not revive earlier reviewers. Writer launches are unchanged.
  Ruling #1724 applies the same epoch to the two reviewer filters in
  `src/drive-run.ts` (driver views and live-run reporting). Carried reviewers'
  verdict follow-ups name the current candidate; a review by that principal
  on the same obligation key counts even if recorded before the latest
  submit. Satisfied `review`/`closure-review` receipt and `owed why` items
  show each principal once, using its latest qualifying review, including
  JSON `evidence` and `observations`. Deduplication is view-only: stored and
  replayed evidence and distinct-principal satisfaction are unchanged.
- **Keep parent act replies brief (compactreply, CR1–CR3; field report
  2026-10-13 15:xx feedback).** Parent act tool replies use
  `renderStatusBrief`: the status outcome line, non-empty `Needs you` items
  and commands, then `Details: owed status · owed why <node>`, without
  progress or debt lists. `dispatch` retains its packet and subagents
  arguments; `plan`, `edit` and `amend` retain their act, carry and downgrade
  lines. The field-report addendum is included: `rule` and `defer` return recorded
  entries plus the brief, replacing the report and omitting
  `Escapes (all time)`. `abandon` and `resume` retain the entry, node phase
  and resume's waits-for tail, then the brief instead of a full receipt.
  Structured tool results, explicit `owed status`, `owed why`, `owed report`
  and `/owed` are unchanged. CLI acts that already append no full status
  need no replacement; CLI `init` and pi `adopt` keep their status suffixes.
  The skill points to `owed status` for the full list. Parent ruling #1776
  includes compactreply in 0.16.1 because it merged before this release.

### Compatibility

Owed 0.16.0 and 0.16.1 share a ledger. No new entry kind or field is added;
replayed states, obligation keys and acceptance gates are unchanged. The
changes are driver decisions, halt text and receipt presentation.

### Deferred

Stopping surplus reviewer runs once review is satisfied (RR4) requires a
driver `stop` effect, which 0.16.1 does not add. None is claimed as delivered.

## 0.16.0

This release delivers the six merged nodes answerrev (AR), slotsetup (SS),
mergeorder (MO), flakyhint (FH), nits016 (NT) and driveload (DL), removing
observed answer, prerequisite, merge-queue and driver-load costs without
weakening any gate. Origins: the dsa 1.0.38 note (2026-10-13 04:2x), field
feedback (2026-10-13 07:3x and 08:4x) in
`/tmp/dsa/owed-requirements.md`, three non-blocking 0.15 metrics/summary
review notes, and `/tmp/pi/pi-performance-diagnosis-2026-10-13.md` task B.
The parent's rulings below pin the behavior where the original asks changed.

- **Answer re-asks at their current revision (AR; dsa 1.0.38 note,
  item 1).** `owed answer <node> [--qid Q] [--rid R] [--rev N] "<text>"`
  (pi `owed_answer` parameter `rev`, a positive integer) uses the matching
  open question's current revision from dsa describe, never an old rule's
  revision. Without a matching open question, `--rev N` retries that prior
  answer; omission retries the latest rule for rid/qid. With no matching
  history it refuses. Answer identity is rid+qid+rev: the same revision
  and text reuse the rule/request; different text refuses and names the
  revision. A higher-revision re-ask records a new rule and request even
  for identical text. Ruling #1599 extends AR to the reducer; #1600 pins
  its duplicate check and requires `supersedes: <latest prior answer seq>`
  exactly when a prior rid/qid answer exists, with a lower revision.
  Retry hints include `--rev`. A q@2 re-ask after a q@1 answer remains an
  open driver question, not an answered quiet line. Answers do not
  invalidate candidates; reviewers still acknowledge in-scope rulings.
- **Prepare writer slots without committing setup artifacts (SS;
  08:4x field feedback, HOST-CASE-DOC-WIRE #6327 and five other slots
  missing prerequisite links).** Dispatch runs `slot_setup` when it is
  a string, otherwise the plan's `setup`, in the new worktree before
  recording dispatch; `slot_setup: false` skips it. CLI, pi and driver
  dispatch share the measurement shell/environment/wrapper and a
  600-second timeout. Success adds a one-line `Setup: ran ... (exit 0)`
  packet line. Failure names the exit code or timeout and last 20 combined
  output lines, rolls back the worktree, unchanged branch and newly
  created directories, and records nothing. Setup outputs never enter a
  candidate (SS5, #1607): tracked-file changes refuse dispatch; newly
  created untracked, not-ignored paths get deduplicated anchored literal
  rules in common `info/exclude` and are named in the packet.
  Newline/carriage-return paths refuse; other special characters are
  literal-escaped, rules start with `/`, and directories retain trailing
  `/` (#1617). SS6 (#1656) verifies every created status path, including
  directory descendants, is ignored. A tracked `.gitignore` negation
  leaving outputs visible refuses dispatch with paths and rollback;
  owed never edits tracked files to hide outputs. Rollback removes only this
  dispatch's appended exclude bytes, preserving existing rules, including
  when exclude append fails after writing (#1671). Successful rules
  persist across worktrees. `slot_setup` enters no key and changes alone
  invalidate no candidate/evidence; existing `setup` keys are unchanged.
  Rebase reuses the worktree without rerunning setup.
- **FIFO merges with explicit-wait priority (MO; 07:3x field feedback,
  QA-MPV1-MARKER #6067, HOST-ACQ-REACH, BG-ACQ-CLEANUP and
  LY-ROAD-LEGACY waiting accepted for 1–4 hours).** Accepted open attempts
  merge first when named by an outstanding latest `resume --after` wait,
  then all others; within each group the order is derived `acceptedSince`
  ascending, dependent count descending, then id. Losing acceptance or
  replacing the candidate resets its tenure; incremental and full replay
  agree. `owed resume X --after Y` raises accepted Y's priority without
  changing the plan or dropping a carried candidate. Replacing the resume,
  closing the waiting attempt or merging Y removes that priority; implicit
  dependencies alone do not raise it. Full status shows `Merge queue:`;
  status JSON adds `mergeQueue: string[]`. The driver's single merge slot
  takes the first eligible action; other actions keep their existing
  order. A continuously accepted, eligible node merges after at most the
  accepted nodes ahead of it; only a waited-on node can newly overtake it.
  Waits, halts, measurements and merge guards still apply.
- **Name intermittent merge failures and possible fixes (FH; field report
  07:3x feedback, repeated names at #4614/#5903 and #5037/#5325,
  CUT-FROZEN-HTTP refusals while QA-MPV1-MARKER waited).** CLI merge
  refusals, driver merge-refused halts and `owed why` block/merge-check
  lines add `known intermittent: <name> also failed on #a, #b (other trees)`
  for exact failed names seen on different commits, scanning at most the
  last 5000 entries and citing at most three sequences, newest first.
  Names of at least six characters found verbatim in an unmerged open,
  accepted or ready node's title, brief, check run or tests list add
  `fix candidate: <node> (<phase>) — owed resume <refused> --after <node>`.
  Same-commit history adds no intermittent hint; anonymous failures add
  neither hint. These are advisory text, not automatic waits, reordered
  work, relaxed gates, changed keys or new ledger fields.
- **Keep metrics meaningful and unrechecked debt visible (NT;
  three non-blocking 0.15 metrics/summary review notes).** Metrics
  `--since`/`--until` also accept date-only `YYYY-MM-DD` at UTC midnight.
  Halt normalization replaces lowercase plain-word node ids only in node
  positions (start before `:`, before `/`, after `node ` or `--after `,
  or as the argument of `owed <verb> `), preserving ordinary prose such
  as `merge refused` and `review refused`; other ids keep prior behavior.
  Default status and brief retain current-attempt owner defers when trunk
  drift or merge conflicts prevent rechecking, labeled
  `not rechecked: trunk moved` or `not rechecked: merge conflicts`; JSON adds
  `deferred[].unverified: 'drift' | 'conflict'`. Retired nodes/invariants
  and older attempts stay excluded; stale defers disappear once the
  current merge key can be queried. Unverified debt is not acceptance
  evidence; no gate, key or ledger schema changes.
- **Reuse verified state caches in every driver load (DL; pi performance
  diagnosis 2026-10-13 task B, a production driver at 113–145% CPU and about
  1.9 GiB RSS).** Driver passes, reaping, refusal and status/state reads
  use the ops replay memo/disk snapshot and persistent parsed-plan cache,
  still reading and verifying the entire ledger hash chain on every load.
  Warm loads never fully replay or parse historical plans. Ruling #1689
  permits same-head/incremental loads to refold up to `MEMO_EVERY-1 = 31`
  entries since the last memo point, in addition to newly appended entries
  for incremental loads; crossing 32 advances that point and the next
  same-head load folds zero. Memo/snapshot cadence is unchanged.
  Reports are applied only to a private loaded state, never the ledger-only
  cache; driver mutations cannot leak into later loads. Corrupt/mismatching
  snapshots fall back to full replay, bad hash chains still refuse, and
  newly appended plans are applied. Locks, stale checks, rulings,
  scheduling, idempotence, keys and ledger schemas are unchanged.

### Compatibility

Owed 0.15 and 0.16 can share a ledger until the first re-ask answer rule
carrying `supersedes` is recorded (#1600). Owed 0.15 refuses that entry
with code `version`; upgrade every process using a ledger — CLI, pi
extension, background drivers and remote executors — together, and
restart existing pi sessions. No new entry kind is added. Other additions
are derived state, output or plan fields and leave acceptance keys intact.
Mixed 0.14/0.15 remains unsupported.

Owed 0.15 ignores top-level `slot_setup`, runs no setup at dispatch and
adds no key for it (#1601); a plan using it needs 0.16 for setup to run.

Driver caching does not remove cold load: a fresh driver process still
pays one cold load (on the host project about 20 seconds). On a 7,071-entry / 279-plan
production ledger, warm loads measured about 0.1 seconds (213x faster), and
loads after +10 entries about 0.09 seconds (328x faster). These are warm
load measurements, not a cold-start or whole-driver-pass guarantee.

### Deferred

Incremental `ledger.read()` verification; automatic re-verification after
a repair (to be decided from 0.15 metrics); setup rerun on rebase; merge
train #22; measuring on the merged trunk #23; cold driver load. None is
claimed as delivered in this release.

## 0.15.0

This release delivers the merged nodes invred (IR), knownred (KR),
notices (QN), summary (SQ) and metrics (MT): quiet by default, loud
precisely, measurable. Origins: field feedback on 0.14 (2026-10-12 22:00)
in `/tmp/dsa/owed-requirements.md`, items 1–3, plus another owed user's
report (2026-10-12) about internal notices, a late old-driver `stopped`
and mixed historical audit/current blockers/version hints. Metrics comes
from the product goal of measuring interruptions and acceptance time.
The parent's rulings below pin the behavior where the original asks changed.

- **Refresh trunk invariants and inherit bounded existing failures (IR;
  field report inv:next-tests, INFRA-PREBUILT-KERNEL #4159, AQ-KF #4178,
  KX-RUNTIME-GC escape #4188 and run-string workaround R139).**
  `owed attest --trunk` (pi `owed_attest {trunk: true}`, no node) records
  executor `refresh` entries for current-plan invariant keys that differ
  from the ledger trunk's recorded keys, then measures them on trunk.
  Refresh changes invariant keys, not trunk commit or sequence; stale
  commits are refused. Merge performs the refresh before measuring its
  result and keeps the trunk observations even when refused; an otherwise
  quiet driver pass refreshes through its background measurement lifecycle
  and shared no-wait lease. An interrupted refresh can resume missing
  observations; nothing pending records nothing. Never-refreshed items
  say `not measured on trunk yet — owed attest --trunk`.
  A differing unsatisfied merge key needs no defer when the deciding
  executor failure on trunk and the merge failure have complete named
  failure lists, the merge failure is test-only, and every merge failure
  already occurs on trunk. Completeness is checked against failure counts,
  including each failing repeat run and its retained union; structured
  `execution.completed` and `execution.countOK` prove test-only execution,
  not display notes or log tails. Unknown/truncated names, timeout/error
  and count/min_tests failures do not qualify. New failures still refuse,
  naming only the new tests when available. Inherited debt stays D and
  belongs to the parent, not any open writer; owner defers and adopt retain
  their rules. Status, `owed why trunk` and the driver give fix-node,
  known-red and sweep options; the driver wakes once per (invariant, key)
  without halting node work. Background changes in rulings #1521/#1523
  are limited to IR3 notice identity and delivery-time revalidation:
  repo-scoped `inv:<id>` notices carry the obligation/key, separate from
  trunk drift, and are dropped before pi delivery if removed, resolved
  or on a different key. Failed ledger reads retain the notice.
- **Exact known-red debt owned by a fix node (KR; field report item 2,
  replacing run-string exclusions).** Invariants accept
  `known_red: [{test: <exact obs.failed name>, fix: <plan node id>}]`,
  at most 20 unique names, with fixes in the same plan; it enters no key.
  `owed edit --invariant <id> --set known_red=...` supports the field.
  Adding a name or changing its fix is an owner-only plan downgrade;
  no `allow` rule covers it, while removal is free (#1484).
  Entries stay active until their fix merges. Test-only failures entirely
  covered by active entries yield W (⚠), satisfied for acceptance and
  merge, with names, fixes and plan sequence visible as debt in default
  status, brief and report. Unlisted failures are evaluated normally.
  At a fix node's merge its own entries are inactive: still-failing owned
  names refuse the merge, even with an unchanged key or owner defer.
  KR3 is wired into IR2: the merging node's own fix names are excluded
  from inheritable trunk failure names, so trunk-red debt cannot excuse
  its unfinished repair (#1485). After the fix merges the entries lapse;
  `status --full` lists them as removable, and recurrence is ordinary red.
- **Answered questions stay quiet; old drivers do not look current (QN;
  field report item 3, HOST-VOCAB, LY-TFL-K and ST-LOCK repeated questions,
  plus the user's notice/late-`stopped` report).** The driver and pi
  delivery revalidation suppress questions covered by a recorded answer
  for the same run/question and no newer revision. A covered asking run
  logs that the answer was recorded and waits quietly. After
  `drive.answer_grace_s` (default 900 seconds, 0 immediately), a still-open
  covered question wakes once per (run, question) in that driver run,
  with the exact answer-delivery retry command; retries reuse the recorded
  answer and request. The one-shot retry survives unrelated node facts
  while that answered question remains open. Terminal events from an
  earlier driver, while a newer live driver owns the repository, ride
  along with the next wake naming both drivers instead of waking alone;
  current-owner terminal events, or those with no live owner, still wake.
  Wake messages lead with plain `Needs you`, then `Done` when present,
  then whether the driver continues or stopped. Full original evidence,
  commands, ids, leases, repeat counters and halt hints remain under
  `Details:`, with `Next: owed status / owed why <node>` last.
- **Outcome and next step by default (SQ; the user's mixed
  audit/blocker/version-hint report and 0.14's historical owner flags).**
  Default CLI/pi status starts with merged, in-progress and needs-you
  counts plus trunk invariant health, followed by current `Needs you`,
  plain progress phases with age, and `Debt` only when present.
  Ready nodes request dispatch only when no driver owns the repository;
  current merge diagnostics are shared with brief, and stale defers do
  not remain debt. Brief starts with the same outcome/needs prefix and
  keeps its other sections. A trunk-red invariant gets exactly one
  curated `Needs you` line in default status and brief, using its trunk-red
  options without an appended command; its Debt line is unchanged
  (#1562). `owed status --full` (pi `full: true`) preserves the detailed
  rendering with IR/KR additions and historical audit lines; default
  status ends with `Details: owed status --full · owed why <node>`.
  JSON retains existing fields with additive summary data. The installed
  versus loaded version hint appears once per pi session per installed
  version, as the last tool-output line; full status and mismatch refusals
  still show it.
- **Ledger-only workflow metrics (MT; evidence for the release's
  interruption/acceptance-time goal).**
  `owed report --metrics [--since <seq|ISO>] [--until <seq|ISO>] [--json]`
  reads ledger entries and recorded plan blobs, with no extra Git-fact or
  dsa queries and no acceptance/key changes. It reports merged nodes,
  interruptions and rate, submit-to-merge median/p90/max, first-dispatch-
  to-merge median/p90, first-pass acceptance, repairs and rate, and the
  top five normalized halt reasons for each decision class. Text is one
  block of at most 20 lines; JSON includes per-merge and per-halt detail.
  Submit timing starts at the first submit of the merged attempt;
  dispatch timing includes earlier abandoned attempts. First-pass means
  merged attempt 1 without an execution or judgment block on any candidate.
  Repairs sum `max(writer submits - 1, 0)` separately across each merged
  node's attempts, excluding executor carries. `since` is exclusive and
  `until` inclusive; merges, driver halts and parent/owner answer rules
  count by their own sequence, using history through `until` for timing
  and classification (#1486). A halt is a decision when a parent/owner
  records a relevant rule/answer, risk/debt decision, abandon/hold,
  approval/evidence/owner review, or a plan change to that node's spec/deps
  before or at its ending entry; otherwise it is no-decision. The window
  ends at the node's next resume, submit, merge, abandon or any later halt,
  whoever wrote that halt. Only `parent:drive` halts count as interruptions,
  alongside parent/owner answer rules (#1488).

### Compatibility

Owed 0.14 refuses the new `refresh` ledger entry kind and observations
carrying the new `execution` field with code `version` (verified against
`v0.14.0`'s reducer). Since 0.15 executor check/invariant observations may
write `execution` even without a refresh, 0.14 and 0.15 cannot be mixed on
one ledger. Upgrade every user of that ledger — pi extension, CLI and
background drivers — together before writing new observations, and restart
existing pi sessions.

Owed 0.14 and older ignore plan `known_red` and treat the listed tests as
ordinary failures (stricter), rather than refusing this plan field (#1484).
Mixed versions may halt merges that 0.15 accepts. `drive.answer_grace_s`
requires a 0.15 plan parser; older readers reject that unknown drive key.

Histories without the new entries/fields keep their earlier replayed states
and keys, apart from the changed not-measured message and default text
renderings. Default status, brief and wakes now foreground outcomes and
next steps; use `status --full` and wake `Details:` for full evidence.
Existing JSON fields remain, with additive result/runtime metadata.

### Deferred

Automatic re-verification after a repair is planned for 0.16; merge train
#22; measuring on the merged trunk #23. None is claimed as delivered in
this release.

## 0.14.0

This release delivers the merged nodes bigdiff (BD), policy (PR), hold (AH)
and trunkred (TR). Origins: field feedback (2026-10-12) in
`/tmp/dsa/owed-requirements.md`, the 15:5x items 1–3 and 16:4x items 4–5:
KX-DRIVING-PROFILE; R122 and ruling #3856 on LY-ROAD-K;
KX-REC-FOCUS #3917/#3925/#3926; KX-RUNTIME-GC #3896/#3919.
The parent's rulings below pin the behavior where the original asks changed.

- **Large candidates without buffered review diffs (BD;
  KX-DRIVING-PROFILE, 409 MiB evidence).** `owed submit` streams the full
  binary diff through a UTF-8 decoder into the review hash, never building
  the diff string. The patch hash stays identical to 0.13, including invalid
  UTF-8 replacement and multibyte characters split across chunks; an empty
  diff keeps its existing hash. All buffered `git()` calls bound combined
  stdout/stderr to 256 MiB by default (clamped to the runtime's string limit).
  Oversize output is refused with code `refused`, naming the Git subcommand,
  even with `allowFail`, instead of `ERR_STRING_TOO_LONG`. Other diffs,
  plan `show` and mutant `cat-file blob` reads are covered; the streamed
  review diff has no stdout buffering bound and retains bounded stderr.
  Optional top-level plan `limits: {file_mib, diff_mib}` accepts positive
  finite MiB values, including fractions. Submit inspects changed blob ids
  and sizes without reading content: `file_mib` bounds the largest changed
  candidate file and `diff_mib` their summed full blob sizes, once per
  changed path, including mode changes. Deleted files and gitlinks add zero;
  an exact limit is allowed. Exceeding a limit refuses with code `refused`,
  the limit and up to five largest changed files with sizes, recording
  nothing; the writer gets the ordinary submit refusal. Without either
  limit there is no size check. Limits enter no acceptance key, invalidate
  no existing candidate and are not rechecked at merge or carry.
- **Prospective policy and ruling re-acknowledgment (PR; R122,
  LY-ROAD-K #3856; ruling #1431).** `owed rule --prospective` (pi
  `owed_rule` field `prospective: boolean`) records `prospective: true` and
  binds only attempts dispatched after that ruling, not merely later
  submissions. Later packets and `rulings_seen` include it normally; older
  attempts keep their acknowledgment scope, halt and repair budget. Each
  unsealed writer/reviewer run of an older open attempt receives one
  informational `notify`, with no follow-up or re-ack obligation; retries
  reuse the same request while unsealed, never halting for a refused notice.
  Sealed runs receive no prospective policy on later follow-ups and are
  never relaunched for it. Removed nodes receive no prospective notices
  (#1431); the parent closes their remaining slots. `--prospective` with
  `--writer` is a usage error and records nothing; omission or false keeps
  ordinary ruling behavior. When an ordinary ruling, including `*`, leaves
  only `rulings` owed, the driver follows up with the latest reviewer or
  launches one, quoting the rulings, candidate commit and `--ack-rulings
  <seq>`. Without a review/closure-review obligation, it instead sends the
  writer one `ruling` follow-up per set and waits for resubmission of the
  same commit or a changed one, rather than a stalled-owner halt (#1431).
  Resubmit acknowledges only recorded writer ruling follow-ups, not later
  unsent rulings, prospective notices or executor carries. An unaddressable
  writer retains the normal run/send refusal route. Report "Status changes"
  omit nodes merged by the end of the window, removing merged-node ruling
  noise. Owner reviews at other ranks refuse with `owner reviews use --rank 3`.
- **Abandon without immediate re-dispatch (AH; KX-REC-FOCUS
  #3917/#3925/#3926; ruling #1403).** `owed abandon <node> --hold` (pi
  `owed_abandon` field `hold: boolean`) appends a separate entry kind
  `hold` with `{node, by, channel, abandon, reason?}` in the same ledger
  lock immediately after abandon; `hold.abandon` references the preceding
  abandon's sequence. Abandon itself has no new field. Status and why show
  `held by abandon #N`.
  A held node is not ready, the driver never dispatches it, and dispatch
  refuses with `owed resume <node>`. Resume releases it without an open
  slot using `release: "hold"`; normal readiness rules then apply. Release
  takes neither `--after` nor `--expect`; held nodes do not satisfy
  dependencies. Removing a node from the plan makes its hold moot. To
  retire a node, remove it first and then abandon its slot; use `--hold`
  while deciding. If the driver's own writer launch is stale because the
  node left the plan, was held or abandoned, it closes only its own attempt
  with no writer launch intent, without a halt or notice. Clean, unlocked,
  non-nested worktrees still at their base are removed with guarded branch
  deletion; commits or uncommitted changes keep their worktree and branch.
  Parent-dispatched slots and attempts with writer launch intent are left
  alone. From 0.14 unknown abandon fields also raise code `version` (#1403).
- **Trunk-red merge diagnostics, not writer repairs (TR; KX-RUNTIME-GC
  #3896/#3919: candidate 21/21, merge tree 18/21 and the same three failures
  on trunk; rulings #1432/#1458).** Before refusing a measured or reused
  merge-result node-check failure, `owed merge` measures that check on
  current trunk alone, under the same lease, setup and execution settings,
  with closure pinned from trunk and its own trunk check key. The executor
  records `obs.probe: "trunk"`, subject = node and commit/base = trunk;
  non-error probes on the same key and trunk commit are reused. Probes
  never satisfy or block obligations or count as decoy catches; invariants,
  red and strength checks get no probes. A failing probe classifies the
  refusal as `trunk-red` if every merge-tree failed name occurs in its
  names, or neither result has names. A passing probe or mismatched names
  keeps the ordinary refusal. The message starts `trunk-red: check:X fails
  on trunk <short> without <node> (<names>)`, identifies both observation
  sequences and says the candidate is not shown to be the cause. The
  driver halts needing the parent before attribution or a writer repair,
  including after restart. Parent options are a fixing node, a configured
  `owed sweep --commit <trunk>` for its bisect/escape proposal, or
  `owed waive <node> check:X --reason "<why the risk is acceptable>"
  --accept-risk <merge-tree obs seq>`. Classification compares check/merge
  keys, not raw base commits: unrelated trunk moves outside reads/closure
  retain the reusable failure, while a current-trunk probe decides its
  classification (#1432). Even after attribution cleared the original
  block, `accept_risk` may cite that merge-result fail only when its key
  equals the current merge key; the waiver binds that key alone (#1458).
  Other cleared/stale observations and probes remain invalid waiver risks.
  Status/why name both observations and route trunk-red to the parent,
  including a separate `mergeChecks` item in why and a parent pending item
  in status for the accepted-candidate/reused-failure path (#1458). Waiving
  that key removes the diagnostic; it does not claim a measured pass.
  Existing merge CAS and current-plan/key guards discard stale results.

### Compatibility

Use owed 0.14 for `rule.prospective`, the new `hold` entry kind,
`resume.release: "hold"`, `obs.probe` and the new cleared-merge-failure
waiver scope. Owed 0.13 refuses the new rule/observation/resume fields and
unknown `hold` kind with code `version`; it silently ignores unknown
abandon fields, which is why hold is a separate kind (#1403). From 0.14,
unknown abandon and hold fields also raise `version`.

Published 0.12 already refuses unknown ruling fields, so it also refuses
`rule.prospective` (including false) and the unknown `hold` kind with code
`version`; it does **not** silently ignore prospective policy. It ignores
unknown observation fields such as `probe`, so it cannot safely interpret
trunk probes. Pre-0.12 readers may silently ignore unknown rule/observation
fields and unknown kinds (0.11 does not interpret `hold`), rather than
provide a `version` refusal; never use them to operate a hold or probe ledger. Owed 0.13 ignores top-level plan `limits`
(as do earlier readers), so enforcing size policy requires 0.14+.

Streaming hashes preserve 0.13 patch/review identities. The other additions
change no existing acceptance keys; histories without new entries/fields
retain their 0.13 replayed states and keys. Upgrade CLI, pi extension,
drivers and remote executors together, and restart existing pi sessions.

### Deferred

Merge train #22; measure on merged trunk #23; milestone-level evidence;
periodic re-measurement of reused verdicts; worker crash not replaying
`session_start`. None is claimed as delivered in this release.

## 0.13.0

This release delivers the merged nodes lease (LE), waits (FL), repeat (RC),
answer (AN), edit (ED), sweep (SW) and quiet (QT). Origins: field feedback in
`/tmp/dsa/owed-requirements.md` §65, including the 41h list (2026-10-11)
and the machine-lock and morning/idle observations (2026-10-12), plus
`/tmp/dsa/owed-feedback-2026-10-10.md` §39 on escapes found by accident.
Quiet comes from another owed user's feedback (2026-10-12), not the host project.
The parent's rulings below pin the behavior where the original asks changed.

- **Measurements hold the machine lease (LE; field report §65 machine-lock
  observation).** Standalone attest, merge, adopt and genesis measurements
  (including init's background attest) wait under
  `pi-durable-subagents hold machine --shared` when dsa is available.
  Driver attests and in-process merges use `--no-wait`: busy means retry
  on a later pass, never halt. Merge starts only after the holder signals
  acquisition; return, failure, abort or parent death releases the holder.
  Setup, checks and invariants get `OWED_LEASED=1` to avoid nested lease
  acquisition, with isolated async contexts for parallel measurements.
  Results say `lease: machine (shared)`. The release also strengthens the
  lease test to compare non-empty check and invariant keys with and without
  the inherited marker.
- **Waiting, reports and backlog (FL; field report §65, 41h list #1/#6 and idle
  observation; ruling #1245).** An open slot with unmerged current-plan
  dependencies waits automatically: no submit/repair/ruling follow-ups,
  measurement or missing-candidate halt, while questions remain visible.
  Adding dependencies to a running writer sends one durable `notify`
  (not steer) per plan entry; when the waits end, the existing rebase and
  follow-up route catches the slot up to trunk. `owed resume <node>
  --after a,b` waits for all named nodes and composes with implicit waits;
  tools accept a string or string array. `owed resume <node> --expect
  report` asks for findings, not a candidate. A successful sealed report
  without a new submit wakes the parent with at most 40 lines/4000 chars,
  records no acceptance verdict and parks the node as `reported (resume
  #n)` until a relevant parent act. Failed/interrupted runs retain their
  recovery routes; a later submit is handled normally. `later: true`
  parks backlog nodes until removed. Idle wakes list ready/open/waiting/
  reported/later counts and repeat every `drive.idle_rewake_min` minutes
  (default 60, 0 off). Existing invariant-id entry points accept `inv:<id>`;
  `why` can show a trunk invariant, and unknown ids list valid spellings.
- **Repeated measurements and explicit counts (RC; field report §65, 41h list
  #2/#3 and flaky invariants; rulings #1243, #1247/#1274).** Checks and
  invariants accept `repeat: {runs: N, need: K}` with
  `1 <= K <= N <= 9` (`need` defaults to N). Commands run sequentially
  in one prepared tree and stop when K passes are reached or unreachable;
  errors stop as errors, not failed votes. Repeat is part of the
  check/invariant key (#1243), forbidden on red or mutants checks, and
  honored for merge-time invariants. Observations retain each run's verdict,
  exit, counts, duration and log digest; disagreement is descriptive
  `noisy (2/3 passed)`, not a separate block. The last valid column-0 line
  `owed-counts {"pass":2,"fail":1,"failed":["latency gate"]}` wins over
  other count formats; pass/fail and optional skip are non-negative integers.
  Indented lines and `# ` comments are silent non-protocol text (#1274);
  malformed protocol lines are ignored and noted. Only the `owed` format's
  fail > 0 fails a non-red run even on exit 0 (#1247); other formats keep
  their exit semantics. RC3 keeps up to 50 failing names from owed/TAP/cargo
  and leads failure notes, `why`, halts and repairs with those names, then
  a tail of at most 15 lines. Reused failing merge observations retain
  names and tail, and failed red checks (including `red_expect` mismatch)
  get the same names-first note (#1274).
- **Answer in one act (AN; field report §65 morning observation).** Parent/owner
  `owed answer <node> [--qid Q] [--rid R] "<text>"` discovers one open
  writer/reviewer question, refusing ambiguity or absence with the open
  question list. It records one node-scoped rule with `writer: false` and
  `answer: {rid, qid, rev}`, then delivers dsa kind `answer` with request
  `owed-answer-<seq>`. Explicit rid/qid retries with the same text reuse
  the rule and request, even after the question closes; changed text refuses.
  Delivery failure leaves the rule and reports its seq and exact retry
  command. Answer delivery is target-specific: the answer is not re-delivered
  as a writer ruling to the rid it answered, nor does it hide earlier
  undelivered rulings. Other runs receive it normally. Acknowledgment scope
  is unchanged: reviewers still acknowledge all in-scope rulings, including
  answers; reviewer acknowledgment follow-ups retain that complete context.
- **Atomic plan edits (ED; field report §65, 41h list #4).**
  `owed edit <node> --set path=yaml ... [--unset path ...] [--node-new]
  [--rule TEXT [--writer]] [--resume [--after a,b] [--expect report]]
  --note WHY [--dry-run] [--plan FILE]` edits node fields, checks and
  drive overrides; `owed edit --invariant ID --set field=yaml --note WHY`
  edits an invariant. YAML comments are kept; bytes outside the target map
  stay unchanged when safe, with whole-document serialization for flow
  containers or anchors/aliases. One ledger lock guards plan/carries,
  optional ruling and optional resume, using existing authority and downgrade
  rules. Refusals leave the ledger and file unchanged; file replacement
  follows the append, and a replacement failure reports the recorded seqs
  and recovery file. Dry run prints diff/downgrades without writing.
  `owed amend` remains available.
- **Advisory sweeps and unguarded paths (SW; field feedback §39; ruling
  #1331).** Top-level `sweep: {run, timeout_s, reads?}` enables
  `owed sweep [--commit C]` on a fresh detached trunk tree, with setup/exec
  and the shared lease. Completed sweeps record commit, definition key,
  verdict, counts, log and failed names, never acceptance observations.
  A staying idle driver schedules sweep/bisect in the background, at most
  once per trunk commit with completed sweep entries. Any ready driver work
  preempts it and proceeds in the same pass without waiting (#1331);
  process cleanup is bounded, releases the lease, and records `sweep
  aborted: preempted` without partial verdicts, so the next idle period
  can retry. A head failure is rerun once: a pass reports `flaky at head`,
  without localization. A green ancestor with a different or missing
  definition key is re-established under the current sweep/setup/exec
  definition before bisect. First-parent owed merges are searched, and
  the failing merge and its actual first parent are confirmed; disagreement
  or exhausted confirmation budget reports `inconsistent (flaky)` with
  verdicts and no culprit/proposal. Each commit runs at most twice and
  localization uses at most `ceil(log2(n)) + 4` measurements (#1331).
  Only a confirmed boundary proposes the exact `owed escape` command and
  tests to promote to an invariant; no escape is recorded and no merge is
  gated. Merge output, driver reports and `brief`'s last 10 merges list
  touched paths outside invariant reads, grouped by their parent directories
  with root files literal and redundant descendant globs removed.
- **Quiet by default (QT; another owed user's feedback, 2026-10-12;
  ruling #1378).** Rendered halts say `needs the parent (the main agent
  acts; owed lists the command)` instead of `needs human`; owner wording
  stays unchanged, and manual evidence offers the plan's role or
  `--as owner:pi`. Only repo-scoped drift wakes are revalidated at delivery:
  matching trunk/ledger commits or an advanced ledger trunk seq drop the
  obsolete wake; failed reads keep it. Unscoped legacy lines, including
  a node named `trunk`, are delivered rather than dropped as drift (#1378);
  ordinary node/question revalidation still applies. Status/brief show no
  drift when trunk matches. The existing "When to use owed" guidance keeps
  small low-risk fixes direct with one parent adoption, reserving reviewed
  nodes/red tests for consequential risks and multi-agent waves.

### Compatibility

Use owed 0.13 for ledgers with repeat-keyed observations, answer rulings,
sweep entries, or edits using the new 0.13 plan/resume shapes. ED itself
adds no entry kind or field: plain plan/carry/rule/resume edits retain their
existing schemas. Published 0.12 silently drops plan `repeat` and replays
its observations by aggregate verdict only, ignoring run/noise/name metadata;
it cannot be retrofitted to refuse them, so do not operate a repeat ledger
with 0.12 (#1243). From 0.13, unknown top-level observation fields raise code
`version` with upgrade/restart guidance. 0.12 refuses new resume `after`
arrays or `expect` as an ordinary refusal, not `version` (#1245); from 0.13,
unknown resume fields/expect values/incompatible after shapes use `version`.
Older owed refuses answer ruling fields, sweep entry kinds and sends of kind
`notify` or reason `waiting`/`report` with code `version`.

The machine lease, its inherited `OWED_LEASED=1` marker and plan `lease`
are not in any check/invariant key and introduce no ledger field. Missing
dsa or an inherited marker runs directly; CLI `--no-lease`, tool
`lease: false` and plan `lease: false` disable acquisition (default on).
Standalone measurements wait; driver measurements retry busy on a later
pass. Older owed replays these entries but does not enforce the new lease
semantics. Halt wording is rendering-only: ledger `needs: 'human'` is
unchanged. Upgrade CLI, pi extension and remote executors together, and
restart existing pi sessions.

### Deferred

Merge train #22; measure on merged trunk #23; milestone-level evidence/debt
(41h list #5); periodic re-measurement of reused invariant verdicts (field
feedback §39.2); worker crash not replaying `session_start`; 0.10 carry/amend
nits. None is claimed as delivered in this release.

## 0.12.0

The host project's pi-zip performance report (N4) showed ledger replay freezing pi's
UI even after 0.10's reducer speed-up. Field feedback §38 in
`/tmp/dsa/owed-feedback-2026-10-10.md` added the in-memory extension version
skew and the low-disk shared-build-dir pattern. This release delivers G1
snapshot replay, G2 tool workers and G3's non-blocking 0.11 review nits,
with the parent's rulings #1176, #1198 and #1200.

- **Resumable replay and snapshots (G1; pi-zip N4).** `reduce` remains
  the full-replay reference; `reduceFrom` folds new entries onto a private
  copy of the state and its replay metadata, preserving internal sharing.
  State, metadata, views and draft-validation decisions match full replay.
  Loads use a per-ledger in-process memo, then `snapshot/state.v8`, when
  the cached seq/hash matches the verified chain; otherwise they replay
  from scratch. Results never alias, and draft simulations fold only the
  added entries. Snapshots record the format, owed version, seq, hash and
  payload digest, and are written atomically after at least 256 further
  entries or when none exists. Missing, corrupt or mismatching snapshots
  are cache misses, never operation failures. `owed verify` always uses
  full replay; a matching-chain snapshot that differs is deleted and
  reported as `snapshotMismatch: <seq>` in text and JSON. Per ruling #1200,
  deletion also invalidates the memo with an epoch and a publication queue:
  a load started before invalidation cannot republish stale state. Old
  plans are loaded lazily, but missing/unreadable plan errors are never
  swallowed by views. Unknown entry kinds, every closed enum value and
  unknown ruling flags refuse replay with code `version`, an upgrade/restart
  message and CLI exit 1 (including `verify`); other invalid entries keep
  their errors.

  Measured on the bench host against 0.11.0, with warm disk snapshots:

  | Ledger / operation | 0.11.0 | 0.12.0 |
  |--------------------|--------|--------|
  | Production ledger, 2229 entries, `status` | about 1.04 s | 293 ms (about 28%) |
  | Synthetic, 10000 entries / 200 nodes, `status` | 1.53 s | 378 ms |

  Cold replay stays within +10% of 0.11.0; an in-process load after
  appending one entry spends <= 50 ms replaying. Interleaved confirmation
  runs measured the warm production ledger at 293/1083 ms (27.1%) and synthetic at
  378/1612 ms, with cold overhead +6.3% and +6.4%, respectively.
- **Tools off pi's main thread (G2; pi-zip N4, field report §38).** Extension
  loading eagerly starts one shared `worker_threads` worker per pi
  process. Tool calls, `/owed`, drive-watch revalidation and background
  genesis attests run there; schemas, owner dialogs, rendering and watcher
  timers stay on pi's thread. Structured-clone RPC preserves OwedError
  codes/messages and forwards aborts to existing abortable operations.
  Owner actions retain preview/dialog/commit order and changed-candidate
  refusal. A crash fails outstanding calls without replaying them; the
  next call replaces the worker only if the installed version still
  matches the session's loaded version. Tools append
  `owed <installed> is installed but this pi session loaded <loaded>: restart pi to use it.`
  on version skew or a `version` error. Both compiled `dist/ext-worker.js`
  and git-install source loading work without optional pi peers. On a
  2201-entry ledger, tool calls in the worker keep pi responsive: about
  0.5 ms main-thread stall versus 115-179 ms in-process.
- **0.11 review fixes (G3).** `stale` is a refusal (exit 1), not an
  internal error (exit 3), in the CLI and driver; the CLI's `version` exit
  mapping comes from G1, as ruling #1198 assigns. A restarted driver
  recovers follow-up generations per run, not per node: it asks dsa for
  the latest applied follow-up's original reply using the same request id
  and stored bytes, cached once per send id per process. Another reviewer's
  later verdict cannot suppress recovery or cause a premature
  `review-missing` halt (#1198). A recorded send's re-send is refused as
  stale if a later plan removed or changed the node, even though plan
  entries name no node. Environment classification filters spec-reporter
  titles (`✔`, `✖`, `▶`) as well as TAP/cargo titles, and its error note
  shows the filtered matching lines, not the unfiltered tail. Ruling
  re-sends after resume include the same active/flaky block lines as the
  original follow-up. A removed node's open slot counts toward `drive.max`
  until abandoned; ruling #1176 lets `owed abandon <node>` close that slot
  without re-adding the node, changing no other removed-node operation.
  Failed-send text names the node as a word. `reuseHooks.beforeTouch`
  remains a documented test-only seam. The skill documents the host project's
  low-disk alternative to per-tree build dirs: shared dirs serialized by
  `flock`, a per-dir last-checkout marker and tracked-file touch when the
  builder changes, leaving `exec.touch` at its default so keys stay stable.

**Compatibility.** Files under the ledger dir's `snapshot/` are private,
derived caches, safe to delete; deletion only costs a full replay. 0.12
adds no entry field or send reason, so its ledgers remain replayable by
0.11 unless they contain an `abandon` of a node the plan removed: owed
< 0.12 rejects that entry (#1176). The existing pre-0.11 limits for
`send` reason `verdict` and `writer` rulings still apply. The explicit
newer-entry refusal with code `version` (exit 1) exists only from 0.12 on;
earlier owed does not provide that forward-compatibility error. Upgrade
the CLI, pi extension and remote executors together, and restart existing
pi sessions to load the installed version.

**Deferred.** #22 merge train; #23 measure on the merged trunk; parent
answers recorded automatically; the carry and amend nits from 0.10.
Tool-worker review #1182: a crashed worker does not replay `session_start`,
so drive-follower recovery waits for the next session start and in-flight
background genesis attests are lost, not automatically resumed; non-OwedError
tool exceptions become `Refused` results with code `internal` rather than
being thrown to pi; a refused replacement repeats the restart line; a
same-process `/reload` after upgrading refuses extension loading instead
of retaining old tools (restart pi); the in-process non-blocking test's
margin above 100 ms is modest. Snapshot review #1218 and nits review #1206
reported no further unfixed product nits after rulings #1200 and #1198.

## 0.11.0

Field reports #12 and #27-#36 reported halts and wrong verdicts that
owed caused itself. The parent's diagnosis changed two of the asks. #35 is
a driver race, not reviewers forgetting: in all three `review-missing`
halts the review was recorded 33-60 s before the halt, by a run that
recorded and sealed between the pass's state load and its describe. #29 is
not a semantic merge conflict but nondeterminism: the attribution rerun
of the same merge tree passed, because reused trees linked stale build
artifacts (#32b), fixed by F6 plus a waiver that reaches merge keys (F3).

- **Fresh act (F1; field report #35).** Every `launch`, `send`, `rebase` and
  `halt` the driver records as the result of `decide` passes `since: S`,
  the seq of the state the pass loaded. Under the ledger lock, an entry
  after S that names the node (its `node` or `subject`, a ruling covering
  it or `*`, or any `plan` entry) and is not by `parent:drive` makes the op
  record nothing and throw `OwedError` code `stale`; a send then makes no
  dsa call. The driver reports outcome `stale` (no halt, no wake) and the
  next pass decides again. Rows 13-14: a sealed reviewer run whose
  obligations still await gets ONE follow-up with the new reason
  `verdict`, naming the candidate, the awaiting obligations and the exact
  `owed review` command; `review-missing` halts only after it sealed again
  without recording (at once for a `rejected` or `pruned` run). The asking
  notify ends with a hint to record a contract or scope answer with
  `owed rule --nodes <node> "<decision>"`.
- **Environment failures wait (F2; field report #36, #12).** Per ruling #1094, a
  measurement is `error` with a note starting `environment: ` only when it
  failed before its command ran (tree preparation, spawn, `setup`), or when
  it exited non-zero with no failing test counted and `ENOSPC`, `No space
  left on device` or `Disk quota exceeded` appears in the last 50 output
  lines after dropping TAP test points, subtest titles and cargo test
  lines (a test name is never evidence). The driver does not count these
  toward "attest recorded no verdict twice"; the node's next attest backs
  off 1, 2, 5, 10, then 30 min (a resume restarts the streak), with one
  notify per streak. `drive.min_free_mb` (default 2048, 0 disables) gates
  starting measurements on free space in the git common dir and the temp
  dir (`disk low: …`, once per change). A disk-full error escaping a pass
  logs a waking `disk-full` event, sleeps 60 s and continues. `owed gc`
  also removes free node-check trees whose check belongs only to merged or
  abandoned nodes.
- **Waiver reaches merge keys (F3; field report #34, #29, #12).** The blocks an
  owner waiver cites in `accept_risk` are cleared whatever their key,
  merge keys included; every cited seq must be an active or flaky exec or
  judgment block of the waiver's node and obligation. A merge-result fail
  cited this way no longer counts on its key, so a pass on that key (the
  attribution rerun) satisfies `owed merge`. The waiver's own key binding
  is unchanged, and the confirmation and result text say the obligation is
  no longer measured on the waived key.
- **Removed nodes and routing fixes (F4; field report #27, #31, #33c, #34).** A
  node absent from the current plan and not merged has phase `removed`:
  never ready, dispatched or selected by the driver. `owed resume` refuses
  when an active needs-parent review block awaits a ruling, naming
  `owed rule --nodes <node> "<decision>"`. A failed send's report names
  node, kind, reason and target. The `min_tests` loop warning skips
  `cargo test` commands. A timed-out describe is retried once per pass, and
  a node in a `resume --after` wait gets no `describe-failed` notify.
- **Writer rulings and waits during merges (F5; field report #33a, #33b).**
  `owed rule --writer` (pi `owed_rule` `writer: true`) records `writer:
  true`; recorded after a named node's open submit it invalidates that
  candidate (a new submit is needed, the same commit is fine), and the
  driver delivers it as a `ruling` follow-up to a sealed writer or a steer
  to a running one. Reviewers still acknowledge it; `*` rulings and rulings
  without `--writer` are unchanged. When a merge measuring at the time of
  a `resume --after` ends refused, the driver reports `waiting` and records
  no halt.
- **Reused trees never link stale artifacts (F6; field report #32b).**
  `exec.touch: reset | always | never` (default `reset`, reused trees only):
  `reset` sets every tracked file's mtime to now when a reused tree changes
  commit, `always` before every reused measurement, `never` keeps Git's
  behavior. An explicitly set `touch` is part of the exec key (owner act,
  fresh evidence). Every setup, wrapper and measurement process gets
  `OWED_TREE` = the absolute tree path, so checks can keep per-tree build
  dirs (`export CARGO_TARGET_DIR="$OWED_TREE/../target-$(basename
  "$OWED_TREE")"`). Per ruling #1095, a newly recorded plan (`init`,
  `plan`, `amend`) may not set `OWED_TREE` in `exec.env` (replay does not
  check it; owed's value overrides it at run time), and reused-tree
  preparation is all-or-nothing: a marker of the last complete preparation
  is removed before checkout and written after touch, so any failed step
  is redone in full on the next lease.

**Compatibility.** A ledger with a `send` of reason `verdict` or a plan
setting `exec.touch` needs owed >= 0.11.0 to verify and replay; 0.10
replays a `writer` ruling as an ordinary one and has no `removed` phase.
Upgrade the CLI, the pi extension and remote executors together. Plans
without `touch` keep byte-identical keys. Checks that relied on a shared
`CARGO_TARGET_DIR` across reused trees should move to per-tree dirs or
`touch: always`.

**Deferred.** #22 merge train; #23 measure on the merged trunk; parent
answers recorded automatically (F1.3 is the interim); the carry and amend
nits from 0.10; moving pi tools off pi's main thread; a driver restart
loses cached run generations; `stale` is reported as an internal error
(exit 3) by cli/drive-run; a send decided before a plan entry removes its
node is not refused as stale; spec-reporter test titles are not filtered
by the environment classifier.

## 0.10.0

Field reports #24-#26 reported parent round trips that owed caused
itself: plan v69 (field report #1373) changed only a check definition of one node,
which invalidated its candidate; the driver then sent a `rebase` follow-up
(#1375) and the writer resubmitted the same commit at the same base
(#1378). A moving trunk failed dispatch and plan CAS, and a writes question
cost a hand-edited plan plus a separate ruling. Separately the pi-zip
agent's profile showed every op replaying the whole ledger: 9.6-13.5 s per
op on a production ledger (1558-1601 entries, 85-87 nodes), with the pi session
UI frozen because pi tools run on its main thread. N1 carries candidates
across measurement-only plan changes, N2 retries CAS under a moving trunk,
N3 adds `owed amend`, and N4 makes replay fast.

- **Carry (N1; field report #24).** When a plan change invalidates a node's open
  candidate and the node spec differs only in `checks`, `writes`, `type`
  and `drive` (plan-wide `setup`, `exec` and `closure` changes do not
  matter), the same `owed plan` call appends, in the same ledger lock and
  right after the plan entry, a carry submit: a `submit` by `executor:owed`
  with `carry: <seq>` of the carried submit, for the same commit at the
  same slot base, its facts recomputed under the new plan. Evidence on
  unchanged keys still counts; the next attest measures the changed keys.
  The candidate stays the writer's commit, so writer-only rules (no
  self-review, decoys) treat it like the original. Any other node field
  change (`brief`, `deps`, `review`, `title`, `approve`, `evidence`, ...)
  never carries. Validation accepts an executor submit only with `carry`
  naming the latest submit of the open attempt, with its commit and base,
  an open slot, no current candidate and an unchanged slot base; a writer
  submit never has `carry`. `owed plan` prints `Carried <node>: ...` per
  carried node; a carry whose facts cannot be computed or that validation
  would refuse is left out (the candidate stays invalidated, as in 0.9)
  and reported as `Not carried <node>: <reason>; the writer submits again`
  (JSON `notCarried`). `owed why` and status show `candidate #C carried by
  plan #P from submit #S`. Driver row 8 label fix (#1375): without a
  current candidate, the `rebase` follow-up goes out only when the slot
  has a rebase and no submit of the attempt followed it; when a later plan
  entry invalidated the latest submit, the `submit` follow-up names it:
  "plan #P changed this node's spec (<fields>); resubmit (re-run checks if
  needed, then `owed submit <node>`)".
- **CAS retry (N2; field report #25, #24 parts 2-3).** A dispatch rollback deletes
  its new branch with `git update-ref -d refs/heads/<branch> <base>`, a
  compare-and-delete in one Git transaction, whatever the main worktree's
  HEAD (the "not fully merged" leftover of field report #25 is gone). A ref that
  moved, or a branch still checked out in a worktree, is kept and reported
  under the existing `git branch -d` rollback label. `owed dispatch` and
  `owed plan` retry a CAS refusal, up to 3 attempts in total, while the
  ledger's plan sha is unchanged; each attempt reads fresh state and
  dispatch completes its rollback first (a failed rollback is returned at
  once). A changed plan sha, or the third failure, returns the refusal with
  its error class and exit code; other failures are not retried. The
  downgrade hint for a changed check definition names the changed fields
  among `run`, `timeout_s`, `reads`, `tests`, `red_expect` and suggests
  adding the new command as a new check id; detection is unchanged. On
  `owed plan`, the check-less and loop warnings skip nodes merged in the
  ledger; `owed init` warnings are unchanged.
- **`owed amend` (N3; field report #26).** `owed amend <node> --writes
  +<path>[,+<path>...] --note "<limit>" [--plan <file>]` (pi `owed_amend`
  `{node, writes, note, plan?, as?, cwd?}`) widens one node's writes. The
  plan file is `--plan`, else the latest plan entry's `path`. The edit
  splices only the node's `writes` list source (via the `yaml` Document
  API) and keeps every other byte; the result must parse to the current
  plan with only that node's writes widened. It refuses a file whose
  normalized plan sha differs from the ledger's (`plan file has unrecorded
  edits`; a comment-only edit does not count), an unknown or merged node,
  an empty path list, a path without `+`, an empty note, no plan path
  without `--plan`, and `nothing to amend` when every path is already
  there; removal is not offered. In one
  lock it appends the plan entry exactly as `owed plan` would (same
  authority: a widening is a downgrade, so the owner or an `allow` rule),
  any N1 carry submits, and a `rule` by the same principal for `[<node>]`:
  `writes of <node> widened by plan #P: +<paths>. Limit: <note>`, which
  reaches the writer through the ruling path and reviewers acknowledge. It
  retries CAS like `owed plan`, re-reading file and ledger per attempt. A
  refusal records nothing and leaves the file unchanged.
- **Replay speed (N4; pi-zip profile).** The fold keeps per-node indexes
  (reviews, evidence, waivers, observations by item, launches, entries by
  seq), recomputes derived fields only for the nodes an entry can change,
  and computes `dependents` once per plan instead of an O(N^3) DFS per
  entry. A differential test (test/perf.test.ts) compares State, queries,
  views and `validateDraft` decisions with a frozen copy of the previous
  reducer at every k-th prefix of fuzz, synthetic and real ledgers.
  Measured on the bench host with a copy of a production ledger (1601 entries, 87 nodes,
  87 plans, 15.5 MB of YAML) and a git repo, full `owed status` path:

  | Case                            | before          | 0.10.0          |
  |---------------------------------|-----------------|-----------------|
  | `status`, cold, warm plan cache | 13.5 s / 335 MB | ~0.62 s/~195 MB |
  | `status`, hot (same process)    | not measured    | ~0.40 s         |
  | First run (fills the cache)     | 13.5 s          | 2.7 s           |
  | Synthetic, 9917 entries         | not measured    | 1.2 s           |

  Parsed plans are cached in process and persistently in `<owed
  dir>/cache/plans/<blob sha>.json` as `{v, sha, plan}`, with `v` = cache
  format + owed version, written atomically (temp file, rename) after a
  successful YAML parse. Any read error, invalid JSON, other `v` or other
  `sha` is a miss and the YAML is parsed again; the cache never fails an
  op. `owed verify` never uses it for replay: it parses every blob from
  YAML and reads each entry raw. An entry of another `v` is rewritten
  silently; one of this `v` whose `sha` or plan differs, or that cannot be
  read or parsed, is reported in `cacheMismatch` (not a verify failure)
  and rewritten. `owed gc` (also `--dry-run`) removes entries whose sha no
  longer appears in a genesis or plan entry, and temp files older than
  60 s; nothing is recorded in the ledger. Deleting the directory is safe.

**Compatibility.** A ledger containing a carry submit needs owed >= 0.10.0
to verify and replay: 0.9 refuses a submit whose `by` is not the slot
writer. Upgrade the CLI, the pi extension and remote executors together.
The plan cache version includes the owed version, so the first run after
the upgrade parses every plan again and refills the cache.

**Deferred.** The formal model does not cover carry (nor resume,
superseded blocks, parallel measurement or reused trees). Carry nits
(review #983, 23:5x): replay does not re-check that a carry submit directly
follows its plan entry and that the spec change was carryable (only ops
writes executor submits, and any local process can already append executor
entries); carry after abandon or re-dispatch, carry of a carry and carry
during a merge measurement are covered by review reading, not tests. Amend
nits (00:4x): in a CRLF file, inserted block-list lines get LF endings; a
flow list with a comment right before `]` is refused as an internal error
instead of a usage error; a node with no `writes` gets the whole file
re-emitted. A verify cache mismatch is shown only in JSON. Moving pi tools
off pi's main thread (to a worker or child process). Still open from
0.9.0: per-check "measure on the candidate merged with the current trunk"
(#23); reading dsa 1.0.34 `delivery: "forwarded"` directly; review #950
nit: two reclaimers that both remove the same dead-pid token older than
1 h can, in a narrow window, end with two holders of one reuse tree; field report
#22 parts 1-2, a merge train. Still open from 0.8.0: `error_expect` for
environment-precondition failures (#12); per-tier model routing (#11); a
plan warning for unknown node keys; attest-busy nits (a dispatch whose
ledger-lock timeout is followed by a failed rollback still exits 75
`retry`; `--json=x` gets no JSON error line); the SIGKILL limit (checks of
a SIGKILLed attest keep running, unrecorded, until they exit); K review
nits (parallel node attests each measure a pending genesis invariant;
stale attest locks with a reused pid read as busy indefinitely; the `hold`
comment in `src/dsa.ts`; the `--json` loop exit record maps `busy` to code
3; merge's abort path keeps the strict whole-plan stability rule; merge
recomputes facts under the ledger lock when the plan changed; merge's
branch for a node that left the plan is unreachable; the invalidation
refusal says `its spec changed` also for `setup`, `exec` or `closure`
changes; a waiver of an obligation the candidate lacks prints an empty
key; the threshold hint reads observations across attempts; the
node-models sha test recomputes the formula; a merge-cas test title);
without dsa, an attest exiting 75 on a lock timeout is logged as `machine
lease refused`; a busy node logs a `started` and a busy line per timed
pass; whether a rejected ruling send survives a dsa `prune` was not
verified. Still open from 0.6.1: the writes hint lists node ids verbatim
as globs and grants every listed node the union of the new prefixes; a
concurrent dispatch can make a rollback report `rollback failed: directory
cleanup`; a rollback that wraps a non-OwedError drops its stack; pi
revalidation replays the whole ledger once per delivery attempt, and a
failed delivery's dropped wakes are already marked resolved; a staying
driver polls neither dsa events nor trunk drift while it waits; binding a
reviewer's identity to its dsa call; rulings that uphold or overrule a
named block. Still open from 0.6.0: a follow-up dsa retires because the
call sealed before delivery ends in a misleading `finished repair
follow-up without submitting` halt (awaits dsa §49); M4 and M6 of
`owed05-big.sh` remain within 9% of the 22M-state cap. A new candidate
that changes the flaky test itself does not clear the block.

## 0.9.0

A production merge took 13.3 min (feedback #23): the sum of its merge-tree checks
run one after another (node checks 47+46+11 s, invariants 36, 86, 318, 16,
103 and 83 s). Each run also measured in a fresh /tmp/owed-run-*/tree path;
cargo fingerprints include that path and every file mtime was new, so every
run rebuilt from scratch and incremental builds never helped. M1 measures a
merge's jobs in parallel, M2 reuses measurement trees, and M3 documents both.

- **Parallel merge-tree measurement (M1; field report #23).** The new plan field
  `exec.parallel` (integer >= 1, default 1) is the number of jobs one `owed
  merge` measures at once: its genesis/invariant jobs and its merge-result
  jobs share one queue started in job order. The same applies to `owed
  adopt` and to the genesis attest (`init`, `attest --genesis`). Node
  attests stay serial, because the driver already runs attests of different
  nodes in parallel. Observations enter the ledger in job order whatever
  order the jobs finish in; with `parallel: 1` behavior is exactly that of
  0.8. An abort (D16) kills every running job and starts no further one.
  Merge and adopt keep the observations of the jobs that completed before
  the abort, through `abortWith` as before; the kept set is in job order
  but need not be a prefix of the job list. The genesis attest records
  each observation once its predecessors are recorded, also after an
  abort; a failure to record one starts no further job, waits for the
  running ones and then fails. A job that throws likewise stops new starts.
- **Reused measurement trees (M2; field report #23).** The new plan field `exec.trees`
  is `fresh` (default: a new temporary worktree per run, removed afterwards) or
  `reuse`. With `reuse`, check, red, strength and invariant runs use a stable
  detached worktree `<git common dir>/owed/trees/<kind>-<check id>-<k>`
  (`<kind>` is inv, check, red or strength; an id that is not path-safe becomes
  `h` + 16 hex of its sha256), with `<k>` the lowest index whose lease is free.
  The lease is `<tree>.lock` holding the pid, taken atomically by writing the
  pid to a unique temporary file and link()ing it to the lock path, so a lock
  file is never empty. A lock whose content X is dead (a dead pid, or empty or
  unparsable and older than 60 s) is reclaimed under the token
  `<tree>.lock.dead-<X>`, created with O_EXCL: the holder re-reads the lock,
  unlinks it only if it still holds X, tries the normal take once (moving to the
  next k if it loses) and unlinks the token; a reclaimer that cannot create the
  token does nothing for that k. Concurrent measurements of one check therefore
  get different trees, and a stale lock ends with exactly one holder. Taking a
  lease also removes that lock's temporary files older than 60 s and dead-pid
  tokens older than 1 h. A reused tree is prepared with `git checkout --detach
  --force <commit>` and `git clean -ffdx`; then each gitlink (submodule)
  directory of the commit is emptied inside that tree, as a fresh `git worktree
  add` tree leaves it. No `git submodule` command runs and no config or modules
  directory changes. The overlays follow as before, so the content equals a
  fresh tree. What survives is the path and the mtimes of files the checkout did
  not change: a build cache outside the tree (a shared `CARGO_TARGET_DIR`)
  builds incrementally; an ignored `target/` inside the tree does not survive. A
  missing or broken tree is recreated; a failure to prepare one falls back to a
  fresh tree with a one-line note in the observation log and never fails the
  measurement. The end of a run releases the lease and keeps the tree; each
  strength mutant prepares the tree again. Reuse trees are not slots: gc's slot
  handling, dispatch, the D19 trunk-worktree report and the slot reports ignore
  them. `owed gc` removes those whose lease is free when the plan no longer sets
  `trees: reuse` or their check id is gone (listed as `trees`, held ones as
  `treesKept`; `--dry-run` only reports), plus lock temporary files older than
  60 s and reclaim tokens older than 1 h. It appends no ledger entry for them.
- **Scheduling fields are not keys.** `exec.parallel` and `exec.trees` are
  not part of `execKey`, any check key or the L2 check definition, and the
  exec comparison (`execChanged`, now over `execKey`) looks at `env` and
  `wrap` only. Changing them supersedes nothing, invalidates no candidate,
  re-measures nothing, records no `exec changed` downgrade and needs no
  owner authority; changing `env` or `wrap` works as before. An `exec:`
  block holding only `parallel` and/or `trees` is valid; parsed plans keep
  each field only when it is set. The `Exec:` view line shows them.
- **Docs (M3).** SPEC (§3.2, §4.1, §7 step 8, new §7.12, gc), README,
  SKILL and MODULES describe both fields and the measurement environment.
  Advice: parallel cargo builds sharing one `CARGO_TARGET_DIR` serialize on
  cargo's build-directory lock, so give heavy invariants separate target
  dirs or accept that they wait for each other.

**Compatibility.** 0.8.x parses `exec.parallel` and `exec.trees` as unknown
exec keys and refuses the plan (`exec.<key>: unknown key`), so upgrade the
CLI, the pi extension and remote executors together before setting them.
Ledger entries and keys are unchanged; plans without the new fields behave
exactly as in 0.8.0. A ledger whose plan sets them needs owed >= 0.9.0 to
replay.

**Deferred.** Per-check "measure on the candidate merged with the current
trunk" (#23; field report: low priority). Reading dsa 1.0.34 `delivery:
"forwarded"` directly. Review #950 nit: two reclaimers that both remove the
same dead-pid token older than 1 h can, in a narrow window, end with two
holders of one reuse tree (rename the old token to a unique name before
removing it). Still open from 0.8.0: `error_expect` for
environment-precondition failures (#12): a check reports these today by
exiting 126/127 without a count, which records `error`, not a failure.
Per-tier model routing (#11 asked for a tier or risk mapping; routing
remains per node). A plan warning for unknown node keys (typos like
`drvie:` are ignored; refusing them would change which existing plans are
accepted). Attest-busy nits: a dispatch whose ledger-lock timeout is
followed by a failed rollback still exits 75 `retry` though leftovers may
block a retry; `--json=x` gets no JSON error line (the CLI matches `--json`
only). The SIGKILL limit: a stop at once SIGKILLs the dsa invocations' and
attest children's process groups only; the checks an attest runs have
process groups of their own, so a check whose attest was SIGKILLed before
ending it keeps running, unrecorded, until it exits. K review nits not
fixed: with per-node locks, parallel node attests each measure a pending
genesis invariant (correct under D24, more CPU); lock owners record no
process start time, so a stale attest lock whose pid was reused reads as
busy indefinitely, and the driver retries without a halt; the `hold`
comment in `src/dsa.ts` still says owed exits only 0..3; the driver's
`--json` loop exit record maps a `busy` error to code 3; merge's abort path
(`abortWith`) keeps the strict whole-plan stability rule, so an abort after
any plan update discards the measurements; merge recomputes facts with git
under the ledger lock when the plan changed; merge's branch for a node that
left the plan is unreachable (removing the node invalidates its candidate
first); the invalidation refusal says `its spec changed` also for `setup`,
`exec` or `closure` changes; a waiver of an obligation the candidate does
not have prints an empty key (unknown obligations are not refused, as
before); the threshold hint reads the node's observations across attempts,
so a new attempt's first under-count may hint at once; the node-models sha
test recomputes the plan sha formula instead of going through `owed plan`,
and a merge-cas test title says `without remeasuring` but counts only the
invariant. Without dsa, an owed attest that exits 75 on a ledger-lock
timeout is logged as `machine lease refused` (busyDetail); the behavior is
right. While a node stays busy the log gets a `started` and a busy line per
timed pass (0.6.x printed one busy line per busy period). A rejected ruling
send is recognized after a driver restart through dsa's request record;
whether that survives a dsa `prune` was not verified. Still open from
0.6.1: the writes hint lists node ids verbatim as globs and grants every
listed node the union of the new prefixes; a concurrent dispatch can make a
rollback report `rollback failed: directory cleanup`; a rollback that wraps
a non-OwedError drops its stack and the pi extension returns it as a tool
error; pi revalidation replays the whole ledger once per delivery attempt,
and a failed delivery's dropped wakes are already marked resolved; a
staying driver polls neither dsa events nor trunk drift while it waits.
Still open from 0.6.0: a follow-up dsa retires because the call sealed
before delivery still ends in a misleading `finished repair follow-up
without submitting` halt (awaits dsa §49); M4 and M6 of `owed05-big.sh`
remain within 9% of the 22M-state cap. The 0.6.1 candidates stay open:
bind a reviewer's identity to its dsa call; rulings that uphold or overrule
a named block. The formal model does not cover resume, superseded blocks,
parallel measurement or reused trees. A new candidate that changes the
flaky test itself does not clear the block; whether it should is deferred.
Field report #22 parts 1-2: a merge train is deferred.

## 0.8.0

The host project's run of 2026-10-10 reported recovery, check-definition and ruling
routing frictions (feedback #16-#22): L1 adds resume without an obligation
(#16-#18), L2 supersedes blocks after a check definition changes (#19-#20),
and L3 delivers rulings before more halts and warns about looped checks
(#21, #22 part 3).

- **Resume without an obligation (L1; field report #16-#18).** `owed resume <node>
  [--after <node>] [--note TEXT]` (pi `owed_resume`) records a `resume` entry
  on the node's open attempt. Only a parent or owner may resume; writers,
  reviewers, executors and `parent:drive` are refused, as are a missing open
  slot, an unknown `after` node or the node itself. Resume clears the halt
  and starts a new repair epoch without changing `rulings` or any other
  obligation. With `--after`, the node waits until that dependency merges;
  an already merged dependency resumes it now. The latest resume replaces
  the previous one; a resume without `after`, abandon or a new dispatch ends
  the wait. While waiting the driver launches, sends, halts, attests and
  merges nothing for that node; row 5 still reports asking runs. Status and
  why show `waiting for <dep> (resume #<seq>)`, and the pass report lists
  waiting nodes. The loop prints the quiet waiting event once per node and
  resume; `--once` and `owed_drive` print it each pass, without a wake. Once
  the wait ends, normal rows apply. Without `after`, the next pass can retry
  measurement or an environmentally refused merge, or send a fresh repair
  to a sealed writer. A repair sent before resume whose writer seals later
  also gets a fresh repair under the new budget. Resume does not fix the
  cause of a halt: row 7 (writer sealed non-ok) and row 14 (missing review)
  can halt again; L3's due-ruling route below applies at row 7. The first
  writer follow-up after resume starts with `The parent resumed this node
  (#<seq>)[ after <dep> merged at <commit12>][: <note>]`. Use resume to retry
  or wait without acknowledgment debt; use `owed rule --nodes <node>` for
  guidance the writer and reviewers must follow and acknowledge. Halts and
  wakes naming a run now include its dsa call address, when known, as
  `to:"<wid>/<key>"`, including asking notices without a reported question.
- **Supersede changed check definitions (L2; field report #19-#20).** Replaying a
  plan entry marks every active or flaky execution block on `check:<id>`,
  `red:<id>` or `strength:<id>` as `superseded` when the check definition
  differs from the plan of its failing observation, and records
  `supersededBy: <plan seq>`. The definition is the node's canonical
  CheckSpec with all fields, plus `plan.setup` and `execKey(plan)`; node
  title, brief, closure and other nodes do not count. Removing the check
  (or node) also supersedes its blocks. A superseded block no longer blocks,
  gets no attribution rerun, makes a queued rerun not current, and needs no
  waiver; the current candidate must still pass the new definition. With
  the same definition, flaky behavior is unchanged. Why and status retain
  `#<seq> superseded by plan #<p> (check <id> definition changed)` (or
  `check <id> removed`); report gives superseded blocks their own section.
  Earlier waivers remain recorded. The parent or owner's recorded plan
  accounts for the definition change; downgrades such as lowering
  `min_tests` or removing a check still need owner authority. Checks and
  invariants run in a fresh temporary worktree of the candidate or merge
  tree: slot-only build outputs, caches and node_modules are absent. They
  must prepare their own artifacts, for example through `setup`; fixing an
  environmental failure in the check definition supersedes the old block.
- **Rulings before halts and loop warnings (L3; field report #21, #22 part 3).** An
  undelivered in-scope ruling naming the node can now reach a sealed writer
  before the owner-needed notify (including flaky or rank >= 2 blocks),
  row 7's writer-sealed-non-ok halt and the stalled halt. No reviewer run of
  the current candidate may be unsealed, and an active needs-parent review
  block on the current candidate's keys (else the latest submit's) keeps
  its D18 route. Owner-needed and row 7 send the ruling to the writer. At
  stalled, a failed item, an active block on the candidate's keys or a flaky
  block sends it to the writer; when only `rulings` is owed, it goes to the
  latest sealed driver reviewer of that candidate, or launches a reviewer
  per row 12 if there is none. The reviewer re-reviews with `--ack-rulings`
  or blocks: a writer resubmit does not discharge `rulings`. Without a
  review or closure-review obligation, the node still stalls. Each ruling
  follow-up records what it carried and is sent once, without spending the
  repair budget; a remaining owner need or missing candidate or verdict
  then notifies or halts as usual. Row 18 keeps its existing K5.2 gate.
  At the owner-needed notify only a sealed writer gets the ruling (a running
  or asking writer keeps the notify), and a recorded but unapplied ruling
  follow-up is resent under the same id instead of being skipped.
  Writer ruling messages also list the node's flaky blocks, marked flaky.
  Flaky hints now offer `owed rule "<what the writer must change>" --nodes
  <node>` alongside the waiver: after the writer fixes the test, the block
  stays flaky until a plan change supersedes its definition or the owner
  waives it once the fixed candidate passes. `owed plan` and `owed init`
  (CLI and pi tools) warn without refusing when a check or invariant sets
  `min_tests` and runs a shell loop (`for`, `while` or `until` with `do`, or
  `seq N` in command position). After the check-less warnings, the warning
  explains the actual count: only the last TAP `# tests` summary (else the
  last `1..N` plan), jest/vitest `Tests:` or pytest summary counts, not the
  sum of repeated runs; only cargo `test result:` lines are added up.

**Compatibility.** 0.7.x cannot replay a ledger containing a `resume` entry.
Superseded blocks change no existing entry's validity: old ledgers replay,
including attribution observations and risk waivers that name a block now
superseded. Validation tracks the state it would have had without supersede;
those entries leave its visible state superseded. Upgrade the CLI, the pi
extension and remote executors together, then restart the driver and pi.
Wake and notice texts change: a batch containing a halt gains one resume
hint line (with `--after` for waiting); halt reasons stored in the ledger do
not gain that hint. Run references include `to:"<wid>/<key>"` when known,
and flaky hints offer a ruling as well as a waiver. Driver behavior changes
at row 7 and stalled: a due ruling can produce a writer follow-up instead
of a halt; a stalled candidate owing only `rulings` is routed to a reviewer
for acknowledgment under the conditions above.

**Deferred.** `error_expect` for environment-precondition failures (#12): a
check reports these today by exiting 126/127 without a count, which records
`error`, not a failure. Per-tier model routing (#11 asked for a tier or risk
mapping; routing remains per node). A plan warning for unknown node keys
(typos like `drvie:` are ignored; refusing them would change which existing
plans are accepted). Attest-busy nits: a dispatch whose ledger-lock timeout
is followed by a failed rollback still exits 75 `retry` though leftovers
may block a retry; `--json=x` gets no JSON error line (the CLI matches
`--json` only). The SIGKILL limit: a stop at once SIGKILLs the dsa
invocations' and attest children's process groups only; the checks an
attest runs have process groups of their own, so a check whose attest was
SIGKILLed before ending it keeps running, unrecorded, until it exits.
K review nits not fixed: with per-node locks, parallel node attests each
measure a pending genesis invariant (correct under D24, more CPU); lock
owners record no process start time, so a stale attest lock whose pid was
reused reads as busy indefinitely, and the driver retries without a halt;
the `hold` comment in `src/dsa.ts` still says owed exits only 0..3; the
driver's `--json` loop exit record maps a `busy` error to code 3; merge's
abort path (`abortWith`) keeps the strict whole-plan stability rule, so an
abort after any plan update discards the measurements; merge recomputes
facts with git under the ledger lock when the plan changed; merge's branch
for a node that left the plan is unreachable (removing the node invalidates
its candidate first); the invalidation refusal says `its spec changed` also
for `setup`, `exec` or `closure` changes; a waiver of an obligation the
candidate does not have prints an empty key (unknown obligations are not
refused, as before); the threshold hint reads the node's observations
across attempts, so a new attempt's first under-count may hint at once; the
node-models sha test recomputes the plan sha formula instead of going
through `owed plan`, and a merge-cas test title says `without remeasuring`
but counts only the invariant. Without dsa, an owed attest that exits 75 on
a ledger-lock timeout is logged as `machine lease refused` (busyDetail);
the behavior is right. While a node stays busy the log gets a `started`
and a busy line per timed pass (0.6.x printed one busy line per busy
period). A rejected ruling send is recognized after a driver restart
through dsa's request record; whether that survives a dsa `prune` was not
verified. Still open from 0.6.1: the writes hint lists node ids verbatim as
globs and grants every listed node the union of the new prefixes; a
concurrent dispatch can make a rollback report `rollback failed: directory
cleanup`; a rollback that wraps a non-OwedError drops its stack and the pi
extension returns it as a tool error; pi revalidation replays the whole
ledger once per delivery attempt, and a failed delivery's dropped wakes
are already marked resolved; a staying driver polls neither dsa events nor
trunk drift while it waits. Still open from 0.6.0: a follow-up dsa retires
because the call sealed before delivery still ends in a misleading
`finished repair follow-up without submitting` halt (awaits dsa §49); M4
and M6 of `owed05-big.sh` remain within 9% of the 22M-state cap. The 0.6.1
candidates stay open: bind a reviewer's identity to its dsa call; rulings
that uphold or overrule a named block.

The formal model does not cover resume or superseded blocks. A new
candidate that changes the flaky test itself does not clear the block;
whether it should is deferred. Field report #22 parts 1-2 are not included: driver
self-rebase is not needed; a merge train is deferred.

## 0.7.0

The host project's run of 2026-10-10 reported lock, threshold, budget, routing and
throughput frictions on 0.6.0/0.6.1 (feedback #8-#14): K1 answers the attest
lock collisions (#8, #12, and the global lock of #15), K2 the bare halt and
waiver notes (#8, #12), K3 per-node models (#11), K4 the merge refused by an
unrelated plan update (#14), K5 the repair budget and undelivered rulings (#8,
#9, #11) and K6 the serial driver (#13).

- **Per-node attest lock, busy exit (K1; field report #8, #12, #15).** In the host project a writer
  ran `owed attest`, as `owed why` told it to, while the driver attested the
  same node; the second attest waited 60 s for the repository-wide `attest`
  lock, failed with `Internal error: timed out waiting for attest` (exit 3), and
  the driver halted (#539). `owed attest <node>` now takes a lock of that node
  only, `attest-<first 16 hex of sha256(node id)>`, so attests of different
  nodes run in parallel (each observation is appended under the ledger lock only
  while its item is current, D24; temporary worktrees, git worktree admin
  directories and log blobs get unique names). `attest --genesis` keeps its
  `genesis` lock. When the node's lock (or `genesis` for `--genesis`) is held by
  a live process on this host, attest fails at once with the new `OwedError`
  code `busy` and exit 75: `attest of <node> is already running (pid <pid> on
  <host> since <ISO time>); its observations will appear in owed why <node>`
  (`--genesis`: `attest --genesis is already running (…); its observations will
  appear in owed status`). A dead owner on this host is reaped as before; a lock
  of another host is waited for (60 s) and then fails with the same text. Every
  other lock wait that times out is `busy` too: `timed out waiting for the
  <name> lock held by pid <pid> on <host> since <time>; retry` (the ledger lock
  reads `the ledger lock`), no longer `internal`. The CLI prints `Busy: <msg>`
  on stderr and exits 75; pi tools return `Busy: <msg>` as a tool error with
  details `{code: "busy", reason}`. With `--json` every command that ends with an
  error (an OwedError or an internal error) now also prints one line `{"error": <message>, "code": <code>}` on stdout; stderr and
  exit codes are unchanged. Where owed suggests `owed attest <node>` to a writer
  (the `owed why` clear hint of an execution block, which the driver's repair
  message embeds), it adds `(skip this when owed drive is running: the driver
  attests)`. A dispatch by the driver (`parent:drive`) stores a packet ending
  with `the driver measures your candidate; do not run owed attest`, and every
  driver writer task carries that line; a manual dispatch stores its packet
  without it.
- **Count notes, block notes, what a waiver means (K2; field report #8, #12).** In the host project
  a check that ran 100 of 100 passing tests against `min_tests: 200` showed only
  `zero tests or min_tests unmet`. A non-red check that fails only on its count
  now notes `min_tests unmet: counted <tests> (<pass> pass, <fail> fail[, <skip>
  skip]) < min_tests <m>; exit <code>` or `zero tests; exit <code>`; red notes
  are unchanged, and the observation gains no field. `owed why` shows the note
  of an execution block's failing observation on the block line (` — note:
  …`), and `owed status` on the pending item it holds (` — note #<seq>: …`),
  one line of at most 200 characters (`--json`: `blocks[].note`,
  `pending.<group>[].blockNotes`). In the host project an owner learned only from the waived status
  that `--accept-risk` stopped measurement (#12). `owed waive` (CLI text,
  `--json` field `meaning`, the pi tool's text and details) now states what the
  reducer applies (review #781): `waived <obligation> for candidate #<submit>
  <commit12> (key <key12>): <effect>; …`. The waiver is recorded for the key,
  not for one candidate: it counts for every candidate of the node with that
  key, in this or a later attempt, while no unaccepted active block remains on
  the obligation; a later block suspends it and clearing that block restores
  it. The effect says whether it is in effect now, not yet (naming the
  unaccepted blocks and what clears each), not needed (the item is satisfied),
  or not applicable; the text says which change of the key makes owed measure
  it again, and that a measured obligation whose key has no observation yet is
  still measured. Each flaky block in `--accept-risk` adds `the flaky block #<seq> stays recorded as
  accepted risk`. The text is computed from the ledger right after the waiver,
  so a later entry cannot relabel it. `owed why` shows a waived item as
  `<subject>/<obligation> waived (not measured for this candidate) by <who>:
  <reason> (<channel>)`.
- **Node models (K3; field report #11).** In the host project every driver-launched writer used the
  plan's one writer model, so cheaper slots stayed idle. A node may set
  `drive: {writer?: {agent?, model?}, reviewer?: {agent?, model?}}`, validated
  like the plan's roles (unknown keys and bad types are errors). For each role
  the node sets, an object with `agent` replaces the plan's role (the model is
  the node's, else none: the agent's default applies); one with only `model`
  keeps the plan's agent and overrides its model. `drive: {}` and an empty role
  object are dropped, so a plan without node `drive` keeps its canonical bytes
  and sha. Node `drive` is not an obligation: changing it is never a downgrade,
  invalidates no candidate and affects only later launches; a re-launch sends
  its stored spec bytes. `owed why <node>` shows `Drive: writer <agent>
  (<model>) · reviewer <agent> (<model>)` with the effective values when the
  node sets `drive` (`(<model>)` only when a model is set; `--json` `drive`).
  README, SPEC §12.2 and the `owed` skill describe it.
- **Node-scoped merge CAS (K4; field report #14).** In the host project a 5-minute merge measurement
  was refused with `Plan, candidate or trunk changed; retry` after two plan
  updates that did not touch the node, and was measured again from scratch.
  After measuring, `owed merge` now compares under the ledger lock only the
  ledger trunk commit, the trunk ref, the node's slot (dispatch seq, attempt,
  base, open) and its candidate (submit seq, commit). If one moved it refuses
  and records nothing, naming the cause: `slot of <node> changed (#<seq>
  <kind>); nothing recorded`; `candidate #<S> <commit12> of <node> was
  invalidated by plan #<N> (its spec changed); the writer submits again, then
  merge`; `trunk changed (CAS): …`; otherwise `Plan, candidate or trunk changed;
  retry`. A plan update that keeps the candidate, and entries of other nodes,
  no longer refuse. The guard runs on the latest state under the latest plan
  (merge facts and keys are recomputed when the plan changed) plus the measured
  observations whose job is still current (D24.1); the others are dropped. A
  job the run did not measure (a key the update introduced, e.g. a changed
  invariant) refuses with `not measured: <subject>/<obligation> (key <key12>),
  … (the plan changed while merge measured); run owed merge again: it measures
  only what lacks a verdict`. On a guard refusal the kept observations are
  appended, so a retry measures only jobs without a verdict (a pass or fail is
  reused, an `error` is measured again). Adopt keeps its strict rule.
  `formal/REPORT-mc-owed.md` §9 "0.7 merge CAS" shows the node-scoped CAS stays
  within what the model checks (`merge_guard`, `merge_jobs` in `owed05.rs`).
- **Repair epoch, rulings to sealed writers, threshold hint (K5; field report #8, #9,
  #11).** In the host project a parent plan mistake used up the repair budget and a later
  ruling never reached the halted writer (FLAKY-COLDPLAY #406, QA-REGISTRY
  #427); the parent sent follow-ups by hand. `drive.repairs` now counts the
  attempt's repair sends after the node's epoch: the latest of its dispatch,
  the latest ruling naming the node (`*` does not count) and the latest plan
  entry that changed the node's canonical spec (ignoring `title` and node
  `drive`; `brief` counts). Both repair rows use it, and the halt reads `repairs
  exhausted (<k> of <n> since ruling #s | plan #s | dispatch): <cause>`. When
  the writer run is sealed and a ruling naming the node is undelivered, the
  driver sends one follow-up with reason `ruling` in place of a `finished …
  without submitting` halt or the `repairs exhausted` halt (the only halts it
  can replace, review #784 F2): `New parent rulings for <node>:`, one line per
  undelivered in-scope ruling, the active blocks of the current (else latest)
  candidate with their notes, then `Apply these rulings; they override your
  packet. Then commit and run \`owed submit <node>\`.` It records `rulings`, is
  not a repair, and a writer that finishes it without submitting halts naming
  it; dsa rejecting it halts at once. The submit, rebase, repair and ruling
  follow-ups to the writer carry the undelivered in-scope rulings first and
  record `rulings`: `submit` and
  `rebase` follow-ups record it too (new; only when they carry a ruling) and
  start with `Parent rulings for <node> (apply them; they override your
  packet):`, and a repair message now starts with its `Rulings since dispatch:`
  block. A send dsa rejected delivers nothing to the writer (review #784 F1):
  the driver reads dsa's request state of the attempt's recorded sends each
  pass (cached once terminal), and the next writer follow-up carries those
  rulings. When the node's latest two failing, non-attribution observations of
  check X both exited 0 with no failure and the same test count below X's
  `min_tests`, and no ruling naming the node followed, the driver halts for the
  parent instead of repairing: `check <X>: <tests> tests ran and passed twice,
  below min_tests <m>; the plan's threshold may be wrong: fix the plan (owed
  plan) or rule (owed rule --nodes <node> "…")`; the first under-count still
  gets a repair. The `repairs exhausted` and `finished … without submitting`
  halts list each deciding failing observation as `#<obs> <obligation>: <note>`
  (at most 200 characters). SPEC and the skill say: after fixing a plan mistake,
  or to let the writer continue, record a ruling naming the node.
- **Background measurements (K6; field report #13).** In the host project one 5-minute merge
  measurement held every other node (16:03-16:09) with Opus 3/12 busy. The
  driver now starts attests (a child process, as before) and merges
  (`ops.merge` in the driver process) and goes on: `decide` gives a node with a
  measurement in flight no action, and other nodes are dispatched, launched,
  sent to and halted as usual. At most `drive.measure` measurements run at once
  (new plan key, integer >= 1, default 2; kept in the parsed plan only when
  set) and at most one merge. A started measurement logs a quiet `{"do":
  "attest"|"merge","node":…,"outcome":"started"}`; one that ends wakes the
  loop, and the next pass handles the result first as before (report, halts,
  rebase). An `owed attest` exit 75 (K1) is `busy`, like hold refusing the
  lease: never a halt, not progress, no wake, and the node waits for the next
  timed or event pass (review #785: the first version retried 53 times in 8 s);
  every `started` line gets its completion line. While its own merge is in
  flight or unhandled, the driver makes no trunk drift check (review #785: its
  own merge raised a false drift notify). The K4 refusals are handled: `not
  measured` → retry next pass; `slot … changed` and `candidate … was
  invalidated` → `superseded`, no halt. `owed drive --status`, `/owed` and
  `owed_drive` status list `<node> <attest|merge> since <time>` from the log
  (`--json` `measuring`). A driver with a measurement in flight is never idle.
  Stopping: the first SIGINT/SIGTERM of the loop (so `owed drive --stop`, a
  SIGTERM as in 0.6.x) and `owed_drive` stop start nothing new and wait for the
  measurements without a time limit, then exit `stopped`; a second signal (so
  `--stop --now`) or the first under `--once` stops at once: it aborts the merge
  (its checks get SIGKILL; a merge aborted before its last abort point does not
  move trunk, D16a.1), SIGTERMs the dsa invocations
  and attest process groups, waits 5 s, SIGKILLs those groups, waits 1 s, then
  writes the `killed` line and exit record and exits 130. A loop error aborts
  an in-flight merge and waits up to 6 s for it. `--once` and the pi tool's pass
  wait for the measurements they started. A driver that died leaves its attest
  children to finish (their observations land under D24); the new driver's
  attest of that node gets exit 75 and retries later.

**Compatibility.** The ledger entry kinds are unchanged, and a plan that sets
neither node `drive` nor `drive.measure` keeps its canonical bytes and sha.
pi-owed 0.6.x refuses a plan that sets `drive.measure` (`drive.measure: unknown
key`: its plan-level `drive` block rejects unknown keys), both as a plan update
and when it replays a ledger that recorded such a plan. 0.6.x does not validate
node keys, so it ignores a node's `drive` and launches that node with the
plan's drive. 0.6.x replay refuses a ledger with a `submit` or `rebase` send
carrying `rulings` (`send rulings is only allowed with reason ruling or
repair`); it accepts the `ruling` follow-ups, since a `ruling` send always
carried `rulings`. Exit 75 and the `busy` code are new; lock timeouts that were
`internal` (exit 3) are `busy` (exit 75). With `--json` every `OwedError` (and
an internal error) now also prints `{"error","code"}` on stdout, where before a
failure printed nothing there. The merge CAS is node-scoped: a plan update that
keeps the merging node's candidate no longer refuses its merge, and the new
refusal texts above replace `Plan, candidate or trunk changed; retry` for their
causes. Upgrade the CLI, the pi extension and the remote executors together: a
0.6.x attest takes the repository-wide `attest` lock and a 0.7.0 attest the
node's lock, so the two do not exclude each other on one node (both record
under D24, but the node is measured twice), and a 0.6.x process cannot replay a
ledger with the plans or sends above.

**Deferred.** `error_expect` for environment-precondition failures (#12): a
check reports these today by exiting 126/127 without a count, which records
`error`, not a failure. Per-tier model routing (#11 asked for a tier or risk
mapping; 0.7.0 routes per node). A plan warning for unknown node keys (typos
like `drvie:` are ignored; refusing them would change which existing plans are
accepted). Attest-busy nits: a dispatch whose ledger-lock timeout is followed
by a failed rollback still exits 75 `retry` though leftovers may block a
retry; `--json=x` gets no JSON error line (the CLI matches `--json` only). The
SIGKILL limit: a stop at once SIGKILLs the dsa invocations' and attest
children's process groups only; the checks an attest runs have process groups
of their own, so a check whose attest was SIGKILLed before ending it keeps
running, unrecorded, until it exits. K review nits not fixed: with per-node
locks, parallel node attests each measure a pending genesis invariant (correct
under D24, more CPU); lock owners record no process start time, so a stale
attest lock whose pid was reused reads as busy indefinitely, and the driver
retries without a halt; the `hold` comment in `src/dsa.ts` still says owed
exits only 0..3; the driver's `--json` loop exit record maps a `busy` error to
code 3; merge's abort path (`abortWith`) keeps the strict whole-plan
stability rule, so an abort after any plan update discards the measurements;
merge recomputes facts with git under the ledger lock when the plan changed;
merge's branch for a node that left the plan is unreachable (removing the node
invalidates its candidate first); the invalidation refusal says `its spec
changed` also for `setup`, `exec` or `closure` changes; a waiver of an
obligation the candidate does not have prints an empty key (unknown
obligations are not refused, as before); the threshold hint reads the node's
observations across attempts, so a new attempt's first under-count may hint at
once; the node-models sha test recomputes the plan sha formula instead of
going through `owed plan`, and a merge-cas test title says `without
remeasuring` but counts only the invariant. Without dsa, an owed attest that
exits 75 on a ledger-lock timeout is logged as `machine lease refused`
(busyDetail); the behavior is right. While a node stays busy the log gets a
`started` and a busy line per timed pass (0.6.x printed one busy line per busy
period). A rejected ruling send is recognized after a driver restart through
dsa's request record; whether that survives a dsa `prune` was not verified. Still open from 0.6.1: the writes
hint lists node ids verbatim as globs and grants every listed node the union
of the new prefixes; a concurrent dispatch can make a rollback report
`rollback failed: directory cleanup`; a rollback that wraps a non-OwedError
drops its stack and the pi extension returns it as a tool error; pi
revalidation replays the whole ledger once per delivery attempt, and a failed
delivery's dropped wakes are already marked resolved; a staying driver polls
neither dsa events nor trunk drift while it waits. Still open from 0.6.0: a
follow-up dsa retires because the call sealed before delivery still ends in a
misleading `finished repair follow-up without submitting` halt (awaits dsa
§49); M4 and M6 of `owed05-big.sh` remain within 9% of the 22M-state cap. The
0.6.1 candidates stay open: bind a reviewer's identity to its dsa call; rulings that uphold or
overrule a named block.

## 0.6.1

The host project's run of 2026-10-10 reported four driver and planning frictions
(feedback #4-#7): H1 answers #4 and #5, H2 answers #6 and #7. H3 closes three
0.6.0 deferrals.

- **Live wakes (H1.1; field report #5).** In the host project the parent read a question from the
  drive log and answered it, but the follower had already handed its wake to pi
  as a follow-up while the agent was busy, so pi delivered the stale wake after
  the answer. The pi extension now tracks the session's agent (`agent_start` …
  `agent_settled`, and `ctx.isIdle()` of the latest event context) and never
  hands a wake to pi while it runs: the follower keeps reading and holds the
  batch. At `agent_settled` (every follower steps at once) or at the next tick
  while idle, the batch is revalidated and what is left is delivered with
  `triggerTurn` as before; a batch that finds the agent busy again after the
  revalidation stays held. Revalidation runs on every pi delivery, also for a
  batch read while idle, and on wake lines only. The driver's asking notify
  carries the log-line fields `rid`, `qid` and `rev` (the run's first listed
  open question; the text is unchanged), and the line is dropped when `describe`
  of that run no longer lists that qid/rev as open; a failed describe keeps it.
  Checking only the first question is enough: once it is answered the notify
  text changes, and the next pass prints the line of the remaining question,
  which is delivered; a line whose first question is still open is delivered
  even if a later one was answered. A node-scoped line with a fact mark is dropped when the
  node's current fact mark (`factMark`) is higher: someone acted on the node,
  and a condition that still holds is reported again by the driver's next pass
  with the new mark; a failed ledger read keeps it. Terminal lines, drift lines,
  `idle-wait` and lines without a fact mark are never dropped. The ledger is
  read and each run described at most once per delivery. A dropped wake that is
  still its key's latest record also drops that record's repeat ride-along, and
  later repeats of it do not ride along. When lines were dropped the message
  ends with `(<n> wake(s) resolved before delivery)` after `Next: …`, counting
  that delivery's drops only; when no wake line is left nothing is delivered and
  the session is not woken (merges and repeats ride along with the next
  message). A failed `sendMessage` keeps the whole batch, revalidated again at
  the next tick. A follower without the pi hooks (the CLI-era `tick`) is
  unchanged.
- **Staying driver (H1.2; field report #4).** In the host project the driver exited idle after a
  merge, the parent then added seven ready nodes, and no driver dispatched them.
  `owed drive --stay` (with the loop or `--detach`; a usage error with `--once`,
  `--status` or `--stop`) and `owed_drive` action `start` with `stay: true`
  (refused with other actions) are opt-in. An idle pass of a staying driver does
  not exit: it logs `{"event":"idle-wait","at":…}` once per idle period (text
  `idle: nothing open and nothing ready; staying until the ledger changes (owed
  drive --stop ends it)`), which wakes the session and is not terminal. The
  driver keeps the lock and polls every `pollMs` the hash of the last complete
  entry of `ledger.jsonl`, read from the file's end, until it differs from the
  head the idle pass decided on, then resumes passes. That baseline is the idle
  pass's own head (review #725): an entry appended while the idle pass ran, such
  as a plan update adding a ready node, already counts as a change and the next
  pass runs at once; the first candidate read the head after the pass and then
  waited for a further change with the new node undispatched. An idle period
  ends with a pass that is not idle, so a ledger change that leaves the driver
  idle logs no second `idle-wait`. A stop or signal ends it as before (exit
  record `stopped`). `--detach --stay` passes `--stay` to the detached driver;
  the lock does not record it. While the driver runs and its log, after the last
  exit record, has an `idle-wait` followed by no action other than a notify,
  `owed drive --status` adds `idle, waiting for ledger changes since <at>`
  (`--json`: `idleSince`), and `/owed` and `owed_drive` status show it too.
  Without `--stay` an idle pass exits `idle` as before. SKILL.md recommends
  `stay: true` when the plan will grow.
- **Ready hint (H1.3; field report #4).** After a successful `owed plan` or `owed_plan`,
  when the new state has nodes the driver would dispatch now (the `dispatch`
  actions of its `decide`: readiness, `drive.max`, writes overlap, owner-needed)
  and no driver holds the repository's lock (a live lock or a lock of another
  host counts as a driver, a stale one does not), the output adds one line: CLI
  `ready: <ids> (<n>); no driver is running: owed drive --detach --stay`, pi
  `ready: <ids> (<n>); no driver is running: owed_drive {action:"start",
  stay:true}`. The ids are in the driver's dispatch order. `--json` output and
  the tool details gain `ready: string[]` and `driver: false` only together with
  the line. Nothing starts automatically, and a failure computing the hint adds
  nothing. The line comes before the H2.2 warnings, which end the plan text
  (SPEC §12.9).
- **Allowance guidance (H2.1; field report #6).** In the host project two writes questions within 15
  minutes each cost a plan version and an owner act. README and the `owed` skill
  gain a planning recipe: keep writes strict by default, and at plan time
  pre-authorize the `writes` prefixes integration and packaging nodes tend to
  need with an `allow` rule, e.g. `allow: [{nodes: ["KB*", "A9-*"], writes:
  ["app/src/entry/", "package.json"]}]`; a writer's writes question then costs
  one parent plan update and no owner step. A parent plan update refused only
  for widened writes (every uncovered downgrade is `writes widened` or `writes
  scope expanded`) adds, when there are new prefixes, in the CLI and in
  `owed_plan`, one line with a
  ready-to-paste rule for the next plan: `hint: an allow rule {nodes: ["KB4"],
  writes: ["<new prefixes>"]} in the prior plan would cover this`, the new
  prefixes being those under neither the node's prior writes nor a matching
  prior rule.
- **Check-less nodes (H2.2; field report #7).** In the host project a milestone node with no check
  merged on a review of its evidence, although its writer reported the milestone
  not met. `owed init` and `owed plan` (CLI and pi tools) warn, refusing and
  recording nothing, for each node of the new plan with no checks and no
  evidence obligations: `warning: node <id> has no checks: its acceptance rests
  on review alone`. Every such node is warned about, not only one whose title
  says "Milestone": a title is not a contract. The CLI prints the warnings after
  the result; `--json`, `owed_init` and `owed_plan` return `warnings: string[]`
  (empty when there are none).
- **What an ok review means (H2.3; field report #7).** The review packet adds a line
  before the needs-parent instruction: `--ok` means the candidate meets the
  node's goal as its title and brief state it, not only that the writer's report
  or evidence is accurate; if the candidate or the writer's report says the goal
  is not met, record `--block` (`--needs-parent` when the goal itself is in
  question). The driver does not parse review notes. The `owed` skill states
  the same meaning of an ok review.
- **0.6.0 deferrals (H3).** Every step of a dispatch rollback (`git worktree
  remove`, `git branch -d`, directory cleanup) runs even when an earlier one
  fails; the error then names the original failure and each failed step
  (`<original>` followed by the line `rollback failed: <step>: <reason>; …`),
  keeps an OwedError's code (`internal` otherwise), and nothing is recorded, as
  before. A new plan (`owed init`, `owed plan`) is refused when two node ids are
  equal ignoring case (`KB4` and `kb4`), since on a case-insensitive filesystem
  they would share a branch ref and a worktree directory; replay is unaffected.
  `formal/run-a3-big.sh` caps its runs like `formal/models/scripts`: `ulimit -v
  8000000` for every run, and for the `owedmc check` runs explicit state caps (exhaustive 22M distinct states; simulations 2e9
  sampled states, so the walk count stays the bound), the model checker's
  `--timeout` and a wall-clock `timeout` 120 s above it; `a3_compare`, which has
  no state cap of its own, runs under the memory cap and a 3600 s wall clock.
  Rerun on the bench host with the caps, no cap stopped any configuration and every
  published a3 verdict and state count is unchanged
  (`formal/REPORT-mc-port.md`).

**Compatibility.** The ledger format is unchanged: no entry gains a field, so
pi-owed 0.6.0 can read a ledger written by 0.6.1. The asking notify's `rid`,
`qid` and `rev` are fields of the drive log line, not of the ledger. `--stay` is
opt-in: without it the driver exits idle as before. The wake timing and the
revalidation change only in pi; a follower without the pi hooks delivers as
before. The JSON output of `owed plan` and `owed init` and the details of
`owed_plan` and `owed_init` gain `warnings`; `ready` and `driver` appear only
together with the ready hint line. New plans are refused for node ids differing
only in case: a ledger whose recorded plan has such ids stays readable, but a
plan update that keeps both ids is refused.

**Deferred.** Review nits not fixed: the writes hint lists node ids verbatim in
`nodes`, which allow rules read as globs, so an id containing `[` does not match
itself and the pasted rule misses that node; with several nodes the hint grants
every listed node the union of all new prefixes (broader than needed, still
covering the update); a concurrent dispatch whose worktree lies in a parent
directory this dispatch created makes the rollback report `rollback failed:
directory cleanup` (ENOTEMPTY) although only empty directories are removed; a
rollback that wraps a non-OwedError drops its stack, and the pi extension then
returns it as a tool error instead of rethrowing it; in pi, revalidating a
batch replays the whole ledger once per delivery attempt, and when delivery
fails the kept batch's dropped wakes are already marked resolved; and a staying driver polls
neither dsa events nor trunk drift while it waits (only the ledger head). Still
open from 0.6.0: a follow-up forwarded into running work that dsa retires
because the call sealed before delivery still ends in a misleading `finished
repair follow-up without submitting` halt; the fix awaits dsa §49; M4 and M6 of `owed05-big.sh` remain within 9% of the
22M-state cap (a larger model stops at the cap: each property without a verdict
yet reports TIMEOUT, never HOLDS, and the exit status counts them). 0.7
candidates: bind a reviewer's identity to its dsa call; rulings that uphold or
overrule a named block.

## 0.6.0

The owedmc model of owed 0.5 (`formal/models`, `owed05`) checked acceptance and
authority and found five gaps, F1-F5 (`formal/REPORT-mc-owed.md` §6): G1 closes
F1 and F2, G2 closes F4 and F5 and adds a rail against F3. F6 (replay-only
entries, which never ease acceptance within the bounds) needs no change.

- **Candidate-bound acts name the candidate they judged (G1; F1, F2).** `owed
  review`, `owed waive`, `owed approve` and `owed evidence` accept `--candidate
  <commit>` (40 hex, or a prefix of at least 7 hex; anything else is a usage
  error), and the pi tools `owed_review`, `owed_waive`, `owed_approve` and
  `owed_evidence` an optional `candidate` string. The act is checked under the
  ledger lock at append time and refused unless the node has an open candidate
  whose commit starts with it: `candidate changed: you named <given>, the open
  candidate is #<seq> <commit12>; nothing recorded` (or `…, node <node> has no
  open candidate; nothing recorded`, which also refuses `--candidate` on a
  merged node's receipt). Without the flag no commit is checked, and the ledger
  records nothing new. Under `OWED_CONFIRM=owner` the waive and owner-review
  confirmations (pi dialog; CLI, unless `--i-am-owner`, printed before the TTY
  prompt) show the candidate — commit, submit seq, base and number of changed
  files, as approve's does — and pin it: a resubmit after the confirmation
  records nothing (`candidate changed since confirmation`). Every command owed
  suggests for a candidate-bound act on a node with an open candidate carries
  `--candidate <commit12>` of that candidate: the decision commands and clear
  hints of `owed why` and `owed brief`, the resolving commands of driver halts
  and notifications, the driver's approve/evidence halt (`awaiting owner
  approval …`, `awaiting manual evidence …`), and the review packet. The
  packet's `owed review` (and reviewer evidence) commands pass the commit under
  review, so a resubmit during the review makes recording fail instead of
  landing on content nobody reviewed; the packet tells the reviewer to re-read
  `owed why <node>` and review the new candidate when that happens. SPEC, README
  and the `owed` skill say to pass the commit actually read.
- **Judgment rails (G2; F3, F4, F5).** Inside a pi-durable-subagents call
  (`DSA_CALL` or `DSA_EXEC` set) owed refuses `review` and `evidence` on a node
  when a working directory of the process (the process's own directory; for pi tools
  also the `cwd` parameter and the session's directory), symlinks resolved, lies
  inside that node's open slot worktree: `a writer worktree cannot record a
  review or evidence for its own node; run the review from the repository root
  or another directory`. Driver reviewer runs start in the main worktree and are
  unaffected. Like the D25.3 rail it guards against accidents and instructions,
  not deliberate evasion (SPEC threat model). The review count of an obligation and the
  rulings acknowledgment now exclude every principal the append-time recusal
  excludes: any role whose id equals that of a writer of the node, in any
  attempt (0.5.1 excluded only the exact writer principal). `parent:drive` is
  the driver's identity only: on append and on replay the ledger refuses an
  entry by it of any kind but `dispatch`, `launch`, `send`, `halt` and `rebase`
  (`<kind> by parent:drive: the driver records only …`), and the CLI and the pi
  tools refuse `--as parent:drive` / `as: "parent:drive"` in every command,
  reads included.
- **Repair before re-review; one message to the writer (G3).** While a review
  block is active on the current candidate's key (and the node does not need the
  owner), the driver sends the repair before launching any reviewer run of that
  candidate; attest, the needs-parent halt and measured repairs keep their order
  before it. When the writer is sealed without a current candidate (a plan
  change invalidated it, or trunk moved and the slot was rebased) and an active
  review block recorded in this attempt on the key of its latest submit does not
  await a parent ruling, the driver sends one follow-up instead of the `submit`
  / `rebase` one: reason `repair` (counted against `repairs`; exhausted → halt),
  the rulings in scope since dispatch (and those quoted by needs-parent blocks)
  first, then the blocks with their notes, then why a new candidate is needed
  (the plan changed, or trunk moved with the `git rebase --onto` instructions),
  then commit and `owed submit <node>`. Its `rulings` records what it carried,
  so no separate ruling steer follows; a writer that finishes it without
  submitting halts, and identical content resubmitted gets another repair (or
  the exhausted halt), never a reviewer run. When a block on that key awaits a
  parent ruling, the driver halts for the ruling (needs parent) before any
  repair, `submit` or `rebase` follow-up, also when no repairable block exists,
  so a new candidate never makes an unruled question stale; with no block on
  that key the `submit` / `rebase` follow-ups are unchanged. A follow-up sent to a running writer is
  forwarded into its running generation (dsa's reply carries no `generation`):
  the driver no longer expects generation + 1 then, so the sealed view that
  follows counts as sealed (a sealed writer was shown as running and the rebase
  follow-up never went out); a follow-up to a sealed call still waits for the
  next generation. The rebase follow-up (and the rebasing repair) lists the
  files that conflict between the previous candidate and the new base (`git
  merge-tree --write-tree --name-only -z`, computed only in passes where that
  follow-up can be due), `none` when it merges cleanly, omitted when the previous
  commit is unknown or git fails. Trunk drift (0.5.1 deferrals): when a pass after a drift finds
  trunk equal to the ledger trunk again, the driver forgets the drift notify's
  print and wake records and logs the quiet event `drift-cleared`, on which the
  follower forgets its own, so an identical later drift prints and wakes again;
  the drift notify carries `scope: "repo"` and its records are keyed apart from
  a plan node named `trunk`; and when the ledger's trunk commit is absent the
  notify says so (`git update-ref` cannot restore it and `owed adopt` cannot
  check a fast-forward from it) and suggests fetching it (`git fetch <remote>
  <ledger>`), then restoring trunk or adopting the current ref (`owed adopt
  --commit <ref12> --note "<why>"`). `owed adopt` still refuses a missing ledger
  trunk.
- **Plan and worktree hygiene (G4; 0.5.0 deferrals).** A new plan (`owed init`,
  `owed plan`) is refused when `worktrees.root` or `worktrees.branch` contains a
  control character (below 0x20, 0x7f, U+2028, U+2029; 0.5.1 refused only NUL, and only in
  `worktrees.root`),
  or when the branch template could render two (node, attempt) pairs as the same
  name: `{node}` must occur exactly once, and each `{attempt}` or `{type}` must
  be separated from it by a character that cannot occur in that value (such as
  `/`), so `{node}{attempt}` is refused. These checks apply to new plans, not on
  replay, so existing ledgers stay readable. `owed gc` keeps a branch whose name
  is recorded for another node or attempt. A dispatch that fails and rolls back
  removes the parent directories it created. Allowance texts: `owed report` and
  `owed brief` list parent adoptions under allowance in their own adoptions
  section (owner adoptions stay under owner decisions); a parent's refused plan
  update lists each uncovered downgrade once; `owed brief` lists the downgrades
  recorded under allowance with the report's label (`by parent:<id> under
  allowance (plan #S)`).
- **Formal follow-ups (G5).** `formal/REPORT-mc-owed.md` §8 lists `error`
  observations as not modeled (the code records no verdict for them and skips
  their attribution). Every script in `formal/models/scripts` runs owedmc under
  `ulimit -v` 8 GB with `--max-states` and `--timeout`; `owed05-big.sh` lowers
  its cap from 14 GB and 40M states to 8 GB and 22M states and still completes
  M1-M6, S1 and S2 with the published states, depths and verdicts.

**Compatibility.** The ledger format is unchanged: no entry gains a field, so
pi-owed 0.5.1 can read a ledger written by 0.6.0. G2's recusal can change
replayed views of existing ledgers: a review by `reviewer:X#n` no longer counts
once `writer:X#n` exists. Replays of the pi-owed and production ledgers passed `owed
verify`, and their status, report and why outputs were byte-identical with and
without the recusal change. Replay now refuses an entry by `parent:drive` of a kind the driver never
writes, and `--as parent:drive` is refused. A subagent's review or evidence from
inside the node's slot worktree is refused; this is a rail, not a security
boundary: nothing prevents a `cd` to the repository root. Upgrade the `owed` CLI
on PATH together with the driver and the pi extension: 0.6.0 review packets and
suggested commands pass `--candidate`, which a 0.5.1 CLI rejects as an unknown
option.

**Deferred.** `owed plan --probe` (heuristic resolution of check commands; 0.5.1 already
reports exit 127 with the failing lines) and a plan warning for a check mixing
cargo and TAP. To 0.6.1: a dispatch rollback continues after a failing step
(worktree remove, branch deletion); node ids differing only in case on
case-insensitive filesystems; caps for `formal/run-a3-big.sh`; M4 and M6 are
within 9% of the 22M-state cap; a follow-up forwarded into running work that
dsa retires because the call sealed before delivery ends in a misleading
`finished repair follow-up without submitting` halt. 0.7 candidates: bind a
reviewer's identity to its dsa call; rulings that uphold or overrule a named
block.

## 0.5.1

- **Driver runs belong to the starting pi session (E1, pi-durable-subagents
  1.0.31).** When a driver starts (`owed drive`, `--once`, `--detach`, pi
  `owed_drive`), owed reads `DSA_SESSION` from its environment — ignored when
  `DSA_CALL` or `DSA_EXEC` is set (a subagent), exactly as dsa does, and when
  it is not a session id dsa accepts (a letter or digit, then up to 127 of
  `[A-Za-z0-9._:-]`) — and records it as `session` in `drive.lock` (absent when
  none); `--detach` hands the starter's value to the detached driver. Every
  `pi-durable-subagents run` the driver issues passes `--session <id>` when a
  session is recorded, and dsa child processes never inherit `DSA_SESSION`, so
  the lock and what dsa records never disagree. A dsa that refuses `--session`
  (older than 1.0.31) is detected once per driver: the flag is dropped, the run
  is issued again without it and one line is logged (`dsa does not accept
  --session (older than pi-durable-subagents 1.0.31): runs start without it and
  are not listed in pi session <id>`); it never halts. For a running driver
  `owed drive --status` (`--json` `session`), `/owed` and `owed_drive` status
  show `runs are listed in pi session <id>` or `no pi session: runs show only
  in pi-durable-subagents status / the CLI`.
- **Check results say what happened (E2).** The count parser sums every
  recognized segment of one log: cargo `test result:` lines and TAP
  plans/results in the same output give one count with format `mixed` (`tests`,
  plus `pass`/`fail`/`skip` where the TAP part reports them), which `min_tests`
  checks; single-format logs keep their format names and numbers. cargo
  summaries count only at column 0, so a TAP comment `# test result: ok. …` or
  an indented YAML diagnostic containing one stays TAP text. A non-red check
  that exits non-zero with no recognizable count, or with zero tests, is `fail`
  (no longer `error` with `unknown test count format with min_tests`), with the
  note `command exited <code> with no recognizable test count` (or `after zero
  tests`) `; last output:` followed by its last 5 non-empty output lines (ANSI
  colour removed, each at most 200 characters). Exit 126 or 127 with no count
  is now `error` also without `min_tests` (0.5.0 recorded `fail` there):
  `command could not run (exit 126: not executable)`, or `127: not found`, plus
  the same tail; the command never ran, so it says nothing about the code. An
  exit-0 run with an unknown count under `min_tests` stays `error`, and red
  runs are unchanged. `owed why`/`owed_why` show each fail observation's note
  under its item (`note #<seq>:`, then the note as recorded, indented), and so
  does the driver's repair message to the writer, which includes the `owed why`
  card. Check processes no longer inherit the `DSA_*` variables (dsa call
  identity) except `DSA_HOME` (configuration), so owed commands inside a check
  run under a dsa call are not refused as subagent acts; a variable the plan's
  `exec.env` sets still applies.
- **One wake per new fact; trunk drift is not a halt (E3).** A driver wake
  (halt, notify, rejected, conflict, refused, error) of a node carries the
  node's fact mark: the highest seq of the ledger entries naming it that the
  driver (`parent:drive`) did not write. The loop driver prints a node's notify
  only when its text or fact mark changed, and marks any other wake whose text
  and mark equal the node's last one with `(repeat <n>, no new ledger
  entries)`. The background follower wakes the session for such a line only
  when its text differs from the last one delivered for that node or the node's
  fact mark rose; a repeat does not wake and rides along with the next message
  with the same suffix. Lines without a fact mark (a 0.5.0 driver's log) wake
  as before. Trunk drift — the trunk ref no longer equals the ledger trunk — is
  a repository fact and never a ledger halt: each pass the driver compares them
  before merging; on drift it merges nothing that pass (other actions continue)
  and emits one repo-level owner notify, `trunk <name> moved outside owed
  (<ledger> → <ref>); the main agent resolves it with: owed adopt --note
  "<why>"` when the ref fast-forwards the ledger trunk, otherwise `trunk <name>
  was rewound or rewritten (<ledger> → <ref>); restore it: git update-ref
  refs/heads/<name> <ledger> <ref>`. It prints once per change (loop) and wakes
  once per change (follower). A CAS failure inside a merge (trunk moved
  meanwhile) is the same notify, retried next pass; other merge refusals still
  halt (needs human). After `owed adopt` the next pass merges with no other
  act. Owner halts use the D25.6 wording everywhere — driver output `halt
  <node> attempt <n>, needs the owner (the main agent decides; owed lists the
  command)`, the halt rows of `owed why` and `owed report` and halt entry lines
  (`owed status` lists owner halts under `Pending owner`) — and resolving
  commands that state a role say `--as owner:cli` (`--as owner:human` under
  `OWED_CONFIRM=owner`). The CLI's default owner principal is now `owner:cli`
  (channel `delegated`), matching pi's `owner:pi`; it is `owner:human` only
  under `OWED_CONFIRM=owner`.
- **Rulings carried, not inferred (E4).** Launch entries and `repair` sends now
  record `rulings`: the highest in-scope ruling seq their message actually
  carried, computed from the state the message was built from, `0` when it
  carried none (a writer launch: the rulings of its dispatch packet; a reviewer
  launch: every ruling in scope; a repair: the rulings since dispatch and those
  quoted by needs-parent block notes). A re-launch keeps the recorded value.
  The rulings delivered to a run are the maximum over its launch and its repair
  and ruling sends (and, for a writer, its dispatch `rulings_seen`); entries
  without the field (written by 0.5.0) keep 0.5.0's position rule. A ruling
  recorded while a launch or repair message is being built is therefore steered
  afterwards instead of counting as delivered. The reducer validates the field
  — an integer, at most the entry's own seq, and 0 or the seq of a ruling
  covering the node recorded before the entry — and replay refuses otherwise;
  other send reasons still may not carry it (`send rulings is only allowed with
  reason ruling or repair`).

**Compatibility.** pi-owed 0.5.1 reads 0.5.0 ledgers unchanged. A ledger on
which a 0.5.1 driver recorded any launch (or a repair send) cannot be read by
0.5.0: its replay refuses the entry (`Entry #N invalid: launch has unknown
fields: rulings`, or `send rulings is only allowed with reason ruling` for a
repair send), so `owed verify` fails. Upgrade every owed together — the CLI,
the pi extension and remote executor hosts — and stop a 0.5.0 driver first. The
CLI's default owner principal is `owner:cli`, also with `--i-am-owner` (which
still records channel `flag`); `owner:human` only under `OWED_CONFIRM=owner`.
Entries recorded earlier keep `owner:human`, and `--as owner:human` still
records that id. `drive.lock` gains the optional `session`, which 0.5.0 readers
ignore; with pi-durable-subagents older than 1.0.31 the driver falls back to
runs without `--session`. Halts recorded for trunk drift (`merge refused: …
trunk changed (CAS)`) by 0.5.0 drivers stay ledger halts: clear one by adopting
(`owed adopt --note "<why>"`) or restoring trunk, then `owed rebase <node>`;
the driver then attests again. A non-red check that exits non-zero without a
recognizable count, or with a zero count, is now `fail` (exit 126/127 with no
count: `error`, also without `min_tests`, where 0.5.0 recorded `fail`), so a
check that used to stall on `unknown test count format with min_tests` now
fails with its real error in the note.

**Known limitations, deferred to 0.5.2.** The loop's record of the printed
drift notify and the follower's last trunk wake are not reset when drift
clears, so an identical later drift (same ledger seq and ref) neither prints
nor wakes. A plan node named `trunk` shares the drift notify's dedupe slot (the
plan does not reserve the id yet). A drift whose ledger trunk commit is missing
still suggests `git update-ref` to that missing commit instead of saying it
cannot be restored. There is no `owed plan --probe` (a dry run of check
commands) and no plan warning for a check mixing cargo and TAP. Still open from
0.5.0: the owner allowance (D21) nits, an ambiguous branch template expanding
to the same name for two attempts, and parent directories left by a failed
dispatch.

## 0.5.0

- **The main agent is the owner; nothing waits on a human by default (D25).**
  Owner acts — plan updates with downgrades, `waive`, `defer`, `adopt`,
  `approve`, owner `evidence`, `init`, decoys, reviews as owner — run with no
  prompt and no dialog and are recorded with the new channel `delegated`. In pi
  the default owner is `owner:pi`. The CLI records `delegated` for owner
  commands (`--as owner:<id>`, or the commands that default to `owner:human`)
  with or without a TTY, so `--i-am-owner` is no longer needed (still accepted;
  it records `flag` as before). Every delegated act that eases acceptance says
  why: `waive`/`defer` `--reason` and `adopt --note` as before, and a plan
  update with downgrades now needs `owed plan --note TEXT` (pi `owed_plan`
  `note`; stored in the plan entry's new optional `note`) — refused without
  one, nothing recorded. `owed brief` starts with *Owner acts (delegated) since
  <since>*, one line per act with its kind, node, what it eased and the reason
  (`--json` `delegated`), and `report` marks such entries `(delegated)`: the
  morning reader audits the hash-chained ledger instead of approving in
  advance. The driver's owner notifications read `<node>: needs the owner (the
  main agent decides; owed lists the command): <reason>; the driver leaves it
  alone; the main agent resolves it with: <commands>`, and its owner halts for
  a stale review block or a stalled candidate end with the same `; the main
  agent resolves it with: <commands>`.
- **`OWED_CONFIRM=owner` restores the confirmation gate** of 0.4.1 (TTY prompt,
  pi dialog; channels `tty`/`pi-confirm`; pi default principal `owner:human`),
  except that the pi dialog waits at most `OWED_CONFIRM_TIMEOUT` seconds
  (default 120, `0` = no limit): a timeout refuses with `Owner confirmation not
  given within <N> s; nothing was recorded.` (code `refused`), an abort of the
  tool call refuses `aborted`, Escape (No) refuses `owner did not confirm;
  action canceled`, and none is read as a confirmation.
- **Subagents never act as owner or parent.** In a process with `DSA_CALL` or
  `DSA_EXEC` set (a pi-durable-subagents call) owed refuses every owner and
  parent act, in pi tools and the CLI alike, before recording anything:
  `owner and parent acts are reserved for the main agent; this process is a
  subagent call (DSA_CALL)`. Reads and `attest` are not refused, writer and
  reviewer roles are unaffected, and `owed drive` (`parent:drive`) is exempt:
  D17 starts the detached driver from dsa calls, and the driver never answers,
  waives, rules, changes the plan or forces anything. This is an accident
  rail, not a security boundary (a process can unset the variables).
- **Worktree location and branch names (D19).** Optional plan block
  `worktrees: {root, branch}`: `root` absolute or relative to the main
  worktree root (default `.owed/wt`), `branch` a template containing `{node}`
  and `{attempt}`, optionally `{type}` (default `owed/{node}/{attempt}`); new
  optional node field `type` (`^[a-z][a-z0-9-]*$`, default `feat`), used only
  by `{type}`. Dispatch creates `<root>/<node>-<attempt>` (recorded as the
  physical path, so writer inference matches git's toplevel under a symlinked
  root) on the expanded branch; a name that fails `git check-ref-format
  --branch`, or a root equal to the main worktree root, refuses (exit 2) before
  any effect. `.git/info/exclude` gets the root (escaped so it matches
  literally) only when it lies inside the main worktree. Submit, rebase, gc,
  the driver and the views read the branch and worktree recorded in the
  dispatch entry instead of reconstructing them. Neither key is an obligation:
  changing them is never a downgrade, invalidates no candidate and affects only
  later dispatches.
- **Trunk checked out in another worktree (D19).** A merge fast-forwards the
  linked worktree that has the trunk branch checked out, and when that
  worktree has uncommitted tracked changes it refuses before trunk moves:
  `trunk worktree <path> has uncommitted changes: commit them there, or detach
  it (git -C <path> switch --detach), then retry`. `owed status`,
  `owed_status` and `/owed` show `Trunk <name> is checked out at <path>; merges
  fast-forward it there (keep it clean).` (`--json` `trunkWorktree`). With a
  root outside the main worktree and the trunk checked out elsewhere,
  dispatch, merge and gc leave the main worktree's HEAD, index and files
  untouched.
- **Execution environment (D20).** Optional plan block `exec: {env, wrap}`.
  Every process owed starts in a materialized tree (setup, check, red,
  strength and invariant runs, attribution reruns) gets
  `{...process.env, ...exec.env, CI: "1", OWED: "1"}` and runs as
  `[...wrap, "bash", "-lc", <command>]`, with the same cwd, process group,
  timeout and abort handling. `env` names match `^[A-Za-z_][A-Za-z0-9_]*$`
  (not `CI` or `OWED`), values are used verbatim; `wrap` is a non-empty argv (a
  relative `wrap[0]` containing a slash resolves against the materialized
  tree). With `exec` set, check, red, strength and invariant keys contain
  `exec: {env?, wrap?}`; without it (or with `exec: {}`) keys are
  byte-identical to 0.4.1. Changing `exec` is owner-only, like changing
  `setup`: it invalidates submitted candidates, attribution reruns use the exec
  of the plan the block was recorded under, and the reducer records the
  downgrade `*: exec changed; cannot prove obligations were not reduced`
  (`wrap: ["true"]` would pass everything). `why` shows `Exec: wrap <argv> ·
  env <NAMES>` (names only). Wrapper contract: run the trailing argv to
  completion in the given cwd and environment, pass output through, exit with
  its exit code, and exit 126 or 127 when it cannot run the command
  (transport, mirror or lease failure). Red runs read 126/127 as `error` and
  any other non-zero code — including a remote `timeout`'s 124 — as a test
  failure, so use `red_expect` with wrappers; a wrapper that runs the command
  elsewhere must bound it there, because owed kills only the local process
  group. README recipes: slot limiter, dsa lease, shared build cache, remote
  host.
- **Owner allowances (D21).** Optional plan block `allow:`, a list of rules:
  `nodes` (id globs, default `["*"]`) with `review_count`, `review_rank`,
  `writes` prefixes and/or `checks` globs, or `adopt` prefixes. A parent plan
  update whose downgrades are all covered by a rule of the **prior** plan is
  accepted without the owner (a reducer rule, so replay checks it too): review
  count/rank lowered not below the rule's bound, writes widened inside its
  prefixes, a check or evidence obligation whose id matches its `checks` globs
  removed or weakened. Never covered: `approve removed`, `node removed`,
  `dependency removed`, trunk invariant downgrades, setup/closure/exec
  changes, and changes of `allow` itself (anything but deleting whole rules is
  the owner-only downgrade `trunk: allow changed`). Covered downgrades still
  enter ΔO⁻ and read `by parent:<id> under allowance (plan #S)`; a refused
  parent update lists the uncovered items. A parent may adopt (`owed adopt
  --as parent:<id>`, pi `owed_adopt` with `as: parent:…`, no prompt or
  dialog) when every changed path lies under an `adopt` prefix of the current
  plan and `adoptGuard` passes; the refusal names the first uncovered path.
  When the writes item fails, `why` lists the out-of-writes paths (first 20;
  `--json` `outOfWrites`) and adds `the parent may widen writes in the plan
  (allowance plan #S)` when an allowance covers them; a ruling never accepts
  them.
- **Rulings reach running calls (D22).** The driver sends parent rulings to
  its writer and reviewer runs that are `running` (never `asking`): a `steer`
  with the new send reason `ruling` (the `send` entry carries `rulings`, the
  highest ruling seq it lists) listing each undelivered in-scope ruling as
  `#<seq> (<nodes>): <text>`, then for a writer `Apply these rulings; they
  override your packet. If you already submitted, fix and submit again.`, for
  a reviewer `Judge the candidate against these rulings and record your review
  with --ack-rulings <seq>.`. Delivered counts the dispatch packet, earlier
  repair messages and ruling sends (writer) or the review packet and ruling
  sends (reviewer). It is the lowest row: still at most one action per node per
  pass, and every other action wins. A dsa rejection (e.g. the call sealed
  meanwhile) is printed, never halts and is never retried; the rulings then
  travel as before. A steer is not an acknowledgment: the `rulings` obligation
  still needs the reviewer's `ack_rulings`.
- **Run names and answer address (D22).** New launches carry dsa's run `name`
  (`owed <node>#<attempt> writer`, `owed <node>#<attempt> reviewer <n>`); a
  re-launch sends its stored spec bytes unchanged (launches recorded before
  0.5.0 have no name). The asking text (halts, drive output, wake-ups)
  addresses each question by dsa's call address `questions[].to`
  (`<wid>/<key>`), else the run id, in both forms: `subagents {action:"send",
  kind:"answer", to, qid, message}` and `pi-durable-subagents send --request
  <id> --to <to> --kind answer --qid <qid> --rev <rev> --message @<file>`.
- **Owner approval and manual evidence (D23).** Node field `approve: owner`
  adds obligation `approve` (keyed by the candidate's patch, like a review),
  discharged only by the owner: `owed approve <node> [--note TEXT] [--block]`
  (pi `owed_approve`); `--block` records an owner block that a later owner
  approval clears. Node field `evidence: [{id, what, by?}]` (`by` reviewer,
  the default, parent or owner; the owner always qualifies) adds obligations
  `evidence:<id>`, discharged by the new entry kind `evidence`: `owed evidence
  <node> <id> --file PATH... --note TEXT [--as role:id]` (pi `owed_evidence`),
  at least one file, each hashed (sha256, bytes) when recorded, by a principal
  of that role who never wrote the node. On a merged node the same command
  records an informational **receipt** (files optional; e.g. npm version,
  dist-tag, tarball sha256), listed by `why` and `report`. Candidate pin: the
  CLI approve preview and the pi `owed_approve`/owner `owed_evidence` dialogs
  pin the candidate they show; a resubmit, rebase or abandon in between
  refuses `candidate changed since confirmation; nothing recorded`. Views
  always mark these manual (`✔ approved (<owner>, <channel>)`, `✔ evidenced
  (manual) by <who>` with files as `path sha12` and the note; the brief counts
  `N manual`), never measured. Downgrades: `approve removed` (owner only, never
  covered by an allowance), `evidence <id> removed` and `evidence <id>
  weakened` (`by` changed to a role other than owner; coverable by an `allow`
  `checks` glob). The driver does everything else first and halts (needs the
  owner for approve, a human for evidence) with the exact command only when
  nothing else remains.
- **Observations are facts about keys (D24).** `owed attest` and the genesis
  attest no longer refuse with `Plan, candidate or trunk changed; retry` when
  the ledger moves while they measure: under the lock each observation is
  appended iff its item is still current, otherwise dropped as superseded and
  listed after the card (`Superseded (not recorded; the item changed while it
  was measured): …`; `AttestResult.superseded`). A plan edit elsewhere
  interrupts nothing. `merge` and `adopt` keep the strict rule.
- **`owed_init`, `owed attest --genesis` (D24).** The pi tool `owed_init
  {plan}` (owner) refuses an initialized ledger, records genesis for the trunk
  commit it resolved (pinned), and returns at once while the genesis attest
  runs in the background in-process (aborted on session shutdown); the session
  gets one `owed-init` message with the recorded, failed and missing ids when
  it ends. `owed attest --genesis` measures the genesis invariants still
  lacking an observation (exit 0 when none is missing, else 1); it takes its
  own `genesis` lock and never blocks node attests. `owed init` exits 0 once
  genesis is recorded; an incomplete genesis attest prints `Initialized
  (genesis #<seq>). Genesis attest incomplete: recorded <ids>; missing <ids>:
  run owed attest --genesis, or the next attest/merge measures them first.` —
  never "retry" (a signal still exits 130/143). While genesis items lack
  observations, `owed status`/`owed_status`/`/owed` show `Genesis: <k>/<n>
  invariants observed` with `(measuring in this session)` or the `owed attest
  --genesis` hint, and `owed plan` succeeds with `Warning: genesis attest
  pending for <ids>`.

**Compatibility.** Plans and ledgers without the new keys (`worktrees`,
`type`, `exec`, `allow`, `approve`, `evidence`) keep 0.4.1's canonical plan and
sha, keys and views; the behaviour changes regardless of keys are the ones
above: owner acts are delegated unless `OWED_CONFIRM=owner`, a delegated
downgrade needs a note, attest drops superseded observations instead of
refusing, and `init` succeeds with an incomplete genesis attest. pi-owed 0.4.x
reading a 0.5 ledger: verify and replay accept `channel: delegated` (0.4.1
never checked channel values) but show no `(delegated)` marker and no delegated
section; they ignore the plan entry's `note` and do not require notes on
delegated downgrades; they ignore `evidence` entries and the plan keys
`worktrees`, `type`, `exec`, `allow`, `approve` and `evidence` — 0.4.x would
dispatch to the default paths, run checks without the wrapper and env, and
drop the approval and evidence obligations, so do not use 0.4.x on a
repository whose plan uses them. 0.4.x refuses to replay (`Entry #N invalid`,
so `owed verify` fails) a ledger containing a parent plan update under an
allowance, a parent adoption, an owner `approve` review or a `ruling` send.
New errors and exit codes: plan errors for the new blocks and an invalid
expanded branch name at dispatch exit 2; the subagent refusal, a delegated
downgrade without a note, the dirty trunk worktree, `candidate changed since
confirmation`, uncovered parent downgrades or adoptions and the confirmation
timeout are refusals (exit 1 / code `refused`); `owed attest --genesis` exits 1
while items are missing; `--as owner:<id>` without a TTY is no longer refused
(only under `OWED_CONFIRM=owner`).

**Known limitations.** Deferred to 0.5.1: The background driver's report and
merge-halt wording still reads `(needs owner)` instead of the D25 wording.
Check processes inherit `DSA_*` from owed's environment (the executor does not
strip them), so an owner or parent owed command inside a check run under a dsa
call is refused. The CLI's default owner id stays `owner:human` (recorded as
`delegated`) while pi uses `owner:pi`. Delivered rulings are inferred from
ledger position, so a ruling recorded while a repair or launch message is being
built can count as delivered without having been carried (the reviewer's
`ack_rulings` obligation is unaffected); 0.5.1 records the ruling seqs each
message carried. Documented limitations: An ambiguous branch template (e.g.
`{node}{attempt}`) can expand to the same name for two attempts (dispatch then
fails in `git worktree add`), and parent directories created by a failed
dispatch stay. A remote wrapper is trusted by its argv: the key names the
wrapper, not where the command ran.

## 0.4.1

- **Signals end the checks a command started.** While `owed attest`, `merge`,
  `init` or `adopt` runs, the first SIGINT/SIGTERM/SIGHUP kills the running
  check's process group, records nothing for that run, starts no further job
  and exits 130 (SIGINT) or 143 (SIGTERM/SIGHUP) after printing
  `Aborted: <signal>`; observations of jobs completed before stay (merge/adopt:
  when the ledger did not move meanwhile), and an abort never moves trunk. So
  `hold machine -- owed attest` releases the lease only after the checks ended.
  130/143 means a real abort only: a signal arriving after the operation
  completed keeps its normal exit code (0/1) and says so on stderr. Signals
  within 1 s of the first are the same request; a later second signal exits at
  once (it may leave the job's temporary worktree; SPEC §10 says how to remove
  it). `owed drive --once` stops at once on the first SIGINT/SIGTERM (its
  attest children and in-process merge checks end too), exit 130. SIGKILL
  remains out of scope.
- The pi tools `owed_attest`, `owed_merge` and `owed_adopt` pass the tool
  call's abort signal (aborting ends the running check; tool error
  `Aborted: aborted`); `owed_drive` (action once) stops its pass after the
  current action.
- `Ledger.withLock` takes an abort signal: an abort while waiting for a lock
  rejects at once with `OwedError('aborted')` and takes nothing (new error
  code `aborted`).
- **Background driver:** `owed drive --detach [--max N]` starts the loop
  driver as a detached process with its output in
  `.git/owed/drive/log.jsonl` (previous log kept as `log.jsonl.1`; the last
  line is an exit record `idle|stopped|killed|error`, absent only after
  SIGKILL or a crash), `owed drive --status [--json]` reports it (running or
  not, last exit record, last 10 log lines), `owed drive --stop [--now]` stops
  it after its current action (`--now`: at once). The pi tool `owed_drive`
  gets `action: once|start|status|stop` (default `once`, unchanged) and `now`;
  after `start`, and in every top-level pi session opened in the repository
  while a driver runs, the session is woken with one message when the driver
  halts, needs the owner, a run asks a question, it is stalled, dsa events
  fail or it exits; the extension reads the driver's log every 2 s without a
  model turn, so no agent has to poll `status`. `/owed` shows whether a
  driver runs.
- **Review blocks that need a parent ruling:** `owed review <node> --block
  --needs-parent` (pi tool `owed_review` with `needs_parent: true`) records
  `needs: "parent"` on a review block (refused on `--ok`). The driver then halts
  (needs human) before any repair until a ruling naming the node
  (`owed rule --nodes <node> "<decision>"`, not `*`) follows the block; the
  next repair carries the ruling, and every repair message lists the rulings
  since dispatch. `owed why` and `owed status` show such current-candidate
  blocks as `(needs a parent ruling)`; the reviewer packet tells reviewers when
  to use the flag.

No new ledger entry kinds. Ledgers containing `needs: "parent"` reviews remain
readable by pi-owed 0.4.0 (it hashes the whole entry and ignores the unknown
field, so `owed verify` passes), but 0.4.0 treats such a block as an ordinary
review block: its driver sends a repair instead of halting, and `why`/`status`
show no hint. Use pi-owed 0.4.1 or later on such ledgers.

## 0.4.0

- **`owed drive [--once] [--max N] [--json]`** (also the pi tool `owed_drive`,
  one pass). Runs the mechanical loop — dispatch, writer, submit, attest,
  reviewers, merge — with pi-durable-subagents as the process runner, as
  principal `parent:drive`, and stops only for decisions: it never answers a
  question, waives, changes the plan or forces a dsa restart. Every intent is
  recorded before the dsa call and retried with the same id and bytes, so a
  kill at any point is safe. Attest runs under `hold machine --shared
  --no-wait`. One driver per repository (`.git/owed/drive.lock`). An optional
  `drive:` block in the plan sets `max`, `repairs` and the writer/reviewer
  agent and model. **Requires pi-durable-subagents >= 1.0.27** (only for
  `owed drive`; the rest of owed does not use it).
- New ledger kinds `launch`, `send` and `halt` (written by the driver).
  `owed status`, `why` and `report` show driver halts and launches; a halt is
  cleared by a later action on the node by anyone other than the driver. A
  halt from dsa rejecting a run or send names the recovery: that attempt's
  request is fixed, so fix the cause and `owed abandon <node>`.
  Ledgers containing these entries need pi-owed 0.4.0 or later.
- Red runs: `min_tests` applies to every non-red run (candidate checks and
  invariants), never to the red run, which needs only a recognizable failure
  (non-zero exit, `red_expect`, not a zero-test run); an unknown count format
  is accepted there. A red run whose command exits 126/127 (not executable /
  not found) is now an `error` observation, not a pass. A spawn failure remains
  an `error` observation.

This release was driven end to end by `owed drive`.

## 0.3.1

- The pi tool `owed_adopt` confirmation dialog lists up to 50 changed paths,
  one per line; beyond that it shows the first 50 and then the exact
  `git diff --no-renames --name-only <prior>..<commit>` command for the full
  list.
- `owed report` lists an adoption once, under trunk adoptions only (no longer
  also under owner actions).
- An adopt refusal names the observation that decides each failing invariant
  (for example `h1 (obs #3)`), also on a repeated adopt of the same commit,
  which measures nothing new and names the existing observation.
- Release commits are now made through an owed node, so trunk no longer moves
  outside owed and no adopt is needed for a release.

## 0.3.0

- **`owed adopt [--commit X] --note TEXT`** (owner only, also the `owed_adopt`
  pi tool). Records commits made outside owed (for example a release commit)
  as the new ledger trunk. Only fast-forwards are accepted; the trunk
  invariants are measured on the adopted commit and no new debt is allowed.
  The CLI prints a preview (range, commit count, changed paths) and pins the
  previewed commit before asking for confirmation.
- `owed status` reports trunk drift between the ledger and the branch,
  including a ledger trunk commit that no longer exists (`ledger-missing`).
- `owed report` lists trunk adoptions as owner decisions.

Ledgers containing `adopt` entries need pi-owed 0.3.0 or later.

## 0.2.0

pi-owed 0.2.0 was developed under owed itself: every change below went through
dispatch, measured checks, counterfactual (red) runs, independent review and a
guarded merge recorded in the project's own ledger.

- **English interface.** All CLI output, receipts, packets and pi tool text are
  in English.
- **`owed brief`**: a morning view of owner decisions, merges, blocks and
  progress since a ledger position.
- **Oracle strength.** A check can declare mutants; the `strength:<check>`
  obligation measures that the check kills them before a candidate is accepted.
- **Escapes and decoys.** `owed escape` records defects found after a merge;
  commit-reveal decoys measure whether review and merge guards catch planted
  faults. Merge results are attributed to the merging node (`merging: <node>`);
  debt already on the trunk before the merge never counts as caught.
- **`owed gc`** (parent/owner only) reclaims finished worktrees and branches and
  pins submitted commits under `refs/owed/keep/<node>/<attempt>/<seq>`.
- **`owed rebase`** moves an open slot to the current trunk in place; the
  candidate is invalidated, blocks stay, and the packet points at the last
  reviewed patch for `git range-diff`.
- **Overlap guard.** `dispatch` refuses a node whose writes overlap an open
  slot unless `--allow-overlap` is given; the overlap is recorded.
- **`plan --rev <commit>`** reads the plan from a commit and records it.
- **`abandon --note`** (`--reason` still accepted).
- **pi tools.** Every tool takes an optional `cwd`; new tools for brief, gc,
  abandon, verify, escape, decoy and rebase. SPEC §11 lists all of them.
- **Fixes.**
  - Dispatch and gc always use the main worktree root, so running owed from
    inside a slot worktree no longer nests worktrees (which gc could delete).
    Submodules and `--separate-git-dir` repositories work from their main
    worktree; from a linked worktree whose main worktree cannot be verified,
    owed refuses before any effect.
  - Owner confirmation dialogs show free text on one escaped line after the
    Repository and Identity lines (control, C1, separator and bidi characters
    are escaped), so a note cannot forge the dialog. The ledger keeps the exact
    text.

## 0.1.0

First release: plan, dispatch, submit, attest, review, merge, rule, waive,
defer, status, why and report over a hash-chained ledger, with a pi extension
and skill.
