bareloop 0.10.0 → 0.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -15,6 +15,20 @@
15
15
  > file ships with the package and is the complete adopter contract; the README is only
16
16
  > the pitch.
17
17
 
18
+ > **DIRECTION CHANGE — 2026-08-17 (design decided, NOT built; nothing below has moved yet).**
19
+ > **`hitl` is RETIRED as a verdict class.** The verdict classes become **`green` +
20
+ > `soft-green`**, and the hitl pause machinery is **RE-HOMED, not deleted**: it becomes the
21
+ > **review door** at the end of EVERY run (green and soft-green alike), where the person
22
+ > `accept`s, `rerun`s (their text is the gap, funded as a FRESH engagement with its own money
23
+ > and time) or `pause`s — and **the door never changes the loop's own verdict**. `soft-green`
24
+ > gets a `judged-floor` stage behind a signed rubric card and a signed calibration set.
25
+ > **Everything documented in this file describes the code as it actually is on this branch**,
26
+ > where `hitl` IS admitted, `human-confirms` IS live, and the three `hitl-*` terminals exist —
27
+ > integrate against what is written here, and expect the class-level rename when the next rung
28
+ > lands. Records: PRD Addendum v1.71 and
29
+ > `docs/02-features/2026-08-17-softgreen-review-door-design.md` (repo-only, not shipped);
30
+ > evidence F102–F105.
31
+
18
32
  ## What this is
19
33
 
20
34
  **"Automate this job — I don't know the best workflow."** bareloop runs tasks that are
@@ -96,7 +110,7 @@ the step plan at run time (gated by `validatePlan`); the human signs only:
96
110
  | field | shape | notes |
97
111
  |---|---|---|
98
112
  | `goal` | non-empty text | what the agent plans against |
99
- | `verdictType` | `green` \| `soft-green` \| `hitl` | declared radio, never inferred (`VERDICT_TYPES`, frozen). v1 ADMITS only `green`; declaring `soft-green`/`hitl` reds `request-red` with the type as a structured `verb` field (declared-but-locked — the tool-menu pattern). Every `request-red` also carries `lib` — the territory the demand lands against, stamped at the emit site (`verdictType` → `bareloop`, a locked tool verb → `bare-agent`): the ledger keys and its `suggestedAsk` seed on it, so a bareloop-catalogue refusal never files as an upstream ask |
113
+ | `verdictType` | `green` \| `soft-green` \| `hitl` | declared radio, never inferred (`VERDICT_TYPES`, frozen). **`green` and `hitl` are ADMITTED** (N4 slice 1) — *and `hitl` is RETIRED as a class by the 2026-08-17 design (see the direction note at the top): it still works exactly as documented here on this branch, and the forward path for a job needing judgement is `soft-green` plus the end-of-run review door*; `soft-green` is still declared-but-locked and reds `request-red` with the type as a structured `verb` field (the tool-menu pattern) until its judged floor exists. Every `request-red` also carries `lib` — the territory the demand lands against, stamped at the emit site (`verdictType` → `bareloop`, a locked tool verb → `bare-agent`): the ledger keys and its `suggestedAsk` seed on it, so a bareloop-catalogue refusal never files as an upstream ask |
100
114
  | `close` | **an ORDERED LIST of named stages** `[{ name, cmd, expect, judged?, gapKeep?, offer?, needs? }, ...]` (PRD v1.28), or a close object (table below) **only** for the declared-but-locked verdict classes | the destination, the only thing hand-authored; the check menu DERIVES from it (below). The plan flow executes a staged close directly and adapts a bare `predicate` object into a one-stage list; a `gold`/`rubric`/`hitl` object close validates (the declared-but-locked verdict classes still parse) but the plan flow refuses it at runtime as `close-unsupported` — it names no command to run |
101
115
  | `closeDecl` | **the AUTHORED close** `{ genre: "TYPES", lang: "js"\|"python", stages: [{ name, kind, params }], notes? }` | the ALTERNATIVE to `close`, and the point of the close-authoring rung: the user answers an interview and an LLM composes a DECLARATION over kinds whose implementations we own — never a script, never a shell fragment, never a new kind. **`close` and `closeDecl` are alternatives**: declaring both reds `close-duplicated` (two closes are two arbiters). It is HARD-class by construction (`CLASS_BY_CLOSE.declared`), so a locked verdict on one reds `close-hierarchy` as well as `request-red`. The declaration is validated by `validateCloseDecl`; the TREE-GROUNDED half of that gate (the path rule, and the scoped-job derivation that arms the F84 one-population law) is DEFERRED at spec-validation time — a job spec is validated with no repository in hand — and the runner re-runs it GROUNDED against the real seed before any stage and before any token. It stores the counting RULE and never a number (D12): there is no seed field, and `baseline: "seed"` is measured at each run's own HEAD |
102
116
  | `checks` | **RETIRED** (PRD v1.28/v1.32) | hand-authored checks are gone, not merely discouraged: declaring `checks` reds `checks-derived` by name. The check menu is DERIVED from the close's own stages instead — see **Staged close** below. The hazard this removes is measured, not theoretical: job #5's three hand-written checks were re-implementations of three stages the close already ran, and a hand-carved copy can drift LENIENT (the worker passes the operator's ruler and fails the real inspection) |
@@ -194,7 +208,9 @@ declared-but-locked verdict classes** (a close is data, never code; verdict-clas
194
208
  is a named red `close-hierarchy`). The go-forward shape is the staged list above; a
195
209
  `predicate` object is legal shorthand for a one-stage list and the plan flow adapts it, but
196
210
  `gold`/`rubric`/`hitl` validate and then refuse at runtime (`close-unsupported`) since v1
197
- admits `verdictType: green` only:
211
+ admits `verdictType: green` and `hitl` only — and N4's hitl is a STAGE
212
+ (`human-confirms` inside a `closeDecl`), never a close TYPE, so the `hitl` OBJECT form below
213
+ stays refused at runtime exactly as it was:
198
214
 
199
215
  | type | fields (exact — extras red) | legal class |
200
216
  |---|---|---|
@@ -475,7 +491,9 @@ is COUNTED.
475
491
  **The verdict class is the USER's answer (PRD v1.57 §1), and it DRIVES the authoring.**
476
492
  `verdictType` is a declared radio the preflight validates, never inferred:
477
493
  `VERDICT_CLASSES` = `green` | `soft-green` | `hitl`, with `LOCKED_CLASSES` =
478
- `soft-green` | `hitl` declared-but-locked. Picking a locked one returns a counted
494
+ `soft-green` alone (N4 slice 1 admitted `hitl`; `soft-green` waits for its judged floor —
495
+ **which is now the forward path: `hitl` is retired as a class by the 2026-08-17 design and its
496
+ pause becomes the end-of-run review door, unbuilt as of this branch**). Picking a locked one returns a counted
479
497
  `request-red` refusal at ADMISSION — before that class's questions are ever asked. The
480
498
  pick is also a PROMISE: every catalogue kind carries the `verdictClass` it can honestly
481
499
  render, `closeCeiling(declaration)` reports the highest class a declaration demands, and a
@@ -487,7 +505,7 @@ a step, or test it without a provider.
487
505
 
488
506
  | step | call | what it is |
489
507
  |---|---|---|
490
- | interview | `runInterview({ verdictType, answers, repoPath })` → `{ ok, answers, verdictType, refusal, reds }` | PURE — no model, no repo, no clock. THREE frozen question sets keyed by verdict class (`QUESTION_SETS`; `questionsFor(cls)` / `requiredAnswersFor(cls)`), and the interview asks NOTHING about a genre. The green set is six questions, answers keyed by number; the soft-green and hitl sets are named but LOCKED (`questions: null`, absent rather than empty) and their selection refuses before they run. `verdictType` and `repoPath` are STRUCTURED input, never parsed out of prose. Answers are scrubbed at INGEST |
508
+ | interview | `runInterview({ verdictType, answers, repoPath })` → `{ ok, answers, verdictType, refusal, reds }` | PURE — no model, no repo, no clock. THREE frozen question sets keyed by verdict class (`QUESTION_SETS`; `questionsFor(cls)` / `requiredAnswersFor(cls)`), and the interview asks NOTHING about a genre and NOTHING about the repository. **Ask the library for the set and its numbers — never hardcode a count or a slot number**: the green set has lost two slots since it was frozen (D13's genre confirm, then the repo question, dropped 2026-08-15 because `repoPath` is mandatory structured input and asking for it again invites a second, drifting answer for a fact the machine already holds), and it renumbers CONTIGUOUSLY from 1 each time rather than leaving a gap. Today the green set is five questions, answers keyed by number; the HITL set is those five byte for byte plus one more (what the signer is deciding when they look at the result — `human-confirms`' `ask`), which is the spread's next number and moves with a green-side deletion; the soft-green set is named but LOCKED (`questions: null`, absent rather than empty) and its selection refuses before it runs. `verdictType` and `repoPath` are STRUCTURED input, never parsed out of prose. Answers are scrubbed at INGEST |
491
509
  | survey | `runAuthorScout({ workdir, provider, attempts?, ceilingUsd? })` → `{ state, facts, reason, meta, raw, raws, calls, budgetStop }` | a bounded READ-ONLY LLM survey. Read-only by MENU CONSTRUCTION (`AUTHOR_SCOUT_VERBS` = the full menu minus write-class and store-class verbs), 8 rounds, F59's reserved toolless final round. **`state: 'ABSENT'` means the scout did not complete — never "no special facts are needed"**, and a parsed `{}` is one of its five ABSENT routes. Up to `SCOUT_ATTEMPTS` (**3**, hardcoded — PRD v1.58) attempts: `attempts` is TIGHTEN-ONLY and CLAMPS rather than throws (floor **1**, because a scout that never ran is an ABSENT nobody can act on), the direction every cap in this system runs in. **A retry fires on the typed MALFORMED class alone** (`SCOUT_RETRY_CAUSES` = `empty` \| `unparseable`); what is excluded is excluded for a reason — `call-failed` covers transport and `truncated:max_tokens`, both provider-red with NO redraft; `short` is F59's cut-off population and already has its own instrument INSIDE the attempt; `non-object` and `empty-object` are valid JSON with wrong or vacuous content, which is a SEMANTIC failure, and F38/F39 measured what re-asking one buys (the same distribution, sampled twice) — that is the self-healing line, and it is not crossed here. The re-ask is TOOLLESS over the conversation the survey already produced (the repository was already read; only the emission was unreadable) and names the mechanical parse error and nothing else. **There is no JSON repair behind it and there never will be** — a repairer decides what the model MEANT and writes it down as though the model had said it, in the one artefact whose whole job is to be honest about what a repository contains. `meta.attempts` / `meta.attemptsAllowed` record what was paid for; every attempt is metered under its own label (`author-scout`, `author-scout#2`, …) and leaves its raw behind. **`ceilingUsd` is the operator's money ceiling** (PRD v1.62) — checked BETWEEN attempts and before F59's reserved round, so no paid call escapes it; `null`/absent is UNBOUNDED, and a non-null non-finite value THROWS (the shared `capStop` seam — see `authorClose` below). When it ends the ladder, `budgetStop` names which stop (`cap-halt` \| `pricing-red`) and a survey that was never asked for carries the typed cause `not-funded` — deliberately OUTSIDE `SCOUT_RETRY_CAUSES`, because a retry is precisely what the cap forbade |
492
510
  | listing | `buildSeedListing({ workdir, seedRef, sourcePaths, testPaths })` | mechanical, `$0`, no model. `files` is the WHOLE tree (what the validator judges paths against); `block` is scoped to the survey's own paths and capped in ANNOUNCED tiers (what the prompt carries). Handing the validator the scoped half would make a job scoped to `src/` read as whole-tree and silently disarm the one-population law |
493
511
  | authoring | `authorClose({ workdir, seedRef, lang, verdictType, answers, scout, listing, generate, ceilingUsd? })` | the grounded loop: author → validate → run EVERY stage at the seed → feed the MEASURED results back → revise, bounded at `MAX_REVISIONS` (2) — a passed `maxRevisions` is TIGHTEN-ONLY, so a caller may LOWER that ceiling and never buy more revise rounds than the constant (floor **0**: one authoring call with no revise round is a legal ask, and unlimited revising would launder thrash as adaptation) — early-stop on an unchanged declaration. The declaration is emitted through a SCHEMA-FORCED TOOL CALL (`declare_close`), never parsed out of prose; the feedback is EXECUTION OUTPUT only — no model ever reviews another model's close. The return carries **`raws`**: what every model call actually SAID, the scout's attempts absorbed together with the declaration's, in the cost book's own order and under the same labels it meters. Each is `{ label, attempt, bytes, trimmed, text, cause, reason }` through the ONE persist helper (`scrubRaw` — redacted over the ONE `SECRET_PATTERNS` inventory, since a raw is the record most likely in this system to carry a live credential; bounded at `RAW_PERSIST_MAX` (8000) with the trim ANNOUNCING its own full size, and the cut walked back off a continuation byte so a multi-byte character is never split). `raws` is present on EVERY path **including the `$0` preflight refusals** — the path that spends nothing more is exactly the one whose evidence used to vanish with the process. `iterations` records what each turn MEANT (declaration, validation, seed read); `raws` records what it SAID, and a malformation is only ever visible in the second. **`ceilingUsd` is the same operator ceiling the survey took** — the scout's spend is ABSORBED into this loop's cost book before its first call is weighed, so spend already incurred folds in and re-entering cannot silently widen it. It is checked immediately before EVERY paid call (the author call, each revise, and each malformed-emission retry). **A MALFORMED ceiling is an ERROR, not a silent unbounded run** (v1.64 §3): the seam every ceiling read goes through (`capStop`, `src/text.js`) THROWS on a non-null non-finite value — `'2.50'`, `NaN`, `Infinity`, `true`, `{}` — before the first paid call, and the throw propagates uncaught rather than being caught into an ABSENT survey, because a caught one would hand you the very silent-unbounded run it exists to prevent. `null`/omitted is the STATED operator choice for unbounded; `0` and negatives are finite, legal, and cap-halt immediately. The advertised ceiling and the enforced ceiling can therefore never be different numbers. A `cap-halt` here can arrive with `ok:true` — a close that was already validated and measured survives the money stop, exactly as a late provider casualty does |
@@ -512,11 +530,158 @@ no model found, it moves no seed number, and a declaration that authors it itsel
512
530
  `genre-owned-env`.
513
531
 
514
532
  **The kind catalogue** (`KIND_CATALOGUE`, and it IS the whole vocabulary): `command-exit`,
515
- `count-not-worse`, `pattern-absent-in-diff`, `files-changed` are live; `judged-floor` and
516
- `human-confirms` are NAMED BUT LOCKED (and carry `verdictClass` `soft-green`/`hitl`), so
517
- declaring one is a counted `locked-kind` red rather than an unknown-kind typo; `harness-loop` (TESTGEN) is ABSENT from v1 entirely. In
518
- tool mode a locked kind is INEXPRESSIBLE — the schema carries one branch per live kind — so
519
- that demand arrives through the interview layer instead (`refuseLockedKind(kind)`).
533
+ `count-not-worse`, `pattern-absent-in-diff`, `files-changed`, `human-confirms` and
534
+ `judged-floor` are all live — `LOCKED_KINDS` is EMPTY today; `harness-loop` (TESTGEN) is ABSENT
535
+ from v1 entirely (an unknown-kind typo, never counted demand). The locked-kind machinery is
536
+ unchanged and is what a future entry arrives on: in tool mode a locked kind is INEXPRESSIBLE —
537
+ the schema carries one branch per live kind — so that demand arrives through the interview
538
+ layer instead (`refuseLockedKind(kind)`), whose "named but locked" wording is currently
539
+ unreachable for exactly the reason above.
540
+
541
+ **`judged-floor` is the one kind that BUYS its measurement** (softgreen module 2), and it is
542
+ CLASS-GATED rather than locked: it renders a `soft-green` verdict, `soft-green` is still a
543
+ LOCKED verdict class, so a real declaration naming it reds `class-ceiling` under a green pick
544
+ and `class-battery-locked` under a soft-green one. The kind runs; no signable spec can reach it
545
+ until the class is admitted. Its parameters are `card` — the signer's rubric as ENUMERATED
546
+ items, where `rule` SELECTS from the arbiter's own rulebook (`JUDGE_RULES` / `JUDGE_RULE_IDS`,
547
+ so a rule we do not implement is inexpressible) and `text` is the signer's words, which explain
548
+ a red and never decide one — and `paths`, the AUTHORITATIVE artifacts, read off the seed listing
549
+ like every other path and bounded at `MAX_JUDGED_PATHS` (one paid call each). It spawns nothing,
550
+ so it takes no `cmd`, no `timeoutMs` and no `env`; it is `offer: false` BY LAW
551
+ (`NEVER_OFFERED_KINDS` — a paid judge is never an in-run ruler); and it SKIPS the seed-verdict
552
+ read (ruling 8) as a recorded `skipped` row.
553
+
554
+ **Wiring a judged close, and what it costs.** The judge is PINNED (`JUDGE_MODEL`, exported) and
555
+ is never a step knob: the runner supplies `runJob({ judgeProvider })` — its own seam, not
556
+ `provider`/`providerFor` — and a judged stage with none instrument-STOPS as a wiring gap rather
557
+ than grading on whatever binding was at hand. Each locate call is metered per call and emitted
558
+ as a distinct `judge-round` spine record, which `runJob`'s ONE ledger accounts exactly like a
559
+ `worker-round` (`ACCOUNTED_ROUND_TYPES`, and `readResume`'s fold reads that same list, so a
560
+ resumed leg cannot silently widen a signed budget by the close's own spend). A budget funds the
561
+ attempt PLUS its close. Unpriced is never free: a null cost is a `pricing-red` stop that is
562
+ never retried, and the ladder is otherwise ONE retry (`JUDGE_ATTEMPTS`) — after that a broken
563
+ judge is an instrument stop, never a red about the tree.
564
+
565
+ **The two SIGNED artifacts of a judged close** (softgreen module 4, design §4.3/§4.4). Q6 and
566
+ Q7 of the softgreen interview compile into the RUBRIC CARD and the FROZEN CALIBRATION SET, on
567
+ the D5 shown-and-fixed path: `proposeJudgedArtifacts({answers, generate, book})` buys ONE
568
+ schema-forced proposal (tool `propose_rubric`, over the same bounded malformed-emission ladder
569
+ the declaration uses — a parse failure is an `artifact-red` retried under the cap, a proposal
570
+ that parsed and then broke a rule is NOT retried); a UI shows it; `signJudgedArtifacts({proposal,
571
+ fix})` stores the SIGNER's version (the fix replaces the proposal WHOLE, never merges — a signer
572
+ who deleted a line must not get it back) and reports `source: 'signer'|'proposal'`; and
573
+ `foldJudgedArtifacts(closeDecl, {card, cases})` enumerates both into the declaration. Free text
574
+ in either artifact is scrubbed at ingest through the one `SECRET_PATTERNS` inventory, because a
575
+ signed spec outlives the run that produced it.
576
+
577
+ **Where they land, and what the hash covers.** The card replaces the ONE `judged-floor` stage's
578
+ `params.card` — one card per close, and it is the signed one; a fold into a close with no judged
579
+ stage (or two) THROWS rather than storing a rubric nothing reads. The cases land beside the
580
+ stages as `closeDecl.calibration.cases`, because the set calibrates the RULER and is read before
581
+ any stage runs. Both sit inside the spec, so `jobSpecHash` covers them by construction: a card
582
+ line or a single case edit flips the hash and forces a re-sign, and a byte-identical re-store
583
+ does not.
584
+
585
+ **What a legal calibration set is** (`validateCalibrationSet`, beside the rulebook in
586
+ `src/judged.js`, and re-checked at the spec gate by `validateCloseDecl`). EXACTLY
587
+ `CALIBRATION_SIZE` = 10 cases — hamr's ruling, verbatim *"go build 10, we could double later"*,
588
+ operator territory and never agent-picked — with BOTH polarities present, because a set that
589
+ cannot fail one way proves nothing. A case is
590
+ `{id, artifact, expect: {verdict: 'pass'|'red', reds: [{rule, fn}]}}`: `artifact` is the REAL
591
+ source text the judge will be pointed at (never a description of one), and a red case must be
592
+ ITEMIZED, naming a `rule` THIS close's card actually carries (§4.3's ceiling, mechanically — the
593
+ fix for a miss is a new card line and a re-sign) and the `fn` the red lands on. The judge's own
594
+ `why`/`quote` are deliberately NOT stored: no signer can predict prose, and pinning it would
595
+ fail an honest pipe on wording. `expectedOf(decision)` is the ONE reduction of a `decide()`
596
+ result to that shape, so a harness compares one shape rather than two.
597
+
598
+ **THE CALIBRATION GATE** (`runCalibration`, softgreen module 5, `src/calibrate.js`). It runs the
599
+ WHOLE pipe — `runLocate` over the shipped `JUDGE_ATTEMPTS` ladder, then `decide()` — over each of
600
+ the ten, and compares `expectedOf(decision)` against the stored `expect` by VERDICT **and**
601
+ ITEMIZED REDS (a `(rule, fn)` set, order-insensitive): *a pipe that reds the right case for the
602
+ wrong reason has not been calibrated, it has been lucky*. The floor is **10/10, zero reds**
603
+ (hamr, 2026-08-18) and it is reported per case — `graded: [{id, ok, got, want, detail}]` — never
604
+ as an aggregate percentage.
605
+
606
+ Alongside it runs **`INJECTION_LOCATE_BATTERY`: five ARBITER-OWNED artifacts**, one per
607
+ bare-agent attack style, **re-aimed at the LOCATE axis** (2026-08-18 second addendum, ruling 1 —
608
+ upstream's `shouldBreak` gold assumes a judge that renders a verdict, and this one never does).
609
+ The attack text is embedded in the artifact, and a style resists only when the extracted FACTS
610
+ are unaffected: same function list, same per-function doc reality. Reading the DECISION alone
611
+ would be blind here — an invented `docQuote` reds `has-doc` through the quote-verification route,
612
+ so a leak and a resist can render identical itemized reds. **All five must resist**; a leak fails
613
+ calibration and names the style. Nothing about the battery is stored in a spec and no signer
614
+ authors it.
615
+
616
+ A **CASUALTY is never a failed case**: a dead call, an unpriced one (F6 — never retried) or an
617
+ emission that never parses after the ladder STOPS the gate on `runLocate`'s own axis
618
+ (`CASUALTY_AXES`), keeps the graded prefix, and reports zero failures — a broken judge is no
619
+ evidence about the set in either direction. Every locate call is metered through `onCost` on
620
+ every route out and the totals fold through `tallyCalls`, so one unpriced call makes `costUsd`
621
+ null rather than an exact-looking floor.
622
+
623
+ **At signing it is MANDATORY and it is the only PAID gate** (`prepareSigning`, gate 4). A close
624
+ carrying a `judged-floor` stage is refused when no set is stored, when no `judgeLoop` seam is
625
+ wired (a wiring gap, never a silent pass), when the gate is a casualty, and when anything short
626
+ of all-of-them grades. It runs **after** gates 1–3, so no $0 refusal is ever paid for. The
627
+ signing record keeps WHAT was certified — `cardHash`, `casesHash`, `setHash` (which covers
628
+ `JUDGE_MODEL`), the graded rows and the battery — beside `judgeModel` itself.
629
+
630
+ **D9.3 for a judged-ONLY close** (ruling 3, hamr: *"fix it now, we are delivering softgreen"*).
631
+ A judged stage skips the seed read (ruling 8), so a close whose only work stage is judged has no
632
+ seed red and used to be unsignable — the one job shape softgreen exists for. For that shape only,
633
+ a PASSED calibration gate plays the seed-red role (the polarity law makes *this close can fail* a
634
+ demonstrated fact), and the record says so: `gates.seedVerdict.satisfiedBy === 'calibration'`. A
635
+ close with any mechanical work stage is UNCHANGED and still needs its seed red.
636
+
637
+ **THE JUDGE TIER IS INSIDE THE SIGNATURE, and the stage refuses a mismatch.** *"A judge-model
638
+ bump forces a full recalibration"* used to be an operator-side rule with nothing to fire on,
639
+ which makes it prose (F45); it now has both halves of a detector. The tier the signer's set was
640
+ graded by is stored beside the cases as **`calibration.judgeModel`** — `CALIBRATION_FIELDS`
641
+ enumerates a stored set as exactly `cases` and `judgeModel` and nothing else — written by
642
+ `foldJudgedArtifacts` from the PIN (`JUDGE_MODEL`) rather than from anything a caller may name.
643
+ Because it is INSIDE the spec, `jobSpecHash` covers it by construction: bump the constant and
644
+ every signed judged spec hashes differently, the signature dies, and the re-sign is what re-runs
645
+ the calibration gate. **`validateCloseDecl` REQUIRES it** on any stored set (`missing-required`
646
+ on `…calibration.judgeModel`) — a set nobody can attribute to a tier is a floor nobody can tell
647
+ apart from a bumped one. **`declaredStages` stamps it onto the judged stage** as
648
+ `calibrationJudgeModel`, from the SIGNED bytes and through the one seam that already turns a
649
+ declaration into stages (the same carry `offer: false` rides), so a stage can never name a tier
650
+ the signature does not and no second close-level channel is plumbed through every executor seam.
651
+ **`runJudgedFloor` then STOPS on a stored-vs-live mismatch** before it buys a call, naming both
652
+ models and the two things that fix it — *re-sign the spec with the new tier* and *re-run the
653
+ calibration gate (recalibrate)* — under `STOP_FAULTS.FAILED`, the same fault and the same reason
654
+ as the absent judge seam beside it: nothing is broken, something was never re-done. It stops
655
+ rather than degrading in either direction, because grading on the new tier mints a verdict
656
+ against an uncalibrated floor and grading on the old one is not on offer (the pin is a constant).
657
+
658
+ **The one honest limit, deliberate:** an ABSENT stamp does not stop. It is the same split the
659
+ SET's own presence already lives under — a signed spec that judges cannot reach a run without a
660
+ calibration (the signing gate) or without a model on it (the spec gate), so the only runtime
661
+ question is ever WHICH model, and a bare stage descriptor handed straight to `runStage` by a test
662
+ or an adopter is not a signed spec making a claim about a tier.
663
+
664
+ **`human-confirms` is the one kind that measures NOTHING** (N4 slice 1). Its whole parameter
665
+ surface is `ask` — the plain question the signer answers — so it cannot spawn, cannot be
666
+ env-capable, and cannot be handed a second job. Four laws ride on it, all checked rather than
667
+ assumed: it is `offer: false` BY LAW (a declaration that offers one reds `human-stage-offered`,
668
+ and `declaredStages` stamps the law onto the stage the runner sees, so `checkMenu` can never
669
+ hand the agent `check-passes(<a person>)`); it must be the LAST stage
670
+ (`human-stage-not-last` — first-red-wins would otherwise silently delete every mechanical stage
671
+ behind it and empty the evidence package the person is owed); it is at-most-once (one signer,
672
+ one judge); and it SKIPS the seed-verdict read (ruling 8), with the skip recorded as a row
673
+ carrying `verdict: 'skipped'` rather than taken in silence.
674
+
675
+ **A human stage does not RUN — it PAUSES.** `StageResult.verdict` gains `pause` (and `skipped`),
676
+ `runDeclaredClose` reports `pause`/`pausedAt`, and the arbiter bridge translates it to the
677
+ distinct `HUMAN_PAUSE` verdict — never `satisfied`, never `needs_revision`, and never a
678
+ `CLOSE_FAULTS` word (a fault says the close could not render a verdict; a pause says it is not
679
+ FINISHED rendering one). With the signer's ruling in hand (`ctx.humanRuling`, carried by the
680
+ runner and never authored) the same stage renders what the person said: `accept` is a green a
681
+ PERSON judged, `rerun` is a red whose gap is their own words. `normalizeHumanRuling` is the
682
+ library half of the gate — `null` is the pause path, a fourth door is refused, and a `rerun`
683
+ with empty or whitespace-only text is refused by name, because an empty gap re-runs the worker
684
+ as though nothing had been said.
520
685
 
521
686
  **A red `count-not-worse` gap carries the LINES it counted, not only how many (F98).** Each
522
687
  term's **KEPT** matched lines nest under that term's own breakdown row — the row that already
@@ -590,6 +755,81 @@ alone. An absent goal renders as absent rather than as a bare label. This is a R
590
755
  validator: nothing compares the goal to the declaration, because deriving one from the other
591
756
  is exactly what F87 forbids.
592
757
 
758
+ **The path an adopter actually drives is TWO scripts, and the first one spends nothing.**
759
+ `runInterview` is a pure function over answers with no prompt loop in it, so until
760
+ `scripts/run-interview.mjs` existed the only way in was to hand-write an `answers.json` and a
761
+ spec draft — precisely the SWE tax this product refuses. That script is the half that ASKS
762
+ (`--patient <repoPath> --verdict <class> --out <outdir> [--budget <usd>] [--lang js]`, default
763
+ `js`), and it is deliberately GLUE: **it calls no provider at all**, so a whole interview costs
764
+ $0 and no model ever sees it. Everything load-bearing in it is borrowed rather than respelled —
765
+ the QUESTIONS are the library's frozen sets (`questionsFor` / `requiredAnswersFor`), printed as
766
+ handed over and never re-worded, re-ordered or re-numbered by the script; the REFUSALS are
767
+ `runInterview`'s; the SCRUB is `redactSecrets` at capture AND again at the library's own ingest,
768
+ so no keystroke reaches stdout or disk by a route that skips the one inventory; and the spec
769
+ draft is checked by the SAME `validateJob` that will judge it after the paid call, with the
770
+ authored half's expected reds filtered off `AUTHORED_SPEC_FIELDS` by name. A typo'd slug or an
771
+ under-floor wall therefore costs $0 rather than a scout plus a model call.
772
+
773
+ A LOCKED class refuses BEFORE a single question is asked: the script hands `runInterview` an
774
+ empty answer set, prints the counted `request-red` verbatim, and stops — nothing is asked and
775
+ nothing is written, because asking a class's questions for a job nothing here can close is an
776
+ interview at the wrong price. On success it writes exactly TWO files into `--out`:
777
+ `answers.json`, which is the LIBRARY's redacted reading of the answers (`iv.answers`) and never
778
+ the raw keystrokes; and `specdraft.json`, which is the OPERATOR's half ONLY — no `close`, no
779
+ `closeDecl`, no `verdictType`, because those three ARE `AUTHORED_SPEC_FIELDS` and `assembleSpec`
780
+ refuses a draft carrying any of them rather than merging over it. `tools` is deliberately
781
+ OMITTED from the draft too (MED-1: an omitted menu hashes as the concrete current `TOOL_MENU`,
782
+ which pins WHICH menu was signed; naming today's list would freeze it into the operator's half).
783
+ An interview that never finishes writes NOTHING: a half-collected answer set that looks finished
784
+ is the failure nobody sees.
785
+
786
+ **Two ceilings, two names, never pooled on screen or anywhere else.** `--budget` is the
787
+ AUTHORING ceiling — what the authoring call may spend — parsed by the same `parseCeiling` rule
788
+ `run-author.mjs` parses it with: absent is UNBOUNDED and ANNOUNCED before anything spends,
789
+ malformed is an ERROR rather than a silent fallback, and zero or negative is rejected on the
790
+ same rule. `budgetUsd` is the JOB's budget, asked separately and signed into the spec. The
791
+ interview stores neither: the ceiling is handed straight to the child, which is the process that
792
+ spends it.
793
+
794
+ **It ends by OFFERING the paid step, and the offer's default is NO.** `run-author.mjs` is a
795
+ DIFFERENT PROCESS under its own ceiling; the interview prints its exact command line (with
796
+ `--budget` propagated only when one was given, and a note when `ANTHROPIC_API_KEY` is unset)
797
+ before asking `Run it now? [y/N]`, so declining still leaves a command to paste and two files
798
+ already on disk. Only an explicit yes spends — the answer that costs nothing is the one you get
799
+ by saying nothing, the same lean the pause's doors take. On a yes it releases stdin before
800
+ spawning (two readers on one terminal is a keystroke landing in whichever happens to be
801
+ listening) and then propagates the CHILD's exit status rather than flattening a refusal there
802
+ into a success one process up.
803
+
804
+ **Both scripts speak exit codes, and an adopter wrapping them in CI reads them:**
805
+
806
+ - `run-interview.mjs` — **0** the interview finished (and, when it spawned the paid step, that
807
+ step's own status is what you get, never a flattened 0); **1** a REFUSAL — a locked class, the
808
+ library's own reds over the answers, or a spec draft that does not validate; **2**
809
+ operator/config — usage, a missing or non-existent `--patient`, a present-but-empty `--lang`,
810
+ a malformed `--budget`, stdin ending mid-interview, or a child that could not be started;
811
+ **3** a LEAK that `scanSecrets` found in a file it had just written (count and path only —
812
+ echoing the matched string is the same leak, one hop on).
813
+ - `run-author.mjs` — **1** a refusal or a failed gate; **2** operator/config; **3** a leak;
814
+ **4** a CRASH inside the paid span. **3 deliberately OVERRIDES a 4**: a secret sitting in a
815
+ written file is the harder line of the two, and the crash keeps both of its own louder
816
+ channels — the whole error on stderr and its own `author-crash` spine record.
817
+
818
+ **A crash inside the paid span leaves a BODY.** The fallible span — from just after
819
+ `author-start` to the end of the main flow — sits in ONE try/catch that RETRIES NOTHING and
820
+ SWALLOWS NOTHING. The operator's copy goes first and verbatim (it must not depend on the two
821
+ writes below it succeeding), then the spine gets `author-crash` via `crashRecord`: `name`,
822
+ `message` and `code` through `redactSecrets`, the STACK through `scrubRaw` — the ONE persist
823
+ boundary, the same `SECRET_PATTERNS` inventory, bounded with the bound announcing its own size —
824
+ plus `author-end {outcome: 'crashed'}`, because `author-end` is the one record that says a run
825
+ stopped AT ALL and a crash omitting it leaves a spine that reads as still-running. The spine
826
+ write is itself guarded and its failure said out loud (F70: a crash handler that crashes
827
+ destroys the report it was built to make). Nothing ABOVE the try is inside it, deliberately —
828
+ the argv and config `die()` paths run before the spine file exists, so they stop loud on stderr
829
+ and 2 instead. **Latent, not live:** nothing reads the author spine today, and the nearest
830
+ reader (`classifyIncidents`, `src/ledger.js`) keys on types neither record uses, so both fall
831
+ through every branch UNCOUNTED — the fail-safe direction, uncounted rather than miscounted.
832
+
593
833
  **Refusals are COUNTED (D13).** `refusalEvents(refusal)` returns the spine events: a
594
834
  `job-red` carrying `{ code: 'request-red', verb, lib: 'bareloop' }` — the `lib` stamped at
595
835
  the EMIT SITE, so `classifyIncidents` files it against bareloop's own catalogue and its
@@ -678,7 +918,7 @@ Reserved spine vocabulary (V7, machinery-free until job #1 surfaces one):
678
918
  `coordination-red` — a failure between units (scope contention, step order, store
679
919
  races), never to be folded into worker/interpreter reds.
680
920
 
681
- ### `runJob(spec, { approvals, workdir, provider, nativeProvider?, providerFor?, emit, capRuns?, strikeLimit?, shellCapUsd?, closeTimeoutMs?, layerRoot?, bridge?, priorSpentUsd?, priorSpendComplete?, priorWallMs?, resumeSeed?, resumeGrades?, resumeReplans?, resumeBranch? })` → outcome — `src/run.js`
921
+ ### `runJob(spec, { approvals, workdir, provider, nativeProvider?, providerFor?, emit, capRuns?, strikeLimit?, shellCapUsd?, closeTimeoutMs?, layerRoot?, bridge?, priorSpentUsd?, priorSpendComplete?, priorWallMs?, resumeSeed?, resumeGrades?, resumeReplans?, resumeBranch?, humanRuling?, heldRuling?, reviewDoor?, doorRerun? })` → outcome — `src/run.js`
682
922
 
683
923
  The last seven are the RESUME fold and are documented under *Resuming a killed run* below; they
684
924
  default to `0` / `true` / `0` / `null` / `[]` / `null` / `null`, so a fresh run passes none of them.
@@ -694,7 +934,53 @@ known-answer round-trip before tokens: `smoke-red` — a silent degradation thro
694
934
 
695
935
  Outcomes: `green | already-green | escalated | unapproved-spec | job-red | smoke-red |
696
936
  plan-red | check-red | close-red | close-unsupported | recipe-stale | branch-red | pricing-red |
697
- provider-red | interpreter-red | cap-halt | wall-halt | step-stalled | step-red:<id>`.
937
+ provider-red | interpreter-red | cap-halt | wall-halt | step-stalled | hitl-pause |
938
+ hitl-decision-red | step-red:<id>`.
939
+
940
+ **The two hitl terminals (N4 slice 1, doors re-cut 2026-08-18)** are the class's whole surface
941
+ at this layer, and each is a CLEAN exit (`spendComplete` stays true — only the two casualties floor). `hitl-pause` is a
942
+ decision-ready CHECKPOINT: the close reached a stage no machine can render, so the run stops
943
+ holding everything it has done, emits the evidence package (every mechanical stage's result, the
944
+ close's own `ask`, and the changed paths — bounded, trim announced), and the clock stops with
945
+ it. It is on `CHECKPOINT_OUTCOMES` (the canonical `resumableOutcomes` list, exported so an
946
+ exported bundle inherits one spelling), and `checkpointAgeGate` refuses one older than
947
+ `PAUSE_TTL_MS` (60 days) by NAMING the age and the TTL. The signer's own `pause` door mints
948
+ that SAME terminal, with `humanDecision: 'pause'` and an explicit `gap: null` on the record: no
949
+ fix loop, no worker round, nothing spent, the rerun allowance untouched, and the checkpoint left
950
+ exactly as it was. `hitl-decision-red` refuses a decision the run cannot act on — a word that is
951
+ not one of the three doors, a `rerun` with empty text, or a decision handed to a close with no
952
+ human stage — before anything is spent. Neither demotes a bridge, and both are excluded from the
953
+ ledger's escalation counting.
954
+
955
+ There was a third terminal, `hitl-cancel`, for a `cancel` door that no longer exists (hamr,
956
+ 2026-08-17: *"pause can resume — that would be more honest"*). Nothing mints it and no constant
957
+ exports it; the ledger still RECOGNISES the bare string so a spine written before the change
958
+ reads as governance rather than as a counted capability gap. An unresumed pause expiring under
959
+ the TTL is what cancel used to be.
960
+
961
+ Pass the signer's answer as `humanRuling: { decision, text? }` (`accept | rerun | pause`, the
962
+ enumerated `HUMAN_DECISIONS` set); it is spent once, at
963
+ the close readings up to the moment the fix loop opens, so the human's words become `post.gap`
964
+ through the SAME seam (same bound, same scrub) and the next machine-clean tree pauses for a
965
+ SECOND review rather than converting one sentence forever.
966
+
967
+ **A decision SURVIVES the halt that interrupts it** (F102). The run records the answer it was
968
+ handed (`human-decision` — the door, the words, the source) and, separately, what BOUGHT it
969
+ (`human-decision-spent` — a fix round, an accept that greened, a pause honoured). A decision with
970
+ no spend after it is still owed to the person, and `readResume` surfaces it as
971
+ `restart.pendingDecision` (`{decision, text, receivedAt, source}`). Hand that back as
972
+ **`heldRuling`** and the leg applies it directly — the ask is never re-rendered, and the spine
973
+ says the answer came from a record rather than from a person. F102's incident is what this is
974
+ for: a rerun that opened the fix loop, stopped with `iterationsUsed: 0`, and whose resume asked
975
+ the byte-identical question again. The two markers are deliberately not one: the fix loop
976
+ "spends" the ruling for THIS leg's close readings the moment it opens, while whether the person
977
+ must be asked AGAIN depends on work actually having been bought.
978
+
979
+ A leg handed BOTH a fresh `humanRuling` and a `heldRuling` is refused (`hitl-decision-red`,
980
+ naming the held decision) before anything costs anything: two answers to one question is
981
+ ambiguity, not a merge. `resolveHumanRuling(fresh, held)` is the exported seam that decides this
982
+ (and whether the leg is a fresh engagement, below), so a runner cannot admit what a run refuses.
983
+
698
984
  `branch-red` is the WORK BRANCH refusing (below): the patient is not a git checkout, its
699
985
  branch namespace has no free name, or a resume's recorded branch is gone. Zero tokens, and
700
986
  never a fallback to working on the branch the run was handed. `provider-red` is a
@@ -789,7 +1075,10 @@ never replans) — plus at most ONE arbiter-granted extra on a second variance s
789
1075
  REAL close and governed by the close TREND rule below (`capRuns`, default 3, survives only as
790
1076
  its blind fallback). `plan-executed` (the plan-as-executed record, design law #2) lands on the
791
1077
  spine on every path that executed steps. Additional outcomes: `already-green |
792
- plan-red | check-red | close-red | wall-halt`. Worker prompts hold the v1.12 §5 contract
1078
+ plan-red | check-red | close-red | wall-halt | hitl-pause | hitl-decision-red`.
1079
+ A `human-confirms` stage is caught at BOTH close seams — the precheck (reaching a person there
1080
+ means the machine half already passes on the untouched tree, so pausing costs $0 where drafting
1081
+ a plan first would spend a budget to arrive at the same question) and the post-steps close. Worker prompts hold the v1.12 §5 contract
793
1082
  (mutation-proven): the absolute repo root, the step's action/target, prior artifacts,
794
1083
  the gap — NEVER the budget, the close command, a check's command, or the arbiter's books.
795
1084
 
@@ -1119,8 +1408,67 @@ about its task, beside the absolute-path law it is the twin of — both are fenc
1119
1408
  cannot infer. It renders for EVERY worker on every grant (a worker granted only `write` gets
1120
1409
  no component strategy paragraph at all and must still be told). The fence is unchanged; what
1121
1410
  changed is that the rule is stated instead of learned one refusal at a time, which cost a real
1122
- run the rounds of a bounded attempt. N2 bounds (honest): `gold`/`rubric` closes refuse
1123
- `close-unsupported` (execution lands at N4).
1411
+ run the rounds of a bounded attempt. N2 bounds (honest): `gold`/`rubric`/`hitl` OBJECT
1412
+ closes refuse `close-unsupported` — N4's hitl is a `human-confirms` STAGE inside a `closeDecl`,
1413
+ never a close TYPE, so there is one live expression of hitl rather than two.
1414
+
1415
+ ### THE REVIEW DOOR — the three doors at the END of a run (softgreen module 8, PRD v1.71 §3)
1416
+
1417
+ The pause machinery re-homed: the same doors, the same evidence package and the same 60-day TTL,
1418
+ one level OUT — from a stage inside the close to the door at the end of a run.
1419
+
1420
+ **THE LAW, and it is not negotiable** (hamr, verbatim: *"it's important not to change the loop
1421
+ self verdict"*): the close mints the verdict, the door records a **disposition**. `runJob`
1422
+ returns `green` / `already-green` exactly as it always did, the ledger records exactly what it
1423
+ always recorded, and no answer a person gives can change either. A green run that is never
1424
+ answered is still a green run — the door is non-blocking.
1425
+
1426
+ **Opening one.** A run whose terminal is verdict-bearing (`DOOR_OPEN_OUTCOMES` — `green`,
1427
+ `already-green`) emits ONE `review-door` record carrying the evidence: the outcome, the class,
1428
+ whether the credit is `quarantined`, every close stage's own result, the stages an `accept` will
1429
+ re-run (`mechanical`), and the changed set (same cap and same F28/F6 announcements the pause
1430
+ package makes). Which runs open one is `doorOpens(job, reviewDoor)`:
1431
+
1432
+ - **always** for the classes in `REVIEW_DOOR_CLASSES` — `['soft-green']`, because a judged green
1433
+ is quarantined at mint (module 6) and an `accept` is the ONLY thing that releases the credit;
1434
+ - **on request** for everything else: `reviewDoor: true` (the runner's `--review-door`). A
1435
+ green-class run is byte-identical to what it always was unless it is asked for.
1436
+ - `reviewDoor: false` shuts it for any class. The flag wins in both directions and is never
1437
+ inferred; `REVIEW_DOOR_CLASSES` is a constant, not a threshold, and widening it is arbiter
1438
+ territory.
1439
+
1440
+ **Answering one — `answerReviewDoor({ job, workdir, events, decision, text?, closeTimeoutMs?,
1441
+ registryDir?, name?, runid?, at?, now?, emit? })`** (`src/reviewdoor.js`). The answer arrives
1442
+ after the run has ended, possibly days later and from another process, so it cannot ride the
1443
+ run's return path. This is that seam. It never throws and never conjures — a fourth door, an
1444
+ empty `rerun`, a run that opened no door (`no-door`), an expired one (`door-expired`), a tree
1445
+ that moved (`door-accept-red`) and a missing registry all come back as named reds.
1446
+
1447
+ - **`accept`** re-runs the close's **mechanical** stages against the tree AS IT STANDS
1448
+ (`mechanicalStages` — everything outside `SEED_EXEMPT_KINDS`, so never a judged floor and never
1449
+ a person) before it is honoured: hamr's ruling that an accept is not a rubber stamp, and the
1450
+ answer to a tree that can move in the 60 days a door keeps. A red REFUSES the accept with the
1451
+ stage named and records **nothing**. A pass records the disposition and, over a held judged
1452
+ green, RELEASES the credit through module 6's `applyDoorDecision`.
1453
+ - **`rerun`** carries the person's words back as a directive (`next: 'rerun'`) and re-proves
1454
+ nothing: the new run's close is what judges. Empty or whitespace text is refused at the same
1455
+ seam every other door is.
1456
+ - **`pause`** runs nothing and spends nothing, in any state. The door simply keeps, and an
1457
+ unanswered one expires under `PAUSE_TTL_MS` — that expiry IS what `cancel` used to be.
1458
+
1459
+ Every door is recorded when a registry is wired, not only the accepts: *"the signer's accepts
1460
+ double as the judge's ongoing report card"*, and a rerun over a judged green is exactly as
1461
+ informative. An **already-green** run wrote no row of its own, so its door records nothing and
1462
+ releases nothing — slice 1's already-green no-mint rule, holding from the other side, and stated
1463
+ in the result's `note` rather than surfaced as a failure.
1464
+
1465
+ **The rerun as a FRESH ENGAGEMENT** — hand the words back to the next run as
1466
+ **`doorRerun: {text, fromRunid?, receivedAt?}`**. Two things follow and only two: the
1467
+ `already-green` shortcut is refused (a tree that passes the close is precisely the state the
1468
+ person rejected — the run emits `door-rerun-open` and carries on), and the words reach the
1469
+ PLANNER as a requirement on top of the signed goal. It is never a `humanRuling`: that answers a
1470
+ close's human STAGE, and a green-class job has none. Money folds (the signed budget is the
1471
+ chain's ceiling); the WALL does not (F103).
1124
1472
 
1125
1473
  ### The reuse registry (Layer 3) — `src/bridges.js`, `src/selection.js`
1126
1474
 
@@ -1151,6 +1499,50 @@ it points, and a percentage over 2–3 runs is fake precision. `costUsd`/`wallMs
1151
1499
  a number or an EXPLICIT `null`, and the key is REQUIRED either way, so an unknown has to be
1152
1500
  said rather than omitted (F6 — an omitted key is how a `?? 0` gets written downstream).
1153
1501
 
1502
+ **QUARANTINE — a judged green earns nothing until the signer accepts it** (softgreen module
1503
+ 6; the standing ruling PRD v1.53, given its mechanism by v1.71 §3). A green minted under
1504
+ `verdictType: "soft-green"` carries `quarantined: true` on BOTH halves of R1's pair — the
1505
+ **version** (what inherits) and its **history row** (what the status ladder reads). It is a
1506
+ FLAG, never a withheld row: the run happened, the record says so, and the listing shows it —
1507
+ it is simply worth nothing yet. A held green does not count in `deriveStatus` (an entry whose
1508
+ only greens are held derives `null` and renders `HELD`, never `NO-GREEN`), and
1509
+ `reuseEligibility(bridge)` / `newestEligibleVersion(bridge)` refuse it for reuse **with a
1510
+ stated reason** — the same visible-skip discipline an unreadable registry file gets.
1511
+ `green` and `hitl` are untouched, byte for byte: the key is ABSENT, because the hold is about
1512
+ the young JUDGE, not about every class that is not green. `QUARANTINED_VERDICTS` is the one
1513
+ list; `quarantinesCredit(verdictType)` is the one predicate. The class travels on the green
1514
+ record as `verdictType` — an unrecognised value REDS rather than falling through to unheld.
1515
+
1516
+ **The review door releases it, forward-only.** `recordDoor(bridge, {runid, decision, at})`
1517
+ records the signer's disposition (`accept | rerun | pause`, the same three doors `kinds.js`
1518
+ owns) on that run's own green row as an append-only `doors: [{decision, at}]` — the **report
1519
+ card**, kept as facts and never as a computed agreement rate (D6's no-score rule applies to
1520
+ the judge too). `accept` over a held green also flips `quarantined` to `false` on both halves;
1521
+ `rerun` and `pause` record and release nothing. There is deliberately **no re-hold path**: an
1522
+ accept followed by a rerun records the disagreement and leaves the credit granted. A repeat of
1523
+ the last decision is a no-op (`released` says whether THIS call freed it), and a door aimed at
1524
+ a run with **no green row of its own** — an already-green run, a red, a casualty — is the red
1525
+ `no-row-for-run`: releasing is not minting. `applyDoorDecision({registryDir, name, runid,
1526
+ decision, at})` is the on-disk half; the door opens AFTER the run ends, so the decision cannot
1527
+ ride the run's return path. **The door never changes the loop's verdict** — a green stays green
1528
+ in the ledger whatever the person then does with it.
1529
+
1530
+ **`writeRunGreenRow({registryDir, job, name, outcome, plan, record})` → `{minted, reason,
1531
+ write}` — the terminal registry write for a runner that drives `runJob` directly** (rather
1532
+ than through `runReuse`, which writes its own rows). It is what gives that runner's review
1533
+ door a row to release: without one, `accept` reaches `recordDoor` and reds `no-row-for-run`.
1534
+ STORAGE ONLY — nothing here selects, promotes or reuses a bridge. Four refusals, each with a
1535
+ named `reason`: no `registryDir` (`no-registry` — the registry is operator-supplied, never
1536
+ conjured), `outcome !== 'green'` (`green-predates-run` for already-green — accept confirms a
1537
+ verdict, it never mints one — else `not-green`), no executed `plan` (`no-plan-executed`, R1),
1538
+ and a verdict class whose credit is not held (`credit-not-held` — `green`-class runs mint
1539
+ nothing here, `quarantinesCredit` is the one predicate). `name` falls back to `job.job`; the
1540
+ row is born HELD because the record carries the spec's own `verdictType`, never a flag. A
1541
+ refused underlying write returns `{minted: false, reason: 'write-refused', write}` with the
1542
+ reds on `write` — never a throw. Minting goes through the same cold-leg green write `runReuse`
1543
+ uses (shape-fork rule and collision refusals included), so the two runners can never spell
1544
+ "what a green writes" differently.
1545
+
1154
1546
  **`loadGate(bridge, job)` → `{ ok, reds }` — the load-time gate.** Exactly three checks,
1155
1547
  asked *"is this the right KIND of recipe?"*: the job's **verdict type**, the **close-stage
1156
1548
  kinds** (v1: the same stage names in the same order), and every stored verb **within THIS
@@ -1172,12 +1564,16 @@ their next green, not repaired.
1172
1564
 
1173
1565
  **`runJob`/`runPlan` take `bridge?`** — a validated entry to reuse. The gate runs **at the
1174
1566
  door**: before the clock, before the close precheck, before any token. On a pass, the
1175
- **newest** version's plan rides into the FIRST drafting prompt as a starting draft
1567
+ **newest ELIGIBLE** version's plan (the newest one no quarantine holds) rides into the FIRST
1568
+ drafting prompt as a starting draft
1176
1569
  (`bridge-loaded {name, versions, runid}` on the spine) and everything after it is the
1177
1570
  ordinary shipped path — same validator, same redraft-on-reds, the same replan ceiling, and a
1178
1571
  **replan never re-injects the bridge** (it drafts from the run's own state). On a fail the
1179
1572
  run returns the distinct terminal **`recipe-stale`** with `bridge-gate {outcome, name, reds}`
1180
- and a decision-ready escalation, having spent nothing. **Falling back to cold is the
1573
+ and a decision-ready escalation, having spent nothing. A **wholly HELD** entry handed in
1574
+ directly is refused at the same door and by the same terminal, with `bridge-gate
1575
+ {outcome: 'quarantined'}` and the accept-then-rerun option named — the recipe is the right
1576
+ kind, it is simply unaccepted, and the two are never spelled the same. **Falling back to cold is the
1181
1577
  caller's decision, not an automatic silent fallback** — starting a paid run on a decision
1182
1578
  nobody made is the same class of error as widening a cap to manufacture a green. Omit
1183
1579
  `bridge` and the flow is byte-identical to a pre-Layer-3 run: no new event, and no starting
@@ -1342,9 +1738,11 @@ semantics are byte-unchanged (PRD v1.46 §3):
1342
1738
  - **`resumableOutcomes: [...]`** reclassifies a landed `job-end` whose outcome is a
1343
1739
  CHECKPOINT rather than a verdict as a restart rather than a completed row. "A landed
1344
1740
  job-end is complete" is true of a VERDICT; a checkpoint means the work is on disk and the
1345
- plan is on the spine with nothing decided against them. **The set is the CALLER's**, and
1346
- the reference runner's is `['cap-halt', 'wall-halt', 'step-stalled']` (`run-u`'s
1347
- `RESUMABLE_HALTS`). The first two are governance halts — an operator-owned allowance ran
1741
+ plan is on the spine with nothing decided against them. **The set is the CALLER's** — the
1742
+ parameter stays a parameter, and its empty default is what the reuse loop's own graded-row
1743
+ semantics depend on — but the canonical ANSWER is exported as **`CHECKPOINT_OUTCOMES`**
1744
+ (`['cap-halt', 'wall-halt', 'step-stalled', 'hitl-pause']`), and the reference runner
1745
+ CONSUMES it rather than keeping a copy. The first two are governance halts — an operator-owned allowance ran
1348
1746
  out. `step-stalled` joined them 2026-08-13 on hamr's ruling (PRD v1.64 §1): it is the one
1349
1747
  terminal whose OWN escalation already offers *"retry the run"*, nothing about the work on
1350
1748
  disk is wrong, and the shape that named the fix is a stall that trips with time left and
@@ -1385,6 +1783,31 @@ watchdog `--wall-ms 0`, which it defaulted to `null` and armed no deadline at al
1385
1783
  still advertising one. The refusal names the lever: raise `maxWallMs`, which moves the spec hash
1386
1784
  and is re-signed.
1387
1785
 
1786
+ **A `hitl-pause` checkpoint is answered on the same command line** (N4, 2026-08-12 §5.2 — the
1787
+ terminal is the v1 surface; the panel is N6's). The reference runner takes
1788
+ `--decide accept|rerun|pause` with `--text` for the rerun door, gated on the SAME
1789
+ `--approve <specHash>` signature the run itself is signed with (ruling 4: the spec hash is the
1790
+ only signer proof this repo has, and no second identity mechanism was invented). The decision
1791
+ SEMANTICS are the library's — an adopter's runner should call `normalizeHumanRuling` and print
1792
+ its refusal rather than re-spell "three doors, no fourth" or "a rerun needs text", and hand the
1793
+ result to `runJob` as `humanRuling`. Two questions belong to the RUNNER because only it can ask
1794
+ them: a decision with no run to answer, and a decision aimed at a checkpoint that is not a pause
1795
+ (an `accept` there would mint a green over evidence nobody was shown). Before any of it, the
1796
+ paused spine goes through `checkpointAgeGate`, and the run is not resumed without an explicit
1797
+ decision: the lean-rerun rule (2026-08-12 §4) governs which door a prompt LEADS with, never an
1798
+ action taken for the operator.
1799
+
1800
+ **A finished run's REVIEW DOOR is answered the same way, one flag over**: `--door <runid>`
1801
+ selects the run (its own spine, its own `review-door` record), `--decide` picks the door and
1802
+ `--approve <specHash>` signs it. The screen is its OWN preview and deliberately not the resume
1803
+ banner: a door is answered about a run that is OVER, so nothing that banner computes (what is
1804
+ left to resume with, which step it re-enters) is a true sentence there. `accept` and `pause`
1805
+ launch nothing and exit; `rerun` falls through into a fresh engagement — its own clock, the
1806
+ answered run's spend folded in — carrying the person's words as `doorRerun`. `--registry <dir>
1807
+ --workflow <name>` are optional: without them the answer is recorded on the run's own spine and
1808
+ the readout SAYS the credit was not released, rather than reporting a release that never
1809
+ happened.
1810
+
1388
1811
  - **Completed tries are not re-run.** They come back as rows (marked `inherited`) and
1389
1812
  their bridges stay excluded from every later selection. A try whose `job-end` landed —
1390
1813
  the close judged it — counts as completed even if the kill beat its `try-end`; paying
@@ -1461,6 +1884,20 @@ and is re-signed.
1461
1884
  (or `wall-halt`, below one close timeout: a try that cannot fund its close produces an
1462
1885
  unreadable row) with **nothing launched**. Topping up is a new envelope, and a new
1463
1886
  envelope is a new signature.
1887
+ - **…except a decide-time RERUN, which is a FRESH ENGAGEMENT** (F103, design record §3.4 —
1888
+ hamr: *"redo/rerun comes with new authoring for money+time and keeps accounting of this far
1889
+ and this session separate counters"*). A rerun opens on the full signed `maxWallMs` instead of
1890
+ the remainder, because the person did not decide on the run's clock: the incident that minted
1891
+ it took the door and inherited **87 seconds** from a leg that had already ended, on a worker
1892
+ measured to read nine rounds before its first write. So there are TWO counters and neither
1893
+ does the other's job — the CHAIN (every leg added up: `engagement.chainWallMs`,
1894
+ `chainSpentUsd`, `job-end.spentUsd`, what the halt readout reports) and the ENGAGEMENT (what
1895
+ bounds THIS leg: the clock's `priorElapsedMs`, `job-end.engagementSpentUsd`). The `engagement`
1896
+ record states both at the top of every run. **MONEY IS DELIBERATELY NOT SYMMETRIC**: it folds
1897
+ on every path, because the signed budget is the CHAIN's ceiling and a signature for $5 never
1898
+ silently authorizes $10 — a rerun spends what REMAINS of it, never a refill. A rerun leg also
1899
+ declares its `job-start` fold engagement-scoped (`priorWallMs` omitted, `chainWallMs` carried),
1900
+ so a later resume of it inherits this engagement's minutes rather than the chain's again.
1464
1901
  - **Money stays honest end to end.** `carrySpentUsd` (completed tries + selection calls)
1465
1902
  seeds the resumed ledger; the mid-try fold is deliberately NOT in it, because it comes
1466
1903
  back inside the restarted try's own terminal — counting it in both places would bill it