bareloop 0.8.0 → 0.10.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.
- package/CHANGELOG.md +625 -0
- package/README.md +101 -110
- package/bareloop.context.md +333 -25
- package/package.json +1 -1
- package/src/authorflow.js +1447 -0
- package/src/authoring.js +1486 -0
- package/src/authorjob.js +801 -0
- package/src/authorscout.js +767 -0
- package/src/bridges.js +16 -3
- package/src/declaredclose.js +519 -0
- package/src/index.js +75 -0
- package/src/job.js +48 -4
- package/src/kinds.js +1445 -0
- package/src/ledger.js +18 -1
- package/src/plan.js +26 -1
- package/src/planrun.js +448 -40
- package/src/ralph.js +24 -4
- package/src/reuse.js +120 -9
- package/src/root.js +8 -1
- package/src/run.js +12 -4
- package/src/text.js +199 -0
- package/src/tools.js +11 -0
- package/src/validate.js +168 -11
- package/src/workbranch.js +261 -0
- package/types/authorflow.d.ts +764 -0
- package/types/authoring.d.ts +559 -0
- package/types/authorjob.d.ts +242 -0
- package/types/authorscout.d.ts +320 -0
- package/types/declaredclose.d.ts +174 -0
- package/types/index.d.ts +9 -1
- package/types/job.d.ts +1 -0
- package/types/kinds.d.ts +395 -0
- package/types/plan.d.ts +18 -0
- package/types/planrun.d.ts +53 -3
- package/types/ralph.d.ts +1 -0
- package/types/reuse.d.ts +1 -1
- package/types/run.d.ts +10 -2
- package/types/text.d.ts +151 -0
- package/types/validate.d.ts +74 -9
- package/types/workbranch.d.ts +102 -0
package/bareloop.context.md
CHANGED
|
@@ -98,6 +98,7 @@ the step plan at run time (gated by `validatePlan`); the human signs only:
|
|
|
98
98
|
| `goal` | non-empty text | what the agent plans against |
|
|
99
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 |
|
|
100
100
|
| `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
|
+
| `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 |
|
|
101
102
|
| `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) |
|
|
102
103
|
| `tools` | optional unique subset of `TOOL_MENU` (14 verbs, below) | the CEILING every plan step's grant must fit inside (omitting it means the full menu — and the hash is taken over that RESOLVED form, so a `TOOL_MENU` widening flips an omitted-`tools` spec's hash and forces a re-sign; see `jobSpecHash`, MED-1); `run` is `LOCKED_TOOLS` and reds `request-red` — locked-but-listed, and the red IS the admission evidence the ledger tallies (a typo stays `invalid-value`). A ceiling of write-class and store-class verbs ONLY reds `invalid-value`: the scout surveys read-only, so it would be handed an empty menu and survey blind |
|
|
103
104
|
|
|
@@ -293,6 +294,14 @@ N−1 are enforced only at the close, at the end of the run. That is a COST shap
|
|
|
293
294
|
hole — the close still refuses a tree that fails any stage, and nothing green is minted mid-run
|
|
294
295
|
— but a stage first tested at the tail is a stage the run pays full price to discover.
|
|
295
296
|
|
|
297
|
+
**The ceiling of 2 is RULED, not provisional (2026-08-13, PRD v1.63 §3).** One check per write
|
|
298
|
+
step is the contract: a step passing its own in-run check is never a verdict, only the close is
|
|
299
|
+
truth, and widening the ceiling so a step's own gate could approximate the close would move
|
|
300
|
+
judgement into the part the agent authors. Plan around one check; do not plan for a second one
|
|
301
|
+
arriving. It is raised only on a demonstrated starvation case (a real run where a step provably
|
|
302
|
+
cannot express its progress gate within one check and stalls for it), never for wording comfort,
|
|
303
|
+
and the change is the operator's — arbiter territory.
|
|
304
|
+
|
|
296
305
|
Red vocabulary (both validators): `parse-error`, `unknown-field`, `missing-required`,
|
|
297
306
|
`invalid-value`, `bounds`, `duplicate-id`, `close-type`, `close-hierarchy`,
|
|
298
307
|
`secret-literal`, `scope-escape`,
|
|
@@ -306,9 +315,12 @@ on refusals). It binds WRITES, not observations: on the exit side it fires only
|
|
|
306
315
|
the narrow one does), and `artifact-written`/`json-valid` paths carry no step-scope red at
|
|
307
316
|
all — the evaluator asks nothing about who wrote the file, so naming a prior step's artifact
|
|
308
317
|
is legal and satisfiable. `job-invalid` — a plan validated against a
|
|
309
|
-
missing or non-plan-shape job fails CLOSED). `
|
|
310
|
-
every check-menu consumer shares
|
|
311
|
-
|
|
318
|
+
missing or non-plan-shape job fails CLOSED). `closeStagesOf(job)` is the ONE staging
|
|
319
|
+
every check-menu consumer shares — it reads whichever close field the spec carries:
|
|
320
|
+
`closeDecl` → its declared stages (each enriched with the arbiter's own `gapKeep`), else
|
|
321
|
+
`stageClose(close)` (array → itself; legacy object predicate → its one-stage list, named
|
|
322
|
+
`close`; gold/rubric/hitl → null). A consumer reading `close` alone would see a declared
|
|
323
|
+
job as closeless and offer the drafter an empty check menu. The `secret-literal` sweep is
|
|
312
324
|
defense-in-depth against known token shapes — env-only loading remains the law, not the
|
|
313
325
|
sweep.
|
|
314
326
|
|
|
@@ -449,7 +461,169 @@ retired-but-accepted, so a plan carries no iteration bound to check; `scopes` is
|
|
|
449
461
|
`tree-changed` scope menu — the array of scope strings the drafter prompt listed, which
|
|
450
462
|
`runJob`/`runPlan` build internally from the signed fence plus the real directories beneath
|
|
451
463
|
it (a direct `validatePlan` caller may pass its own array); omitted, it derives from the
|
|
452
|
-
signed `writeScope` alone, never a free-text fallback. Menus exported: `EXIT_TYPES`, `MAX_EXITS_PER_STEP`, `MAX_PLAN_STEPS`, `WRITE_VERBS` — plus `stageClose`.
|
|
464
|
+
signed `writeScope` alone, never a free-text fallback. Menus exported: `EXIT_TYPES`, `MAX_EXITS_PER_STEP`, `MAX_PLAN_STEPS`, `WRITE_VERBS` — plus `stageClose` and `closeStagesOf`.
|
|
465
|
+
|
|
466
|
+
### Close authoring — the user declares what done means (`src/kinds.js`, `src/authoring.js`, `src/authorscout.js`, `src/authorflow.js`, `src/declaredclose.js`, `src/authorjob.js`)
|
|
467
|
+
|
|
468
|
+
The one layer where a human still wrote code. A close used to be a hand-written script
|
|
469
|
+
per patient; now the user PICKS A VERDICT CLASS, answers that class's questions, and an LLM
|
|
470
|
+
composes a DECLARATION over kinds whose implementations bareloop owns. **v1 admits exactly
|
|
471
|
+
ONE genre** (`TYPES`: a type checker stops complaining without breaking the tests) and
|
|
472
|
+
exactly one verdict class (`green`) — everything else refuses honestly, and every refusal
|
|
473
|
+
is COUNTED.
|
|
474
|
+
|
|
475
|
+
**The verdict class is the USER's answer (PRD v1.57 §1), and it DRIVES the authoring.**
|
|
476
|
+
`verdictType` is a declared radio the preflight validates, never inferred:
|
|
477
|
+
`VERDICT_CLASSES` = `green` | `soft-green` | `hitl`, with `LOCKED_CLASSES` =
|
|
478
|
+
`soft-green` | `hitl` declared-but-locked. Picking a locked one returns a counted
|
|
479
|
+
`request-red` refusal at ADMISSION — before that class's questions are ever asked. The
|
|
480
|
+
pick is also a PROMISE: every catalogue kind carries the `verdictClass` it can honestly
|
|
481
|
+
render, `closeCeiling(declaration)` reports the highest class a declaration demands, and a
|
|
482
|
+
declaration ABOVE the pick is a `class-ceiling` red naming the kind that raised it (inert
|
|
483
|
+
in v1 by construction — every live kind is mechanical).
|
|
484
|
+
|
|
485
|
+
**The pipeline, in call order.** Each piece is exported so an adopter can drive it, cache
|
|
486
|
+
a step, or test it without a provider.
|
|
487
|
+
|
|
488
|
+
| step | call | what it is |
|
|
489
|
+
|---|---|---|
|
|
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 |
|
|
491
|
+
| 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
|
+
| 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
|
+
| 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 |
|
|
494
|
+
| everything above, composed | `authorCloseForJob({ verdictType, answers, repoPath, lang, generate, ceilingUsd?, ... })` → `{ ok, closeDecl, verdictType, refusal, cost, ... }` | refuses at the cheapest gate that can refuse: an interview refusal costs **zero**. THE GENRE REFUSAL LIVES HERE (not in the interview): a language the catalogue owns no data for, and a LOCKED KIND the model reached for, both come back as counted `request-red` demand. **`ceilingUsd` is ONE number handed to BOTH paid seams** (the survey's and the declaration loop's) — the advertised ceiling and the enforced ceiling are the same ceiling. It travels as an EXPLICIT `null` when unset, because unbounded is a stated choice and an absent field read as falsy is that state reached by accident — and anything that is neither a finite number nor `null` throws at the seam rather than becoming that state by typo |
|
|
495
|
+
| assembly | `assembleSpec(specDraft, { closeDecl, verdictType })` | folds the authored half into the OPERATOR's half. Budgets, the fence, cadence, escalation and the provider are never authored by anything here. The GOAL is passed through, not generated. **A draft that ALREADY carries the authored half is REFUSED — it THROWS, never merges and never overwrites.** `AUTHORED_SPEC_FIELDS` (`close`, `closeDecl`, `verdictType`) names the fields this fold WRITES, as data, so the refusal and the fold cannot disagree about which half of the spec is which; the rule is `job.js`'s one step earlier (two closes are two arbiters, and picking one silently is how a signed artefact stops meaning what the signer read). `scripts/run-author.mjs` asks the same question at **$0 BEFORE the scout**, so the answer never arrives after the model has been paid |
|
|
496
|
+
| the three gates | `prepareSigning({ spec, workdir, seedRef? })` → `{ ok, specHash, gates, work, guards, refusal }` | D9, and it NEVER signs |
|
|
497
|
+
|
|
498
|
+
**D5 — the mandatory guards are SHOWN and FIXED, and the battery keys off the VERDICT
|
|
499
|
+
CLASS** (PRD v1.57 §2 — what a battery is FOR is the class of dishonesty that class of
|
|
500
|
+
verdict admits). `CLASS_BATTERIES` is the attachment point (structure, un-removability,
|
|
501
|
+
which slot is the model's); the tool-specific CONTENTS — which suppression patterns, which
|
|
502
|
+
extensions — resolve at COMPOSITION from the language the declaration names. The green
|
|
503
|
+
battery injects `changed-from-seed`
|
|
504
|
+
and `no-suppressions` FULLY PARAMETERISED (`classGuards({ verdictType, lang })`, which
|
|
505
|
+
THROWS rather than ever hand back an empty battery for a known class and language); the
|
|
506
|
+
model fills exactly one
|
|
507
|
+
slot (the target prefixes) and cannot change, drop, rename, re-kind or NARROW them —
|
|
508
|
+
`validateDeclaration` reds `guard-weakened` on any of it, including on an added `scope` that
|
|
509
|
+
would shrink what the suppression scan covers. `genreEnv(lang, { sourcePrefixes })` is the
|
|
510
|
+
same idea for environment: `MYPYPATH` on a Python patient is a fact no user can supply and
|
|
511
|
+
no model found, it moves no seed number, and a declaration that authors it itself reds
|
|
512
|
+
`genre-owned-env`.
|
|
513
|
+
|
|
514
|
+
**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)`).
|
|
520
|
+
|
|
521
|
+
**A red `count-not-worse` gap carries the LINES it counted, not only how many (F98).** Each
|
|
522
|
+
term's **KEPT** matched lines nest under that term's own breakdown row — the row that already
|
|
523
|
+
prints its `contributes +n`/`-n`, so a line's arithmetic role travels with it. Kept means the
|
|
524
|
+
lines that survived the scope filter and were actually read by the aggregate (a `first` term
|
|
525
|
+
echoes only the line that produced its value), which is to say the ones the count is made of.
|
|
526
|
+
A scope-DROPPED line is never among them (it was never counted, so naming it would aim the
|
|
527
|
+
worker outside its own population, F84's one-population law), duplicates collapse within a
|
|
528
|
+
term (a line feeding two terms plays two roles and appears under each), and the value is
|
|
529
|
+
still computed from the parsed values and never from this list.
|
|
530
|
+
They ride the **existing** gap channel and invent none: the declared stage's `gapKeep` prefix
|
|
531
|
+
on every line (Layer R's `redKeep` is DERIVED from that prefix), the same `GAP_LINE_CAP`, and
|
|
532
|
+
the same announced trim on overflow — so a consumer parsing the gap sees more lines under the
|
|
533
|
+
same shape, never a new one. `parseValue` returns them as `matched` alongside `value`,
|
|
534
|
+
per-term, index-aligned with the breakdown. The
|
|
535
|
+
licence is the one `pattern-absent-in-diff` already runs under: the instrument NAMED these
|
|
536
|
+
lines, and echoing what it named is not the barred move of naming a culprit it never
|
|
537
|
+
reported. **Nothing here can flip a signed spec hash** — the verdict logic, the baselines,
|
|
538
|
+
the counting, the `closeDecl` schema and `detail` are untouched; only what the executor TELLS
|
|
539
|
+
the worker changed.
|
|
540
|
+
|
|
541
|
+
**The three gates, and the signature (D9).** Nothing LLM-judges a close.
|
|
542
|
+
1. `validateCloseDecl(closeDecl, { verdictType, ... })` — schema, kinds, params, the F84
|
|
543
|
+
one-population law, F49's static nested-quantifier reject, the D5 guard equality, the
|
|
544
|
+
class ceiling, and the listing rule (a declared path SELECTS from the seed tree or it
|
|
545
|
+
does not exist). `verdictType` is REQUIRED and has no default: the guard battery hangs
|
|
546
|
+
off it, and a validator with no class in hand reds `class-absent` rather than reporting
|
|
547
|
+
a declaration it never checked D5 against.
|
|
548
|
+
2. the CLOSE PRECHECK — every stage runs against the real patient. A stage that cannot run
|
|
549
|
+
is `broken-close`: a CASUALTY, never a red.
|
|
550
|
+
3. the SEED-VERDICT READ — every stage, offered or not, at the seed. Which are RED (that is
|
|
551
|
+
the work) and which are GREEN (those are the guards) is handed back for the user to read.
|
|
552
|
+
**A close with no WORK stage red at seed is refused decision-ready** — it grades nothing,
|
|
553
|
+
and an instrument that scans nothing reads clean exactly like one that measures correctly.
|
|
554
|
+
|
|
555
|
+
**KNOWN LIMIT: gate 1 does not check a parser's FORMAT, and never will (F94, ruled WON'T-BUILD
|
|
556
|
+
2026-08-13, PRD v1.63 §1).** A declaration can name the right stage, over the right population,
|
|
557
|
+
with the right aggregate, and carry a `lineMatch` for a shape the tool never prints. Detecting
|
|
558
|
+
that at draft time needs stage → tool attribution, and `npm run typecheck` defeats it
|
|
559
|
+
mechanically: the declared command names npm, what prints is `tsc`, and the mapping lives in a
|
|
560
|
+
`package.json` the validator does not read. A detector that resolved some spellings and passed
|
|
561
|
+
the rest would read as coverage while being blind exactly where patients differ. **Gates 2 and 3
|
|
562
|
+
are the instrument for this class, and they work** — a term matching nothing over a non-empty
|
|
563
|
+
population reads unknown-not-zero (F6/F45/F93), so the seed-verdict read refuses it at $0 before
|
|
564
|
+
a signature, which is how the real case was caught. Formats are checked by RUNNING the tool,
|
|
565
|
+
never by reading a string. The complementary defence is upstream of the gate: for the tools the
|
|
566
|
+
genre owns facts about, `genreInstruments` hands the author the measured pattern (with its real
|
|
567
|
+
captured line) instead of asking, so fewer declarations reach gate 1 carrying an invented format
|
|
568
|
+
at all. Reopens only if a wrong-format parser ever reaches a SIGNED close.
|
|
569
|
+
|
|
570
|
+
Then the human signs `specHash`, unchanged: the approvals array and the human's word are
|
|
571
|
+
the arbiter relocating to the user, not disappearing. **Re-authoring is a spec edit (D6)** —
|
|
572
|
+
a different declaration is different bytes, a different resolved hash, and a new signature.
|
|
573
|
+
The guards are stored ENUMERATED and every short-form parser is expanded, so no
|
|
574
|
+
omittable-with-a-default field can change what runs without changing what was signed.
|
|
575
|
+
|
|
576
|
+
**A signing surface must show the GOAL and the judged stages as one reading (F87).** The
|
|
577
|
+
goal has to state everything the close will judge, and **nothing derives one from the other
|
|
578
|
+
or checks them against each other** — the only derivation runs close-stages → check menu, one
|
|
579
|
+
hop, one direction. So the sole defence against an unstated stage is that the person signing
|
|
580
|
+
reads both halves at once, and any surface you build around `prepareSigning` inherits that
|
|
581
|
+
obligation. Both reference runners now do it: `scripts/run-u.mjs --approve` names every close
|
|
582
|
+
stage (name + kind) under the goal — for **BOTH spec forms**, through `closeStagesOf`, since
|
|
583
|
+
gating that block on `closeDecl` reached exactly one of the eleven shipped specs and left the
|
|
584
|
+
other ten (command `close[]`) showing the goal with no stages under it, which is the same
|
|
585
|
+
half-reading in the guard against it (F100); a command stage prints `[command]` and NOT the
|
|
586
|
+
catalogue kind name it does not carry — and `scripts/run-author.mjs` prints the goal above the
|
|
587
|
+
declaration via `declarationLines(spec)` in `scripts/author-readout.mjs`, rendered from the
|
|
588
|
+
**RESOLVED** spec — the bytes that get hashed, never the draft and never the authored half
|
|
589
|
+
alone. An absent goal renders as absent rather than as a bare label. This is a READING, not a
|
|
590
|
+
validator: nothing compares the goal to the declaration, because deriving one from the other
|
|
591
|
+
is exactly what F87 forbids.
|
|
592
|
+
|
|
593
|
+
**Refusals are COUNTED (D13).** `refusalEvents(refusal)` returns the spine events: a
|
|
594
|
+
`job-red` carrying `{ code: 'request-red', verb, lib: 'bareloop' }` — the `lib` stamped at
|
|
595
|
+
the EMIT SITE, so `classifyIncidents` files it against bareloop's own catalogue and its
|
|
596
|
+
`suggestedAsk` reads `bareloop: …` rather than seeding an upstream ask — plus one
|
|
597
|
+
decision-ready `escalation` under `close-unauthorable`, which is in the ledger's excluded
|
|
598
|
+
set precisely because the demand is already counted once. A refusal that is a STOP rather
|
|
599
|
+
than demand (a broken instrument, a close with nothing to do) emits the escalation ALONE.
|
|
600
|
+
**Known limit, stated:** v1's derivation cannot tell `soft-green` from `hitl` and does not
|
|
601
|
+
try — both refuse under one verb (`non-green-verdict`), because naming which locked class a
|
|
602
|
+
job belongs to would be a guess about prose.
|
|
603
|
+
|
|
604
|
+
**At run time** the declared close is executed by the kind executor, never compiled down to
|
|
605
|
+
shell (that would turn owned kinds back into authored strings — the exact thing D3 makes
|
|
606
|
+
inexpressible). `runPlan` picks the executor from which field the signed spec carries and
|
|
607
|
+
everything downstream is the same code: first-red-wins, the `CLOSE_FAULTS` forbidden zone
|
|
608
|
+
(routed by a TYPED fault, so a timeout still offers "raise the close timeout"), the same
|
|
609
|
+
`close stage "<name>" failed:` gap header the trend reader parses, the same scrub at the
|
|
610
|
+
emission boundary, and the check menu still derived from stage names one hop. The seed is
|
|
611
|
+
READ at run start (`seedAtHead`) and recorded on a `close-decl` spine event together with
|
|
612
|
+
the grounded re-validation's result. Direct executor access: `runDeclaredStages(stages,
|
|
613
|
+
redact, { cwd, seedRef, timeoutMs })` returns `runClose`'s verdict shape **plus one added
|
|
614
|
+
field, `notes`** — the DIAGNOSTIC channel, per stage and on the summary, ABSENT when empty
|
|
615
|
+
(never `''`). M1's kinds announce some things whichever way a stage lands (a declared env var
|
|
616
|
+
that was dropped, the scope filter's own arithmetic), and a RED carries those out inside its
|
|
617
|
+
`gap` while a GREEN and an instrument stop had nowhere to put them — checking the GREENS is
|
|
618
|
+
doctrine, and an audit trail that only survives a red cannot do it. It is deliberately NOT
|
|
619
|
+
`gap`: every consumer guards with `if (gap)`, so a green carrying one would read as
|
|
620
|
+
revision-worthy and a stop carrying one would look like a verdict it never rendered. **Nothing
|
|
621
|
+
routes on it, nothing bounds on it, and no verdict moves because of it** — it rides the records
|
|
622
|
+
that already spread the whole verdict (`close-precheck`, `outer-close`, ralph's
|
|
623
|
+
`close-verdict`). `runDeclaredClose`
|
|
624
|
+
and `seedRead` are the kind executor's own two entries (`runDeclaredClose` is named apart
|
|
625
|
+
from ralph's shipped `runClose` because one runs a DECLARATION and the other an argv);
|
|
626
|
+
`closeStagesOf(job)` is the ONE staging every close consumer reads, widened to both fields.
|
|
453
627
|
|
|
454
628
|
### `snapshotScope(dir, scope)` / `evalExits(exits, { dir, snapshot?, runCheck? })` — `src/exits.js`
|
|
455
629
|
|
|
@@ -504,10 +678,10 @@ Reserved spine vocabulary (V7, machinery-free until job #1 surfaces one):
|
|
|
504
678
|
`coordination-red` — a failure between units (scope contention, step order, store
|
|
505
679
|
races), never to be folded into worker/interpreter reds.
|
|
506
680
|
|
|
507
|
-
### `runJob(spec, { approvals, workdir, provider, nativeProvider?, providerFor?, emit, capRuns?, strikeLimit?, shellCapUsd?, closeTimeoutMs?, layerRoot?, bridge?, priorSpentUsd?, priorSpendComplete?, priorWallMs?, resumeSeed?, resumeGrades?, resumeReplans? })` → outcome — `src/run.js`
|
|
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`
|
|
508
682
|
|
|
509
|
-
The last
|
|
510
|
-
default to `0` / `true` / `0` / `null` / `[]` / `null`, so a fresh run passes none of them.
|
|
683
|
+
The last seven are the RESUME fold and are documented under *Resuming a killed run* below; they
|
|
684
|
+
default to `0` / `true` / `0` / `null` / `[]` / `null` / `null`, so a fresh run passes none of them.
|
|
511
685
|
Three of them are folds of a bound the operator SIGNED (money, wall, replans) and one is a
|
|
512
686
|
readout seed (grades) — the distinction matters and is spelled out there.
|
|
513
687
|
|
|
@@ -519,8 +693,11 @@ known-answer round-trip before tokens: `smoke-red` — a silent degradation thro
|
|
|
519
693
|
`min(job budget − spent, shell cap)`.
|
|
520
694
|
|
|
521
695
|
Outcomes: `green | already-green | escalated | unapproved-spec | job-red | smoke-red |
|
|
522
|
-
plan-red | check-red | close-red | close-unsupported | recipe-stale | pricing-red |
|
|
523
|
-
provider-red | interpreter-red | cap-halt | wall-halt | step-stalled | step-red:<id>`.
|
|
696
|
+
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>`.
|
|
698
|
+
`branch-red` is the WORK BRANCH refusing (below): the patient is not a git checkout, its
|
|
699
|
+
branch namespace has no free name, or a resume's recorded branch is gone. Zero tokens, and
|
|
700
|
+
never a fallback to working on the branch the run was handed. `provider-red` is a
|
|
524
701
|
transport throw or a worker round the API cut off mid-generation (`truncated:max_tokens`,
|
|
525
702
|
BA-6 — before which it laundered into a clean finish, F25): no verdict exists and the failed
|
|
526
703
|
round's spend is only partly known (F6). `cap-halt` is the wallet; `wall-halt` is the clock
|
|
@@ -544,9 +721,37 @@ three times on one call — each stall silently abandons the hung call and reiss
|
|
|
544
721
|
laundered into `provider-red`, and carries `spendComplete:false` (an abandoned call may
|
|
545
722
|
already have been billed). **The reissue is wall-aware:** past the run's deadline the fuse does
|
|
546
723
|
not self-heal — self-heal is what a run does with time left — and it gives up as `wall-halt`,
|
|
547
|
-
not `step-stalled`, since a replan has nothing left to re-allocate.
|
|
724
|
+
not `step-stalled`, since a replan has nothing left to re-allocate. **A `step-stalled` run is
|
|
725
|
+
a CHECKPOINT, not a verdict** (hamr's ruling, 2026-08-13 — PRD v1.64 §1): it is resumable on
|
|
726
|
+
the reference runner alongside the two governance halts, the work on disk stands, and its
|
|
727
|
+
`spendComplete:false` carries forward as a floor. Every one of them is a
|
|
548
728
|
decision-ready escalation with a terminal `job-end`: the spine never dangles.
|
|
549
729
|
|
|
730
|
+
**`gate-red` is not in that list, and the plan flow no longer mints it (F98).** A fence
|
|
731
|
+
DENIAL STREAK — bare-agent's BA-11 deny-spin guard returning `denied:*` after consecutive
|
|
732
|
+
refusals — is a **BOUNDED ATTEMPT, never a terminal**, on BOTH worker surfaces: the attempt
|
|
733
|
+
ends, `attempt-bounded` is emitted with the `reason`, the close judges the partial work, the
|
|
734
|
+
gap feeds forward, the caps are unchanged and the loop continues.
|
|
735
|
+
**The NEXT attempt is told which bound fired** (v1.64 §2): the bound carries
|
|
736
|
+
`{iteration, cause, reason}`, and where the cause is `denied` the note says the GATE cut the
|
|
737
|
+
attempt and quotes what the gate recorded, verbatim — instead of the round-cutoff sentence,
|
|
738
|
+
which was false on cause and on count and aimed the worker at its read budget when the thing
|
|
739
|
+
that stopped it was the fence. **Nothing is invented**: bare-agent returns `denied:<tool>`
|
|
740
|
+
and nothing else — no path, no streak count — so neither is claimed. Every other cause keeps
|
|
741
|
+
the round-bound wording byte for byte. The reason is scrubbed ONCE at capture through the one
|
|
742
|
+
`SECRET_PATTERNS` inventory and shared by prompt and spine, and trimmed under the repo's one
|
|
743
|
+
`GAP_TRIM_MARKER` (F90.2). It is the same lane
|
|
744
|
+
`max_turns` and `loop.stop()` take, for the same reason — the attempt ran, produced audited
|
|
745
|
+
work, and was cut short by a governance bound doing its job. **The fence holding is the
|
|
746
|
+
system working, which is the opposite of a fault**; routing it as an instrument stop killed a
|
|
747
|
+
converging run with $1.77 and 21.6 wall-minutes still on it. Nothing about the gate changed:
|
|
748
|
+
the same actions are denied, the same audit rows are written, a genuine scope escape produces
|
|
749
|
+
exactly what it did before — it now costs one bounded attempt instead of the whole run. The
|
|
750
|
+
category survives only as a passthrough (`ralph`'s decision table still answers to it if a
|
|
751
|
+
caller's own middle throws it, and `EXCLUDED_ESCALATIONS` still lists it so a future emission
|
|
752
|
+
is filed as governance rather than re-read as a capability gap), but no site in this library
|
|
753
|
+
raises it.
|
|
754
|
+
|
|
550
755
|
**Every `job-end` carries the money, on every path**: `{ outcome, spentUsd, spendComplete }`
|
|
551
756
|
(plus `step`/`cause`/`detail` where the outcome has them). `spentUsd` is the accumulated sum
|
|
552
757
|
of PRICED rounds ONLY — never an estimate from token counts or averages (cap-not-estimate) —
|
|
@@ -568,7 +773,9 @@ their own ledger): **close precheck** (`already-green` is a
|
|
|
568
773
|
DISTINCT zero-token outcome; a forbidden-zone verdict escalates before spend) → **check-menu
|
|
569
774
|
preflight** (`check-menu` names the derived menu, then every OFFERED stage's chain runs once
|
|
570
775
|
at $0 — an unrunnable stage is a `check-red` stop before tokens, not a fault mid-plan) →
|
|
571
|
-
**
|
|
776
|
+
**THE WORK BRANCH** (below — created before the first paid call, `branch-red` if it cannot
|
|
777
|
+
be; on a RESUME it runs FIRST, ahead of the precheck and the preflight, because the recorded
|
|
778
|
+
branch IS the tree those instruments must measure) → **SCOUT** (read-only by construction: neither the
|
|
572
779
|
write-class nor the store-class verbs are in its menu; hard-bounded rounds) → **PLAN** (the decompose call —
|
|
573
780
|
the planner never sees the repo, only the scout blob; drafted against a schema
|
|
574
781
|
description with check NAMES only; `validatePlan` gates it, one redraft with the reds
|
|
@@ -586,6 +793,56 @@ plan-red | check-red | close-red | wall-halt`. Worker prompts hold the v1.12 §5
|
|
|
586
793
|
(mutation-proven): the absolute repo root, the step's action/target, prior artifacts,
|
|
587
794
|
the gap — NEVER the budget, the close command, a check's command, or the arbiter's books.
|
|
588
795
|
|
|
796
|
+
**THE WORK BRANCH — a hard rule, and a REQUIREMENT on the patient (PRD v1.57 §3).** hamr's
|
|
797
|
+
ruling, verbatim: *"The agent creates a NEW BRANCH before it touches any code — a HARD RULE,
|
|
798
|
+
no exceptions. Not a default, not a preference: no job edits the branch it was handed, and
|
|
799
|
+
none edits `main`."* So **the patient must be a git repository with at least one commit** —
|
|
800
|
+
that is new, and it is the honest consequence of the rule rather than an option. And the
|
|
801
|
+
patient itself is **ALWAYS a separate COPY of the repo being treated, never the original**
|
|
802
|
+
(hamr, 2026-08-09): the branch bounds where writes land, but workers and close stages
|
|
803
|
+
still mutate the patient tree (branches per try, caches, worktrees) — the copy is the
|
|
804
|
+
blast radius; the original never carries it. What the run does, before its first paid
|
|
805
|
+
call:
|
|
806
|
+
|
|
807
|
+
- **The name is DERIVED from the SIGNED spec**, never model-authored (a branch name is
|
|
808
|
+
arbiter bookkeeping): `bareloop-` + the spec's own `job` slug — `bareloop-typecheck-fix`.
|
|
809
|
+
`workBranchName(spec)` is exported so a runner can show it at its approval gate. The goal
|
|
810
|
+
prose is deliberately NOT an input: it is edited between runs without changing what the
|
|
811
|
+
job is, and the branch name would move with it.
|
|
812
|
+
- **A name already taken is SUFFIXED** `-2`, `-3`… — a second run of the same spec is a
|
|
813
|
+
second blast radius and never reuses the first one's branch.
|
|
814
|
+
- **A RESUME returns to ITS OWN branch.** Pass `resumeBranch` (read off the dead spine by
|
|
815
|
+
`readResume` as `restart.branch`); without it the deterministic name would collide with the
|
|
816
|
+
killed leg's own branch and mint a `-2` beside the work the resume exists to keep. The
|
|
817
|
+
recorded branch is the ONLY branch a resume can land on — it is validated and checked out,
|
|
818
|
+
never re-derived from the spec and never suffixed. A recorded branch that no longer exists
|
|
819
|
+
is a `branch-red` STOP, never a fresh start.
|
|
820
|
+
- **On a RESUME this step runs FIRST**, ahead of the close precheck and the check preflight
|
|
821
|
+
(cold, it runs after them — see the already-green bullet). Both are $0 readings OF A TREE,
|
|
822
|
+
and on a resume the tree they must read is the recorded branch's: standing on whatever ref
|
|
823
|
+
the operator handed back, the precheck could read `already-green` off a tree that is not
|
|
824
|
+
this run's and every `baseline: "seed"` stage would baseline against it. Nothing is minted
|
|
825
|
+
by moving it — the resume arm creates nothing — and a resume whose branch is gone or foreign
|
|
826
|
+
now stops `branch-red` before the arbiter grades anything at all.
|
|
827
|
+
- **`work-branch { branch, created, resumed, from, base, repo, collided? }`** lands on the
|
|
828
|
+
spine at creation — the run's books say where its work went.
|
|
829
|
+
- **Every failure is `branch-red`**, escalated decision-ready with zero spend. There is no
|
|
830
|
+
fallback to the handed branch anywhere; and `mkWorker`, the ONE seam that grants
|
|
831
|
+
write-class verbs, refuses to build a write-capable worker with no branch prepared, so the
|
|
832
|
+
ordering is a convenience and the guard is the rule.
|
|
833
|
+
- **A COLD run's `already-green` tree leaves no branch behind** — the precheck returns above
|
|
834
|
+
this step, and a run with no work has no blast radius to bound. The clause is about
|
|
835
|
+
MINTING: a RESUME that reads `already-green` off its own branch (which it is standing on by
|
|
836
|
+
then) leaves that branch and the paid work on it exactly where they were.
|
|
837
|
+
|
|
838
|
+
**What it does NOT do.** bareloop never commits, so the branch bounds where work LANDS: the
|
|
839
|
+
tree stands on a branch nobody handed the run, and the handed ref is never moved or written
|
|
840
|
+
through. It is not containment — v1.41's local-trust model is unchanged, and worker/close
|
|
841
|
+
children still run as the operator's OS user with network egress. A workdir that is a
|
|
842
|
+
SUBDIRECTORY of a repository gets its branch in the enclosing repository (git resolves
|
|
843
|
+
upwards, exactly as every other git read in the flow does); the `work-branch` record's `repo`
|
|
844
|
+
field names which repository that was, rather than leaving it to be discovered.
|
|
845
|
+
|
|
589
846
|
**The strike ladder — how a STEP ends (F77–F79).** A plan step is **not** bounded by a
|
|
590
847
|
number of iterations. It runs until it stops making PROGRESS: a *strike* is a red iteration
|
|
591
848
|
that repeats an already-seen gap or wrote nothing, and `strikeLimit` strikes (shell option on
|
|
@@ -713,7 +970,7 @@ is read, so a resume of a resume inherits the previous leg's chain and not the o
|
|
|
713
970
|
(`createClock`, `src/clock.js`) from the signed `maxWallMs` and emits `wall-clock` with the
|
|
714
971
|
requested AND enforced numbers up front, plus the `closeStages` count that explains the gap
|
|
715
972
|
between them (`enforcedMs = maxWallMs + closeStages × closeTimeoutMs`, one expression — the
|
|
716
|
-
stage count comes from `
|
|
973
|
+
stage count comes from `closeStagesOf`, the same staging the runner executes). Enforcement is a
|
|
717
974
|
**between-round deadline** — the only seam that exists, since `loop.stop()` cannot cut an
|
|
718
975
|
in-flight call (F61: fired at 500ms,
|
|
719
976
|
returned at 4,018ms) — so an attempt that crosses the deadline mid-flight emits `wall-bounded`,
|
|
@@ -831,7 +1088,9 @@ learning credit and runs no on-green retention. A close that cannot RUN is a `br
|
|
|
831
1088
|
escalation before any provider call. So the resume story is: a `cap-halt` or `wall-halt`
|
|
832
1089
|
stop IS the checkpoint (the workdir + the close), the human raises `budgetUsd` or
|
|
833
1090
|
`maxWallMs` (a new spec hash — re-sign), and the rerun picks up where the budget died,
|
|
834
|
-
paying zero provider calls for anything already green.
|
|
1091
|
+
paying zero provider calls for anything already green. (Those two are the *allowance* stops,
|
|
1092
|
+
the ones with a lever to raise. They are not the whole resumable set — `step-stalled` is also
|
|
1093
|
+
a checkpoint, with nothing to re-sign; see `resumableOutcomes` under the resume reader.)
|
|
835
1094
|
|
|
836
1095
|
**Unpriced is never free (F6/F12):** the ledger meters **per ROUND** (`worker-round`, at
|
|
837
1096
|
bare-agent's `onLlmResult` seam) — money is counted as it is spent, never at the end of an
|
|
@@ -853,7 +1112,14 @@ the loop contract (F16 — it is ONE attempt inside `while close-red and under-c
|
|
|
853
1112
|
re-run with the close's verdict; without this a model one-shots and can eat the budget in
|
|
854
1113
|
reads before ever writing). It is NOT allowed to read the run's own machinery (F14): the gate
|
|
855
1114
|
audit, the smoke store and the litectx store are denied — the agent neither authors the
|
|
856
|
-
arbiter nor reads its books.
|
|
1115
|
+
arbiter nor reads its books. **That denial is now REGISTERED in the persona, not left to be
|
|
1116
|
+
discovered (F98):** `PERSONA_TOOLS` names `gate-audit.jsonl`, `.smoke`, `.litectx` and the
|
|
1117
|
+
run's spine as always-denied, records of how the worker is being judged and never anything
|
|
1118
|
+
about its task, beside the absolute-path law it is the twin of — both are fence facts a worker
|
|
1119
|
+
cannot infer. It renders for EVERY worker on every grant (a worker granted only `write` gets
|
|
1120
|
+
no component strategy paragraph at all and must still be told). The fence is unchanged; what
|
|
1121
|
+
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
|
|
857
1123
|
`close-unsupported` (execution lands at N4).
|
|
858
1124
|
|
|
859
1125
|
### The reuse registry (Layer 3) — `src/bridges.js`, `src/selection.js`
|
|
@@ -1054,8 +1320,8 @@ the secrets scan.
|
|
|
1054
1320
|
A reuse run is up to `tries + 1` full jobs long, so a kill mid-run is a real event and it
|
|
1055
1321
|
must not cost the whole envelope. **`readResume(events, {deathAt?, direct?, resumableOutcomes?})`**
|
|
1056
1322
|
reads the dead run's own spine back into the state a resume continues from: `{ started,
|
|
1057
|
-
approvalHash, completed[], tried[], restart (with its step-level `seed
|
|
1058
|
-
`grades`), r1Missing, spentUsd,
|
|
1323
|
+
approvalHash, completed[], tried[], restart (with its step-level `seed`, its close
|
|
1324
|
+
`grades`, and its work `branch`), r1Missing, spentUsd,
|
|
1059
1325
|
spendComplete, carrySpentUsd, carrySpendComplete, ended, greened, … }`. Hand that object to
|
|
1060
1326
|
**`runReuse({…, resume})`** and the run picks up where it stopped.
|
|
1061
1327
|
|
|
@@ -1073,16 +1339,45 @@ semantics are byte-unchanged (PRD v1.46 §3):
|
|
|
1073
1339
|
is `$0`: a leg whose every round came back unpriced folds nothing countable and is still not
|
|
1074
1340
|
exact, so the declaration is emitted to carry that unknown forward rather than letting it read
|
|
1075
1341
|
as an exact zero (F6).
|
|
1076
|
-
- **`resumableOutcomes: [
|
|
1077
|
-
|
|
1078
|
-
complete" is true of a VERDICT; a
|
|
1079
|
-
|
|
1080
|
-
|
|
1342
|
+
- **`resumableOutcomes: [...]`** reclassifies a landed `job-end` whose outcome is a
|
|
1343
|
+
CHECKPOINT rather than a verdict as a restart rather than a completed row. "A landed
|
|
1344
|
+
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
|
|
1348
|
+
out. `step-stalled` joined them 2026-08-13 on hamr's ruling (PRD v1.64 §1): it is the one
|
|
1349
|
+
terminal whose OWN escalation already offers *"retry the run"*, nothing about the work on
|
|
1350
|
+
disk is wrong, and the shape that named the fix is a stall that trips with time left and
|
|
1351
|
+
then rides out past the wall — losing a checkpoint a `wall-halt` would have kept. Its NAME
|
|
1352
|
+
deliberately stays `step-stalled` because `runJob` keys the **F44 spend FLOOR** on that
|
|
1353
|
+
outcome; renaming it would report unknown spend as exact. The floor travels: a stalled
|
|
1354
|
+
leg's `spendComplete:false` reaches the preview as `≥$x` and the resumed leg as
|
|
1355
|
+
`priorSpendComplete: false`, so the next terminal stays a floor rather than healing an
|
|
1356
|
+
unknown by inheriting it. A green or any red stays non-resumable under every setting — a
|
|
1357
|
+
verdict already rendered is never re-bought.
|
|
1358
|
+
**One exception, and it is a DERIVATION rather than a widening (D4a, F96).** A recorded
|
|
1359
|
+
`step-red` is re-read as a wall-halt — and becomes resumable — when the run's OWN spine
|
|
1360
|
+
carries a `wall-bounded {bounded: true}` record STRICTLY BEFORE its terminal, and only when
|
|
1361
|
+
`'wall-halt'` is itself in `resumableOutcomes`. It fires on the run's own primary evidence,
|
|
1362
|
+
which no caller can assert; a `step-red` with no wall record in front of it is untouched, so
|
|
1363
|
+
"a red is an answer" survives intact. It touches no close verdict — the grades stand and the
|
|
1364
|
+
resume seeds from them. The readout surfaces `wallDerivedHalt` so an operator resuming a spine
|
|
1365
|
+
that SAYS `step-red` is told which record turned it into a checkpoint. **This is a PERMANENT
|
|
1366
|
+
safety net, not a migration shim (ruled 2026-08-13, PRD v1.63 §2):** the defect that minted
|
|
1367
|
+
the shape is fixed at source, but spines are append-only forever, so the population it protects
|
|
1368
|
+
can never shrink — and on a healthy spine it is a branch that is never taken.
|
|
1369
|
+
|
|
1370
|
+
`restart.branch` is the WORK BRANCH the dead leg was working on, READ off its `work-branch`
|
|
1371
|
+
record and never re-derived: the name is deterministic from the signed spec, so a restart that
|
|
1372
|
+
recomputed it would collide with its own predecessor and be handed a fresh `-2` beside the work
|
|
1373
|
+
it came back for (the declared-fold precedent, `priorSpentUsd`'s exactly). Pass it on as
|
|
1374
|
+
`resumeBranch`; `runReuse` does it for you. `null` — a leg killed before it ever branched — is
|
|
1375
|
+
the cold path, and correct, because a leg that never branched has no work to strand.
|
|
1081
1376
|
|
|
1082
1377
|
`scripts/run-u.mjs --resume <runid|path>` is the operator-side consumer: it skips the patient
|
|
1083
1378
|
reset (`resumeTreeGate` instead — a dirty tree is what a resume expects; only a moved HEAD
|
|
1084
|
-
stops it), folds the halted run's money and wall in,
|
|
1085
|
-
outside watchdog on the REMAINING wall. The top-up itself stays a spec edit the human signs.
|
|
1379
|
+
stops it), folds the halted run's money and wall in, returns to the dead leg's own work branch,
|
|
1380
|
+
re-enters at the checkpoint, and arms the outside watchdog on the REMAINING wall. The top-up itself stays a spec edit the human signs.
|
|
1086
1381
|
**A zero wall remainder REFUSES the launch** (exit 2, before the key and before the patient):
|
|
1087
1382
|
W-2 says a run out of time keeps the grade it has and stops, so a resume with no time to start
|
|
1088
1383
|
anything in is a decision, not an unbounded launch — and launching it used to hand the outside
|
|
@@ -1152,6 +1447,12 @@ and is re-signed.
|
|
|
1152
1447
|
fold — a garbage, non-finite or negative `count` reads as `0` rather than widening the ceiling.
|
|
1153
1448
|
Null/omitted is the cold path. The disarm control is pinned: a resumed leg with an UNSPENT
|
|
1154
1449
|
ceiling still replans, so the bound is bounded, not switched off.
|
|
1450
|
+
**NEVER-REFILL is now the RULING, not just the behaviour (2026-08-13, PRD v1.63 §4).** A resume
|
|
1451
|
+
inherits the spent replan ledger, entire; a refill would let every kill buy an allowance, which
|
|
1452
|
+
is unlimited replanning through the side door, and unlimited replanning launders thrash as
|
|
1453
|
+
adaptation. The consequence for an adopter is a real one and is deliberate: a resume whose
|
|
1454
|
+
ledger is already spent re-enters its plan with no channel to replace it. That is not funded —
|
|
1455
|
+
it is SHOWN, by the resume banner, before you sign.
|
|
1155
1456
|
- **Under the REMAINDER, never a fresh allotment.** The killed attempt's spend and wall
|
|
1156
1457
|
FOLD IN (`runJob`'s `priorSpentUsd`/`priorSpendComplete`/`priorWallMs`, `createClock`'s
|
|
1157
1458
|
`priorElapsedMs`), so
|
|
@@ -1221,12 +1522,19 @@ whichever territory was seen first), `broken-close` (consumer-attributed),
|
|
|
1221
1522
|
`verb` field, prose-quoted verb as legacy fallback; both classes take their `lib` from the
|
|
1222
1523
|
red's own stamp, so neither seeds an upstream ask for a bareloop-catalogue refusal),
|
|
1223
1524
|
`retention-red`, `config-red`
|
|
1224
|
-
(drafting friction — attributed to bareloop's own schema/prompt).
|
|
1525
|
+
(drafting friction — attributed to bareloop's own schema/prompt). Close-authoring
|
|
1526
|
+
refusals ride the SAME `request-red` channel with `lib: 'bareloop'` stamped at the emit
|
|
1527
|
+
site (`refusalEvents`), so genre/verdict/locked-kind demand is counted without ever
|
|
1528
|
+
seeding an upstream ask. Deliberate exclusions
|
|
1225
1529
|
(`EXCLUDED_ESCALATIONS`, a runtime set — anything outside classified ∪ excluded is
|
|
1226
1530
|
counted as unmapped, never dropped): `cap-halt`/`wall-halt` (budget stories, money and
|
|
1227
1531
|
time), `step-stalled` (the stall fuse firing is our governance, not an observed provider
|
|
1228
1532
|
failure), `step-variance` (a planning story), `gate-red`/`smoke-red` (governance working
|
|
1229
|
-
as intended / already counted
|
|
1533
|
+
as intended / already counted — and `gate-red` is no longer minted anywhere in this library,
|
|
1534
|
+
kept listed because this set is EXECUTABLE: dropping a name would not delete the category, it
|
|
1535
|
+
would re-file any future emission of it as a counted capability gap), `hitl-close`/`close-unsupported`/`close-unauthorable` (by design — and the last one
|
|
1536
|
+
is excluded for a SECOND reason: its demand is already counted once as the `request-red`
|
|
1537
|
+
the same refusal emits, so counting the escalation too would double every refusal),
|
|
1230
1538
|
`close-timeout`/`close-killed`/`close-crashed` (the arbiter's own named terminals, F17);
|
|
1231
1539
|
`close-verdict`/`artifact-red` stay worker stories, `pr-red` operator environment.
|
|
1232
1540
|
`suggestedAsk` on every row is a template seed for an upstream ask — filing stays human;
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bareloop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.10.0",
|
|
4
4
|
"description": "Workflows that earn their own design, with receipts — agent-authored workflows for repeated, long, verifiable jobs that improve across runs under an un-gameable gate.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|