bareloop 0.6.0 → 0.8.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 +690 -0
- package/README.md +30 -11
- package/bareloop.context.md +658 -38
- package/package.json +2 -2
- package/src/bridges.js +571 -0
- package/src/clock.js +57 -5
- package/src/exits.js +8 -2
- package/src/index.js +29 -0
- package/src/job.js +43 -6
- package/src/ladder.js +196 -0
- package/src/ledger.js +84 -9
- package/src/plan.js +106 -107
- package/src/planrun.js +1040 -168
- package/src/ralph.js +156 -29
- package/src/reuse.js +1665 -0
- package/src/run.js +92 -6
- package/src/selection.js +160 -0
- package/src/spine.js +7 -2
- package/src/trend.js +405 -0
- package/src/validate.js +121 -0
- package/types/bridges.d.ts +198 -0
- package/types/clock.d.ts +25 -3
- package/types/exits.d.ts +14 -0
- package/types/index.d.ts +3 -0
- package/types/job.d.ts +13 -2
- package/types/ladder.d.ts +86 -0
- package/types/plan.d.ts +41 -41
- package/types/planrun.d.ts +208 -9
- package/types/ralph.d.ts +98 -4
- package/types/reuse.d.ts +392 -0
- package/types/run.d.ts +63 -3
- package/types/selection.d.ts +46 -0
- package/types/spine.d.ts +8 -1
- package/types/trend.d.ts +108 -0
- package/types/validate.d.ts +50 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,696 @@ All notable changes to bareloop are documented here. Format:
|
|
|
5
5
|
[SemVer](https://semver.org/spec/v2.0.0.html). Pre-1.0: **minor** = a ladder rung or
|
|
6
6
|
feature lands, **patch** = docs, fixes, scaffolding.
|
|
7
7
|
|
|
8
|
+
## [0.8.0] — 2026-08-07
|
|
9
|
+
|
|
10
|
+
### Changed
|
|
11
|
+
- **The variance meter REPORTS progress — and still decides nothing on it** (F85, A+B;
|
|
12
|
+
`src/planrun.js`). The meter stopped a step that was CONVERGING and then told the redrafting
|
|
13
|
+
planner that step's exits were *"unmoved"*. Two instruments over one run, and only one of them
|
|
14
|
+
right. Live on `u-msh70zla`: the close gaps went 30 → 24 → 15 → 14 with the ladder recording
|
|
15
|
+
`distinctGaps 3` / `strikes 0`, while the replan brief said nothing had moved and the materials
|
|
16
|
+
said *"0 step(s) completed"* — so the replanner, told it had achieved nothing, rebuilt a file
|
|
17
|
+
that was already at zero errors, wrote nothing twice, and the run died with $1.09 and 7 minutes
|
|
18
|
+
unspent. hamr's ruling, verbatim: *"meter is right but missing a piece … it should give heads up
|
|
19
|
+
on money/time + progress for llm to judge."*
|
|
20
|
+
- **The firing condition is byte-identical** — `moneyShare`/`timeShare >= 0.5`, both axes, the
|
|
21
|
+
same threshold, the same throw. A progress term in the TRIGGER would make a governance
|
|
22
|
+
instrument over an operator-owned allowance judge capability, so a converging step that is
|
|
23
|
+
eating the run is still stopped. What was missing was never a term in the condition; it was
|
|
24
|
+
the READING.
|
|
25
|
+
- **The reading is read off `src/trend.js` — the SAME instance the money halt reads.** Nothing
|
|
26
|
+
new is computed at the meter: no second reader, because two instruments answering one question
|
|
27
|
+
is the exact defect being fixed. Per-stage, stage names and numbers only, and `unknown` stays
|
|
28
|
+
`unknown` — a close that reports no number donates nothing (F6).
|
|
29
|
+
- **The hardcoded `with its exits unmoved` string is DELETED.** It printed on every step-variance
|
|
30
|
+
stop whatever had happened, and on `u-msh70zla` it was simply false. The escalation detail —
|
|
31
|
+
which a human reads and which the replan brief quotes — now states the meter's fact (*a share of
|
|
32
|
+
the run was consumed*) and then the trend instrument's measured reading, as two separate claims
|
|
33
|
+
rather than one fused verdict on the work. The materials `progress` line keeps its structural
|
|
34
|
+
sentence (*"step N of M did not finish; K step(s) completed before it"* — a plan's shape is what
|
|
35
|
+
the planner re-allocates) and gains the close trend beside it.
|
|
36
|
+
- **Spine change:** the `variance` record gains `trend` / `motion` / `reading` / `series` — the
|
|
37
|
+
same four names and the same shapes the money halt already emits, so one reader parses a
|
|
38
|
+
progress reading wherever it appears. ADDED fields, never repurposed ones (the spine is
|
|
39
|
+
append-only forever). Measured post-fix on `u-mshcpdg4`, iteration 3 on `finish-strict-typecheck`:
|
|
40
|
+
`moneyShare 0.497`, `timeShare 0.769`, `axis time`, `trend "converging"`,
|
|
41
|
+
`reading "still progressing — typecheck 30 → 15 → 15 → 1"`,
|
|
42
|
+
`series [{stage:"typecheck",values:[30,15,15,1]}]` — against `u-msh70zla`'s pre-fix record, which
|
|
43
|
+
carried the shares and nothing else.
|
|
44
|
+
- **`runTrend` was blind to everything a STEP achieves** (F85, the prerequisite found en route). The
|
|
45
|
+
run's trend reader was fed only the close PRECHECK and the outer fix loop, so a meter firing
|
|
46
|
+
mid-step had nothing to report about a step that had just gone 24 → 15 → 14. Two folds corrected:
|
|
47
|
+
a step's failing check is an arbiter-rendered close grade (same `runStages`, same chain, same stage
|
|
48
|
+
names) and is now recorded like one; and it is recorded on the RAW gap, because `evalExits` wraps
|
|
49
|
+
stage output as `check "<name>" red: …` and that wrapper line carries the word "red" with no number
|
|
50
|
+
on it — so every step grade used to donate a null. Measured on `u-msh70zla`'s own archived gaps:
|
|
51
|
+
raw reads `typecheck 24 → 15 → 14`, wrapped reads nothing at all. Plus a **preflight seed** so the
|
|
52
|
+
counter has a baseline: the precheck reds at the close's FIRST failing stage (first-red-wins) and
|
|
53
|
+
every shipped close opens with `changed-from-seed`, so the numeric stages had no baseline at all and
|
|
54
|
+
a run would read `unknown` on exactly the stop the readout exists for. Seeded ONCE PER STAGE, off the
|
|
55
|
+
reader's own books — the precheck and the preflight menu grade the same unchanged tree back to back,
|
|
56
|
+
and a repeated baseline in a series reads as an attempt that achieved nothing, which is the phantom
|
|
57
|
+
flat step this change exists to remove.
|
|
58
|
+
- **`tests/resume-u.test.js` is decoupled from the live job spec.** It read
|
|
59
|
+
`jobs/bareagent-u-types.json` while hardcoding $8 / 45min, so an operator budget edit on a live spec
|
|
60
|
+
turned the suite red. Every number now derives from the spec it reads, proven by passing at a third
|
|
61
|
+
budget value. Suite: **1023 tests** (1027 minus the 4 deleted with the `never wrote` feature below);
|
|
62
|
+
typecheck and `build:types` exit 0.
|
|
63
|
+
- **The per-step model tier menu narrows to `['sonnet']`** (`STEP_MODELS`, `src/plan.js` — a PUBLIC
|
|
64
|
+
surface: a plan declaring `model: "haiku"` is now REDDED by `validatePlan` with `invalid-value` at
|
|
65
|
+
`steps.N.model`, carrying the menu). hamr's order, verbatim: *"haiku should be dropped to see if it
|
|
66
|
+
passes, if it's agent poor work or harness, like always."* **This is a reversible ATTRIBUTION PROBE,
|
|
67
|
+
not a finding that haiku is incapable** — what the $0 archive read behind it shows is a CONFOUND, in
|
|
68
|
+
both directions. Over 186 spines / 71 accepted plans / 255 steps, only **20 steps ever declared a tier
|
|
69
|
+
at all**: haiku **12** — 9 of them on a REPLANNED plan, 6 the LAST step of their plan, round bounds
|
|
70
|
+
3–12, **2 ever green** — against explicitly-declared sonnet **8**, 2 on a replan, 3 last, bounds 14–32,
|
|
71
|
+
**5 green**. The planner reaches for haiku on steps it judges mechanical, and a replanned step sits on
|
|
72
|
+
a run already in trouble, so neither column is a controlled read; and all three bareagent-u runs of
|
|
73
|
+
2026-08-06 tiered their replanned step down (haiku/6, haiku/8, haiku/12) and all three failed. That
|
|
74
|
+
confound sat directly on top of the programme's most recent capability read (F86's tail). Removing the
|
|
75
|
+
tier is how it gets attributed: with one tier expressible, a still-failing replan is agent or harness,
|
|
76
|
+
never the tier.
|
|
77
|
+
- **The `model` field and its validator branch STAY.** A one-entry menu means a plan — or a stored
|
|
78
|
+
bridge — declaring `model: "sonnet"` still validates, and restoring haiku is a one-token edit. No
|
|
79
|
+
stored bridge declares haiku (verified).
|
|
80
|
+
- **The drafter prompt's `model` line is DELETED** rather than reduced to a one-item menu: it
|
|
81
|
+
advertised *"a cheaper tier for mechanical steps"* and there is no cheaper tier now, and reciting a
|
|
82
|
+
single legal value spends tokens to invite a no-op field (`attempts` is the existing
|
|
83
|
+
legal-but-not-offered precedent). Verified by RENDERING the real prompt — 0 lines match
|
|
84
|
+
`/model|tier|haiku|sonnet|cheaper/i` in either the fresh or the replan form.
|
|
85
|
+
- **Only what the AGENT may express narrows**, which is the safe direction. `--model haiku` remains
|
|
86
|
+
the OPERATOR's probe knob (PRD v1.36), untouched in `scripts/run-u.mjs`; two comments there called
|
|
87
|
+
the runner's tier map *"the same closed menu the planner uses"* and now say plainly that it is
|
|
88
|
+
deliberately wider.
|
|
89
|
+
- **No spec hash moved.** `jobSpecHash` covers the resolved spec, not `STEP_MODELS`; all 10 job specs
|
|
90
|
+
were re-hashed and none moved — bareagent-u and bareguard-u are still the hashes hamr signed.
|
|
91
|
+
- Measured on the first sonnet-only run against the last haiku-era one, same patient: **10.9 → 17.5
|
|
92
|
+
seconds per round** (1.6× slower), 120 → 87 rounds, and the failure CLASS changed — struck out at 2
|
|
93
|
+
remaining errors with money and clock left, versus still converging at 1 error when the wall
|
|
94
|
+
expired. Better rounds are slower rounds, and the binding constraint moved from the strike ladder to
|
|
95
|
+
the clock. That sonnet-only run was also the first to reach the outer close, where it was redded for
|
|
96
|
+
**silencing the type checker instead of typing the code** (F87's suppression genre) — the close
|
|
97
|
+
refused it, and the eventual green removed the suppressions.
|
|
98
|
+
- **`bareloop.context.md` named `sonnet | haiku` in two places and is corrected** — the shipped adopter
|
|
99
|
+
contract carried the old menu in the `steps[].model` schema row and in the `providerFor` (P) section,
|
|
100
|
+
so an integrating agent reading it would have drafted a plan that reds. Both now name the one-entry
|
|
101
|
+
menu and say why it is one entry.
|
|
102
|
+
|
|
103
|
+
### Fixed
|
|
104
|
+
- **A locked-VERDICT `request-red` no longer files as a bare-agent upstream ask** (`src/job.js`,
|
|
105
|
+
`src/ledger.js`). `classifyIncidents` hardcoded `lib: 'bare-agent'` for every `request-red`, but the
|
|
106
|
+
code covers two territories: a locked TOOL verb (bare-agent, the original case) and a locked VERDICT
|
|
107
|
+
type (`soft-green`/`hitl`) — demand against bareloop's OWN `VERDICT_TYPES` menu, live-misfiled for
|
|
108
|
+
every `LOCKED_VERDICTS` declaration (the BA-2 misattribution class). Per the typed-lib rule the
|
|
109
|
+
territory is now stamped at the EMIT site (`Red` gains `lib?`; `verdictType` → `bareloop`, `tools` →
|
|
110
|
+
`bare-agent`) and the ledger reads it, keeping the hardcode only as the pre-field legacy-spine
|
|
111
|
+
fallback (zero archived rows carry the code, verified). The `suggestedAsk` template renders the
|
|
112
|
+
stamped target, so a bareloop-catalogue refusal can never seed a bare-agent ask; D13's genre refusals
|
|
113
|
+
(close-authoring design record) route by stamping their own lib with no ledger change. 5 new
|
|
114
|
+
failing-first tests; `capability-gap` still hardcodes bare-agent — flagged, structurally unreachable
|
|
115
|
+
today (a validation red returns before any provider call; the module header says so).
|
|
116
|
+
- **…and `capability-gap` no longer hardcodes it either — the flag above is CLOSED** (`src/ledger.js`).
|
|
117
|
+
`capability-gap` is `request-red`'s cap-halted form and inherits both of its territories, but the fix
|
|
118
|
+
landed at one of the two sites and not the other: the synthesis loop still wrote `'bare-agent'`
|
|
119
|
+
literally, and so did its `suggestedAsk` template — the one field a human actually files from, where
|
|
120
|
+
fixing only the occurrence's `lib` would have left the misattribution fully intact. The territory now
|
|
121
|
+
comes from the OCCURRENCE (the `request-red` rows already carry the lib stamped at their emit site),
|
|
122
|
+
and the **dedup key becomes `(verb, lib)` rather than the verb alone**: the ledger's own occurrence
|
|
123
|
+
key carries `lib`, so a `Set` over verbs would merge two territories into one row and attribute it to
|
|
124
|
+
whichever the set happened to hold first — an arbitrary target, which is the defect itself. One gap
|
|
125
|
+
per (territory, verb) costs nothing and each carries an honest one. **Reachability is unchanged and
|
|
126
|
+
still DORMANT** — `validateJob` returns not-ok on any red and `run.js` returns on a validation red, so
|
|
127
|
+
a `request-red` and a `cap-halt` cannot share one spine today; this is a latent-consistency fix at a
|
|
128
|
+
seam the module header documents as dormant, and it claims no live bug. 3 new failing-first tests,
|
|
129
|
+
each labelled in-file as a FUNCTION-CONTRACT fixture (`classifyIncidents` is pure) so a later reader
|
|
130
|
+
cannot mistake the synthetic stream for something a run can produce. Closed now rather than left
|
|
131
|
+
flagged because a fix that lands at one of two identical sites is the class this repo has already
|
|
132
|
+
paid for once — the ripgrep install that went into `ci.yml` and not `publish.yml`, which then failed
|
|
133
|
+
on the identical cause.
|
|
134
|
+
- **The replan ceiling reset on EVERY resume leg — a kill refilled a bound the operator signed**
|
|
135
|
+
(`src/planrun.js`, `src/run.js`, `src/reuse.js`, `scripts/run-u.mjs`). `replanned`/`varianceGrantUsed`
|
|
136
|
+
were locals in `runPlan`, and a resume is another `runPlan` call. Confirmed by execution before any
|
|
137
|
+
remedy: leg 1 replanned and stopped; leg 2, driven with a `resumeSeed`, replanned again — a run total
|
|
138
|
+
of 2 with every record showing 1. Two became four by being killed once, which is precisely the creep
|
|
139
|
+
the latch exists to make impossible (*"unlimited replanning launders thrash as adaptation"*, PRD
|
|
140
|
+
v1.12).
|
|
141
|
+
- **Fixed on the MONEY-FOLD precedent, not the grade-seed one, and the choice is the fix.**
|
|
142
|
+
`readGradeSeed`'s documented known limit is that it reads ONE spine, so a resume of a resume
|
|
143
|
+
inherits leg 2's grades and not leg 1's — the chain shortens by a leg per kill. That is fail-safe
|
|
144
|
+
for a READOUT (a short chain can only under-claim a direction) and it is the DANGEROUS direction
|
|
145
|
+
for a CEILING, where an under-claimed ledger is a refilled allowance. So each leg DECLARES the
|
|
146
|
+
ledger it inherited on its own spine (`priorSpentUsd`'s mechanism) and the next reader adds only
|
|
147
|
+
its own window's `replan` records. Leg 3 inherits the whole chain, not leg 2's slice.
|
|
148
|
+
- **The counter-argument was answered, not stepped over.** `src/trend.js` deliberately refuses to
|
|
149
|
+
seed the fix loop's ITERATIONS from history, and that stays right: an attempt allowance is a LEG
|
|
150
|
+
bound, and a restarted leg buys its own attempts with its own money. A replan ceiling bounds how
|
|
151
|
+
many times the WORKFLOW may be redrawn, which is the run's span — the same span the halt readout
|
|
152
|
+
already covers. The two seeds disagree on purpose and both now say so in-source.
|
|
153
|
+
- **SPINE FIELDS — `job-start` gains `priorReplans` / `priorReplanGrantUsed`.** Emitted only when
|
|
154
|
+
there is a fold (a decorative `0` is indistinguishable from a run nobody folded), on the same
|
|
155
|
+
declaration `priorSpentUsd`/`priorWallMs` already ride. The grant latch travels BESIDE the count
|
|
156
|
+
rather than being derived from it: a leg can inherit a spent ordinary ceiling with the arbiter's
|
|
157
|
+
extra still unearned (`1`, `false`), and deriving one from the other would collapse two real states
|
|
158
|
+
into one. `readResume`'s `direct` mode reads the fold off these very fields, so a U-path run
|
|
159
|
+
resumed twice inherits leg 1.
|
|
160
|
+
- **SPINE MEANING CHANGE — `plan-executed.replans` counts the CHAIN, not the leg** (and `replanned`
|
|
161
|
+
with it). **An adopter parsing spines must re-read this field.** The change was made rather than
|
|
162
|
+
shadowed by a second field precisely because `replans` shipped only on this unreleased branch, so
|
|
163
|
+
no archived spine carries the leg reading for a reader to be confused by. The leg's own number is
|
|
164
|
+
not lost: a leg emits one `replan` record per replan it drafts, so `chain − records-in-this-window`
|
|
165
|
+
is the fold it inherited — the same arithmetic `readResume` runs — and a second field stating a
|
|
166
|
+
derivable number would be a second reader of one question.
|
|
167
|
+
- **New option: `runJob(spec, { resumeReplans })`** (and `runPlan`'s identically-named option),
|
|
168
|
+
`{count, grantUsed} | null`, defaulting to `null` = the cold path, byte-identical to a run nobody
|
|
169
|
+
resumed. Belted like every declared fold entering the arbiter's arithmetic: a garbage, negative or
|
|
170
|
+
non-finite seed reads as `0` rather than poisoning later comparisons as `NaN`, and a negative one
|
|
171
|
+
can never cancel a real replan and widen the ceiling. `scripts/run-u.mjs --resume` and `runReuse`
|
|
172
|
+
both pass it. **The disarm control holds** — a resumed leg with an UNSPENT ceiling still replans, so
|
|
173
|
+
the bound is bounded and not switched off.
|
|
174
|
+
- **The replan brief's `closeGapBlock` re-bounded an ALREADY-bounded gap and deleted the red names the
|
|
175
|
+
first envelope had rescued** (`src/planrun.js`, `src/exits.js`). `src/exits.js` states the rule for
|
|
176
|
+
this same artifact one seam earlier: the check gap arrives already enveloped by `runClose`'s
|
|
177
|
+
`boundGap` under the stage's own `gapKeep`, which deliberately rescues the `not ok`/`FAILED` names
|
|
178
|
+
out of the elided middle — the mechanical gap F46's conversion mechanism feeds the worker. A second
|
|
179
|
+
400/1500 envelope deleted exactly those a second time, which is F28 reintroduced; measured on a
|
|
180
|
+
120-red-line close output, the first bound rescued **78** names and the second dropped **12** of
|
|
181
|
+
them. `CHECK_GAP_MAX` (12,000) is now **exported** from `src/exits.js` and imported here rather than
|
|
182
|
+
respelled, so the two seams cannot drift on how big a check gap may be, and it is applied as a
|
|
183
|
+
**backstop, not an envelope**: under it the gap passes through VERBATIM. The headroom is measured,
|
|
184
|
+
which is why the ceiling is not smaller — `runClose`'s own envelope tops out at ~10.1KB (400 head +
|
|
185
|
+
8192 keep + 1500 tail + labels), and joined with a `tree-changed` detail that is ~10.2KB, leaving
|
|
186
|
+
~1.8KB to spare, so the shipped path is untouched and the backstop fires only where `src/exits.js`
|
|
187
|
+
says it should: a seam that returned something never bounded at all. Length is measured AFTER the
|
|
188
|
+
secret scrub, because a mask is wider than most literals it replaces and bounding the raw string
|
|
189
|
+
would let a gap cross the ceiling on its way through the one transformation that always runs.
|
|
190
|
+
- **The replan brief's block was LABELLED as the close's own output but could lead with ours**
|
|
191
|
+
(`src/planrun.js`). `res.gap` is `lastGap` — the join of EVERY failing exit's detail in exit order —
|
|
192
|
+
and with `MAX_EXITS_PER_STEP` at 2 plus the mandatory `tree-changed` pairing on a write step, a step
|
|
193
|
+
that wrote nothing opens the block with the EVALUATOR's own prose (`0 files changed under src/** …`)
|
|
194
|
+
under a label reading *"the verification's own output"*. Captured verbatim from a real replan prompt.
|
|
195
|
+
The **label** is corrected to *"What this step's exits reported on its last attempt (their own
|
|
196
|
+
output, verbatim)"*; the **payload is deliberately kept** — "the step wrote nothing" is exactly what
|
|
197
|
+
a replanner should be told, and stripping it to make an old label true would trade a real fact for a
|
|
198
|
+
tidy sentence. F86's thesis (the artifact travels as TEXT, never parsed) is restated, not weakened.
|
|
199
|
+
- **F49's ReDoS reject now covers the OPERATOR's two regex fields, not just the agent's**
|
|
200
|
+
(`src/job.js`, `src/validate.js`, `src/plan.js`). `hasNestedQuantifier` shipped wired at exactly ONE
|
|
201
|
+
site — the agent-authored `artifact-written.pattern`. Two operator-authored patterns reach an
|
|
202
|
+
equally untimed evaluator and were uncovered: `judged.pattern`, exec'd by `runClose` against the
|
|
203
|
+
close's whole stdout+stderr, and `gapKeep`, compiled by `boundGap` and run **once per line** of
|
|
204
|
+
close output. Both now red `invalid-value` at the validation gate, before any tokens burn.
|
|
205
|
+
- **Measured, not argued:** `boundGap(stream, '(a+)+$')` against a 40-character line does not finish
|
|
206
|
+
in 20 seconds (`timeout` exit 124).
|
|
207
|
+
- **Why this is worse than an operator self-DoS, and F67 is the reason.** On the agent side F49's
|
|
208
|
+
own scope note holds — the agent authors both the pattern and the artifact, so a hang burns only
|
|
209
|
+
its own wall-clock and compromises no arbiter. These two run in **bareloop's own process**, so the
|
|
210
|
+
hang blocks the MAIN EVENT LOOP — and the in-process stall fuse is a timer in that same loop, so
|
|
211
|
+
it cannot fire (*a guard living inside the process it guards shares that process's fate*). The run
|
|
212
|
+
then dies to the OUTSIDE watchdog and reads as a model stall: **a bad regex in a SIGNED spec
|
|
213
|
+
presents as a provider problem.** The cost is a misdiagnosis, not just a stop.
|
|
214
|
+
- **The detector MOVED to `src/validate.js` — one inventory, never a second copy.** `plan.js`
|
|
215
|
+
imports `job.js`, so `job.js` cannot import `plan.js`; housing the scan with the other shared
|
|
216
|
+
primitives is what lets both documents reject through the same function. `plan.js` re-exports the
|
|
217
|
+
name (the `WRITE_VERBS` precedent), so the F49 site is byte-identical in behaviour. A second copy
|
|
218
|
+
would be precisely the drift class `SECRET_PATTERNS` exists to prevent — a shape one validator
|
|
219
|
+
rejects and the other admits. `hasNestedQuantifier` is not exported from `src/index.js`, so the
|
|
220
|
+
move is internal and breaks no adopter.
|
|
221
|
+
- **Admissibility swept before the change and re-run after:** all **92** operator regexes across all
|
|
222
|
+
**10** signed specs in `jobs/` — **zero** newly red. No spec breaks, no spec-hash churn (no spec
|
|
223
|
+
file is touched). The shipped shapes are pinned in the suite as the control that can produce the
|
|
224
|
+
negative: if the reject ever reds one of them, it has broken the live fleet.
|
|
225
|
+
- **Known limit, PARKED not chased:** the scan misses the alternation-overlap class (`(a|aa)+$`,
|
|
226
|
+
`(x|xy)*$` — no inner quantifier exists to find; the blowup comes from overlapping branches under
|
|
227
|
+
a repeat). Widening it changes which SIGNED specs are admissible, which is arbiter-adjacent.
|
|
228
|
+
Docked to the close-authoring rung, where hand-authored operator regexes may cease to be a surface
|
|
229
|
+
at all — see **PRD v1.55**. Recorded there too, so it is not re-chased: `([^])*$` is NOT caught and
|
|
230
|
+
that is CORRECT (`[^]` is the any-char idiom, so the pattern is linear); the genuinely nested
|
|
231
|
+
`([^]*)*$` IS caught.
|
|
232
|
+
|
|
233
|
+
### Added
|
|
234
|
+
- **A SECOND variance stop can earn ONE more replan — granted by the ARBITER, on a mechanical
|
|
235
|
+
reading** (F85, C). A second `step-variance` after the one-replan ceiling was spent used to be a
|
|
236
|
+
hard stop, and on a run that was still converging that stop threw away work already paid for. Every
|
|
237
|
+
clause of the grant is a constraint: **ONE**, bounded by a LATCH rather than a comparison so the
|
|
238
|
+
ceiling cannot creep by arithmetic (unlimited replanning launders thrash as adaptation, v1.12) — a
|
|
239
|
+
third variance stop is the stop, however well it is going; **the arbiter's**, read mechanically off
|
|
240
|
+
the same `runTrend` instance the meter and the money halt read, with the agent given no channel to
|
|
241
|
+
it — it never asks, is never offered it, and cannot influence it (no self-adjusted budgets, ever);
|
|
242
|
+
**`converging` only**, the trend instrument's own category with no fresh number invented here
|
|
243
|
+
(threshold-setting is the operator's), so `flat` and `unknown` stop exactly as before and an unknown
|
|
244
|
+
is never rounded up into a reason to spend (F6); and **`step-variance` only** — an exhaustion or a
|
|
245
|
+
stall after the ceiling is unchanged. `remainingUsd() > MONEY_MIN` still gates it, as it gates the
|
|
246
|
+
ordinary replan. **Spine change:** `replan` gains `replan` (which replan this is) and — only when the
|
|
247
|
+
arbiter granted one past the ordinary ceiling — `granted` with the reading it was granted on, so a
|
|
248
|
+
reader can never mistake the ceiling for a grant; `plan-executed` gains a `replans` count riding
|
|
249
|
+
BESIDE the shipped `replanned` boolean, never repurposing it. Live on `u-mshcpdg4`: three
|
|
250
|
+
`plan-accepted` records — the initial draft and two replans, the second granted on `converging`.
|
|
251
|
+
- **`closeGapBlock(gap)` — the replan brief carries the close's OWN output** (F86; `src/planrun.js`).
|
|
252
|
+
`u-mshcpdg4` reached ONE remaining strict error and still died. The close gap named the work exactly
|
|
253
|
+
— `src/recurse.js(978,115)` — and the worker read it every attempt; the REPLANNER, which is the
|
|
254
|
+
component that chooses which files the next plan targets, saw only
|
|
255
|
+
*"still progressing — typecheck 30 → 15 → 15 → 1"*: a number with no address. It aimed the last plan
|
|
256
|
+
at `src/loop.js`, already at zero errors, wrote nothing twice, and took two strikes — with $1.10 and
|
|
257
|
+
82 seconds still on the clock at the moment that replan was granted (the run itself ended $0.8174 and
|
|
258
|
+
~6 seconds short of its caps). F85 gave the brief the trajectory; this gives it the artifact the trajectory
|
|
259
|
+
is a summary OF. Handed over as **TEXT**, with both boundaries reused rather than respelled: scrubbed
|
|
260
|
+
through the one secret inventory (`redactSecrets`/`SECRET_PATTERNS`) because a prompt is an egress
|
|
261
|
+
point and the next caller will not remember the upstream scrub, and bounded by ralph's own `boundGap`
|
|
262
|
+
— now **exported** from `src/ralph.js` for it, since a private slice would be a second truncation
|
|
263
|
+
scheme that drifts on exactly the property that matters (whether the red lines survive, and whether
|
|
264
|
+
the trim announces itself, F28); the close path still calls it with the stage's own `gapKeep`,
|
|
265
|
+
byte-identically. The red-set is `REPLAN_GAP_KEEP = '\S'` — every non-blank line — because a shipped
|
|
266
|
+
`gapKeep` is `^`-anchored (`^BAREAGENT `, `^red`, `^FAILED`) and the exit evaluator wraps stage output
|
|
267
|
+
in `check "x" red: …`, so the anchor no longer sits where the pattern expects it; a gap that reaches
|
|
268
|
+
here is ALREADY a red-set (`judge` builds it out of the failing exits only), so keeping every line is
|
|
269
|
+
the correct instrument, not a widened one. **No spine field** — `src/trend.js`'s rule is that no
|
|
270
|
+
record carries a close byte. An empty gap yields the empty string, so the brief renders
|
|
271
|
+
byte-identically to the pre-F86 one (a labelled empty section would invite the planner to explain an
|
|
272
|
+
absence the run never observed). Verified by execution, not by reading: sabotaging the wiring
|
|
273
|
+
(`gapBlock = ''`) turns the `u-mshcpdg4` regression test red (`not ok 121`); restored byte-clean.
|
|
274
|
+
|
|
275
|
+
### Removed
|
|
276
|
+
- **`gapFilesNeverWritten()` and the `never wrote` replan advisory** (F86) — an EXPORT of
|
|
277
|
+
`src/planrun.js` is gone (it was never re-exported through the package entry, whose `exports` map
|
|
278
|
+
admits only `.`, so no adopter import breaks). hamr's order, verbatim: *"delete it and rerun
|
|
279
|
+
bareagent"*. The measured reason, on `u-mshcpdg4`'s own gap: the advisory resolved to
|
|
280
|
+
`src/loop.js` — the file already at zero errors — and said so as a DIRECTIVE, beside the artifact
|
|
281
|
+
that said the opposite; this worker follows directive prose (positive-scope confinement). It also had
|
|
282
|
+
no evidence of ever converting anything: it fired by construction on `u-msdsmkid`, and that run still
|
|
283
|
+
step-redded. The general rule it broke is why `closeGapBlock` hands over text instead: **a parsed
|
|
284
|
+
file list cannot tell a summary line from a detail line**. Line 1 of that very gap reads
|
|
285
|
+
`reports 1 error(s) in src/recurse.js, src/loop.js` — every file in SCOPE — and only line 2 names the
|
|
286
|
+
culprit; and the shapes differ per close anyway (tsc `file:line`, pytest test ids, a count close that
|
|
287
|
+
names no file at all). A model reading the artifact can make that distinction; a regex reproduces the
|
|
288
|
+
bug. Two readers of one question with the parsed one wrong is the same failure the variance meter just
|
|
289
|
+
had. Its 4 tests went with it, and its sole caller was the brief — no speculative code left behind.
|
|
290
|
+
|
|
291
|
+
## [0.7.0] — 2026-08-05
|
|
292
|
+
|
|
293
|
+
### Changed
|
|
294
|
+
- **`ralph()` now REFUSES a call with no exhaustion rule** (public API). `capRuns` became
|
|
295
|
+
optional in the signature when the `ladder` governor arrived — *"required unless `ladder` is
|
|
296
|
+
supplied"* — and nothing enforced the "unless". With neither, `1 <= undefined` is false: the
|
|
297
|
+
middle ran **zero** times and the call still returned `escalated` after emitting `cap-halt`
|
|
298
|
+
and an escalation reading `undefined/undefined runs spent, close still red` — a governance
|
|
299
|
+
stop narrated for a loop that was never entered. A param guard at entry now throws, naming
|
|
300
|
+
both alternatives (either one repairs the call). `capRuns` must be a positive integer;
|
|
301
|
+
`0`, `2.5`, `'3'`, `null` and `NaN` are refused the same way. **No shipped path changes** —
|
|
302
|
+
both in-library callers (the step loop and the close-fix loop) pass a `ladder`.
|
|
303
|
+
- **ONE progress instrument for both governance halts.** The wall halt used to run its own
|
|
304
|
+
reader — byte equality of the last two close gaps, reporting `stalled` / `moving` / `unknown`
|
|
305
|
+
beside the money halt's `flat` / `converging` / `unknown` on the same run. Two readers for one
|
|
306
|
+
question is how the two come to disagree, and the byte reader could only ever report MOTION —
|
|
307
|
+
that something changed, never which way. Both halts now read the run's own `src/trend.js`
|
|
308
|
+
reader. Where a stage reported a comparable NUMBER it decides (so a wall halt on a converging
|
|
309
|
+
run now says `converging`, with the series, instead of "it moved"); where none did, the byte
|
|
310
|
+
comparison survives INSIDE `unknown` as a new `motion` field (`changed | unchanged | null`)
|
|
311
|
+
and as a clause in the `reading`, with the lever adapting (`unchanged` → revise the goal;
|
|
312
|
+
`changed` → read the last close output). **Never promoted to a direction and never mapped onto
|
|
313
|
+
`trend`** — "the close's output changed" is not "the run got better" (F6 in a trend's coat).
|
|
314
|
+
W-2's semantics are untouched: the grade already minted is kept, the three levers are the same
|
|
315
|
+
three, and the close is still never bounded by the wall. **Spine change:** the `wall-halt`
|
|
316
|
+
record's `trend` now carries the money halt's vocabulary and a sibling `motion` field; no
|
|
317
|
+
`src/` or `scripts/` reader consumed the old values (grepped).
|
|
318
|
+
|
|
319
|
+
### Added
|
|
320
|
+
- **A MONEY halt is decision-ready, exactly as W-2 made the wall one** (PRD v1.46 §2). A run
|
|
321
|
+
cut by its budget KEEPS the verdict already minted and emits a new `money-halt` spine record
|
|
322
|
+
carrying `budgetUsd`, `remainingUsd`, the kept `verdict`/`stage`, a progress `trend`
|
|
323
|
+
(`converging | flat | unknown`) with the `reading` and `series` it judged, and three
|
|
324
|
+
`options`: top up and resume, revise the spec, abandon. Emitted at every site that cuts a run
|
|
325
|
+
on money — the step loop, the close-fix loop, and the scout/draft relay. **The library only
|
|
326
|
+
reports**: `budgetUsd` is in the spec hash, so a top-up is a spec edit a human signs, and
|
|
327
|
+
nothing in a run may widen its own budget (unchanged hard line).
|
|
328
|
+
- **The close TREND instrument** (`src/trend.js`, internal). Reads a run's own close grades as
|
|
329
|
+
a series **per stage**, compared only against that stage's own best so far. Accuracy law:
|
|
330
|
+
never across stages — merging a staged close's axes reads a real see-saw (suppressions
|
|
331
|
+
12 → 5 while the type errors that scrub re-exposed went 2 → 0) as a regression. Never model
|
|
332
|
+
prose: the reading is the first number on the first red-marked line of the stage's output.
|
|
333
|
+
A stage that reports no number donates nothing — not a zero, not a strike, and the verdict
|
|
334
|
+
says `unknown` out loud (F6). Reaching a later stage than ever before counts as progress
|
|
335
|
+
(a staged close is first-red-wins). Known accepted limit, documented at the site: a
|
|
336
|
+
floor-shaped stage (`N tests executed, below the seed's M`) reads as not-improving rather
|
|
337
|
+
than converging — the fail-safe direction.
|
|
338
|
+
- **A resumed run's trend judges the WHOLE chain, not just the leg.** `readResume`'s `restart`
|
|
339
|
+
gains `grades`: the dead leg's close grades in order, as `[{stage, value}]` — counts and stage
|
|
340
|
+
names only, never a gap byte. `runJob`/`runPlan` take them as `resumeGrades` (beside
|
|
341
|
+
`resumeSeed`) and `createTrend` takes them as `seed`, folding them into the run trend's series
|
|
342
|
+
and per-stage bests. Without it a resumed leg restarts the trend at its own first close and can
|
|
343
|
+
report `flat` — *revise the goal* — on a run that was converging when its allowance ran out; a
|
|
344
|
+
leg that re-grades an unchanged tree is flat on its own evidence while the RUN is what the
|
|
345
|
+
top-up decision is about. The seed is history, not this leg's evidence, so it counts no
|
|
346
|
+
iteration, mints no strike, never sets `comparableEver` on its own (the blind-cap backstop
|
|
347
|
+
still governs a leg whose own readings never compare) and donates no stage position. The
|
|
348
|
+
close-fix loop's governor is deliberately **not** seeded — it answers the leg-local question
|
|
349
|
+
("is this loop out of ideas"), and seeding it rendered "still progressing" inside the terminal
|
|
350
|
+
of a loop that had just struck out flat. Grades are read from the `ladder` records the live
|
|
351
|
+
governor already wrote (`governor: 'close-trend'`, verbatim, never re-parsed), from
|
|
352
|
+
`close-precheck`/`outer-close`, and — only on a spine predating that governor — from the
|
|
353
|
+
`close-verdict` records after the `fix-loop` marker, which is the same population and excludes
|
|
354
|
+
a STEP's failing exit. Known limit: one spine is read, so a resume of a resume inherits the
|
|
355
|
+
previous leg's chain and not the one before it. `scripts/run-u.mjs --resume` wires it and names
|
|
356
|
+
the inherited baselines in its preview.
|
|
357
|
+
- **`--resume` on `scripts/run-u.mjs`** (PRD v1.46 §3) — the third leg of the resume rulings.
|
|
358
|
+
It skips the patient reset (`resumeTreeGate` instead: a dirty tree is what a resume expects;
|
|
359
|
+
only a moved HEAD stops it), folds the halted run's money and wall in so the signed ceiling
|
|
360
|
+
cannot widen by being re-invoked, re-enters at the checkpoint the steps already earned, and
|
|
361
|
+
arms the outside watchdog on the REMAINING wall. `readResume` gains two options, both OFF by
|
|
362
|
+
default so the reuse loop is byte-unchanged: `direct` reads a plain `runJob` spine as one
|
|
363
|
+
implicit try, and `resumableOutcomes` reclassifies a landed `job-end` that is a GOVERNANCE
|
|
364
|
+
HALT (`cap-halt`/`wall-halt`) as a checkpoint rather than a graded row. Green and every red
|
|
365
|
+
stay non-resumable — a verdict already rendered is never re-bought. `job-start` now carries
|
|
366
|
+
the declared fold (`priorSpentUsd` with its `priorSpendComplete`, and `priorWallMs`) when
|
|
367
|
+
there is one, so a chain of resumes adds only each attempt's new rounds instead of
|
|
368
|
+
re-deriving and double-billing.
|
|
369
|
+
**Validated live and pinned by cycle tests (F83).** Live: a signed top-up resumed a real
|
|
370
|
+
money cap-halt at its close checkpoint — finished step skipped, no re-scout or re-draft,
|
|
371
|
+
patient continued, watchdog armed on the REMAINDER — and greened, with `job-end` stating
|
|
372
|
+
the folded total across both legs. Pinned deterministically by five `$0` scripted-round
|
|
373
|
+
tests covering the whole cycle: leg-1 halt → top-up → leg-2 green at the checkpoint (a
|
|
374
|
+
close-precheck red proving the green was earned, not inherited); a **no-top-up control**
|
|
375
|
+
where the same budget buys no second pass and leaves a byte-identical tree (one
|
|
376
|
+
round-boundary overshoot round, the documented behaviour of a cap that binds BETWEEN
|
|
377
|
+
rounds); an **unsolvable cycle** where a topped-up leg making no per-stage progress
|
|
378
|
+
strikes out `flat` with the wallet still largely unburned — the fix loop's strike rule
|
|
379
|
+
governs a resumed leg exactly as it governs a cold one; and an F6 floor surviving halt →
|
|
380
|
+
fold → green terminal.
|
|
381
|
+
- **Layer 3, the REUSE rung — the registry, the load gate, and the MECHANICAL START**
|
|
382
|
+
(design record `docs/plans/2026-08-01-layer-3-reuse-design.md`; execution probe green
|
|
383
|
+
before any of it was built). A green's plan is now an artifact the next run of the same
|
|
384
|
+
SHAPE can start from, instead of being discarded.
|
|
385
|
+
- **`src/bridges.js` — the registry (D1/D6).** A directory of plain JSON files at an
|
|
386
|
+
OPERATOR-supplied path (no database, no new dependency, no default location).
|
|
387
|
+
`mintBridge` / `appendGreen` / `appendRed` / `saveBridge` / `loadBridge` /
|
|
388
|
+
`loadRegistry` / `makeRegistry` / `registryExists` / `validateBridge` / `listingRow` /
|
|
389
|
+
`deriveStatus`, all exported. **Only a green writes the box (R1):** a red writes a
|
|
390
|
+
history row and never touches `versions`. **Status is DERIVED, never stored (D6):**
|
|
391
|
+
candidate = one green, proven = greens on two DISTINCT patients, a red on a proven
|
|
392
|
+
entry demotes it — and a CASUALTY is not a red. There is deliberately no probability
|
|
393
|
+
score. Costs and durations are a number or an EXPLICIT `null` with the key required
|
|
394
|
+
either way, so an unknown is said rather than omitted (F6).
|
|
395
|
+
- **`loadGate(bridge, job)` — D2 as SPLIT (2026-08-01 addendum).** Three shape checks at
|
|
396
|
+
the door — verdict type, close-stage kinds, verbs within the signed menu — and
|
|
397
|
+
**nothing about paths, scopes or targets**: those are instance-bound and expected to
|
|
398
|
+
red, which the pre-probe measured the drafter fixing unaided (3/3 legal after tweak),
|
|
399
|
+
while the gate as originally frozen would have refused every cross-patient recipe.
|
|
400
|
+
- **`bridge?` on `runJob` and `runPlan` — the mechanical start (D4).** With a bridge, the
|
|
401
|
+
gate runs before the clock, the close precheck and any token; on a pass the NEWEST
|
|
402
|
+
version's plan-as-executed becomes the drafter's STARTING DRAFT inside the ordinary
|
|
403
|
+
drafting prompt (`planPrompt` gained an optional `startingDraft` argument). The tweaked
|
|
404
|
+
plan then passes the SAME `validatePlan` every cold draft passes — **no second, looser
|
|
405
|
+
path exists for an inherited plan** — and the ONE-replan ceiling is untouched (D5): a
|
|
406
|
+
replan drafts from the run's own state and never re-injects the bridge.
|
|
407
|
+
- **New terminal `recipe-stale`** (`runJob`/`runPlan`): a bridge that fails the load gate
|
|
408
|
+
is refused at the door with `bridge-gate {outcome, name, reds}` on the spine, a
|
|
409
|
+
decision-ready escalation, and **zero spend**. Falling back to cold drafting is the
|
|
410
|
+
CALLER's decision, never an automatic silent fallback. Classified as a deliberate
|
|
411
|
+
ledger exclusion — the gate refusing a wrong-KIND recipe is the mechanism working, not
|
|
412
|
+
a library bug to file upstream.
|
|
413
|
+
- **New spine events:** `bridge-gate` (a refusal, with its reds) and `bridge-loaded`
|
|
414
|
+
(`{name, versions, runid}` — which version inherited).
|
|
415
|
+
- **`src/selection.js` — D3's display half.** `renderListing(registry)` renders the
|
|
416
|
+
operator/LLM-facing workflow listing (name, goal, status, greens/reds, the cost/time
|
|
417
|
+
band of the greens), deterministic by name and total on garbage; an unknown cost or
|
|
418
|
+
time renders `UNKNOWN` and a partial aggregate says how many it skipped, and registry
|
|
419
|
+
entries that could not be read are NAMED so the listing is never quietly shorter than
|
|
420
|
+
the directory. `selectionPrompt(listing, ask)` is the pure prompt text for the pick,
|
|
421
|
+
carrying D3's two standing rules: *"none matches"* is a first-class answer that means
|
|
422
|
+
drafting new, and a PINNED workflow may be refused only EXPLICITLY, with a reason. Both
|
|
423
|
+
are pure text — the selection call, the pin/shortlist/force-cold flow and the parse of
|
|
424
|
+
the answer belong to the caller.
|
|
425
|
+
- **The cold path is byte-identical without a bridge** (the F47 works-both-ways rule),
|
|
426
|
+
pinned by test: no new event fires and no starting draft appears anywhere in the prompt.
|
|
427
|
+
- **`src/reuse.js` — the D7 ENVELOPE and the REUSE RUNNER.** hamr's sentence made
|
|
428
|
+
executable: *"we ask user for cost/time and how many workflows to try before starting
|
|
429
|
+
new like $5 and 30 mins x2 then start anew if both red"*.
|
|
430
|
+
- **`validateEnvelope(input, { job? })`** — `{ perTryBudgetUsd, perTryWallMs,
|
|
431
|
+
bridgeTries }`, **all three required and explicit, no defaults** (a defaulted cap is
|
|
432
|
+
a silent second ceiling); `bridgeTries: 0` = force-cold. The envelope may only
|
|
433
|
+
**TIGHTEN** the signed spec — a per-try number above `job.budgetUsd`/`job.maxWallMs`
|
|
434
|
+
is `envelope-widens`, never a silent raise.
|
|
435
|
+
- **`resolveTrySpec(job, envelope)`** — the per-try spec every try runs, cold leg
|
|
436
|
+
included. Tightening moves the SPEC HASH, so a tightened run is a new spec VERSION
|
|
437
|
+
the operator signs; `runReuse` passes `approvals` straight through and cannot forge
|
|
438
|
+
one. An envelope equal to the spec's numbers is hash-identical.
|
|
439
|
+
- **`selectBridge({registry, job, ask, provider, pinned?, shortlist?, forceCold?,
|
|
440
|
+
exclude?})`** — ONE model call on the caller's drafter-tier provider (none is
|
|
441
|
+
constructed inside), parsed through the repo's one `extractArtifact` into a strict
|
|
442
|
+
`{choice, reason}`. `forceCold` and an empty candidate set skip the call for $0. A
|
|
443
|
+
name outside the listing is a red, never used. **A pin does not bypass the call**
|
|
444
|
+
(D3): it is stated in the prompt, and an answer naming anything else returns
|
|
445
|
+
`{refused: true}` — the substitute is NOT adopted and the decision goes back to the
|
|
446
|
+
operator. Cost metered and reported (F6).
|
|
447
|
+
- **`runReuse({job, approvals, registryDir, envelope, patient, workdir, provider,
|
|
448
|
+
emit, …})`** — selection → try → box → next try → cold. Selection re-reads the
|
|
449
|
+
registry per try with the tried names excluded; **a green ENDS the loop**; tries
|
|
450
|
+
exhausted falls through to a cold run under the same per-try numbers, whose green
|
|
451
|
+
mints a new bridge for the job slug (or appends if that name already holds greens —
|
|
452
|
+
clobbering a green is what R1 exists to prevent), and whose red writes nothing (the
|
|
453
|
+
entry bar is a green). Every non-green return is decision-ready. The three answers
|
|
454
|
+
**no further try could change** — `unapproved-spec` (a tightened envelope is a new
|
|
455
|
+
spec version, signed not inherited; the decision hands over the exact hash),
|
|
456
|
+
`job-red`, `smoke-red` — end the run where they happen instead of reproducing
|
|
457
|
+
themselves down the whole envelope. A cold green also REFUSES to mint over a file of
|
|
458
|
+
that name it could not read (`mint-collision`): an unreadable entry is not an absent
|
|
459
|
+
one, and whatever greens it held are gone once overwritten.
|
|
460
|
+
- **`REUSE_GRADED_RED`** — only `escalated` demotes: the terminal where the close
|
|
461
|
+
judged the tree, the fix loop spent its attempts with money still on the table, and
|
|
462
|
+
the close was still red. Every other non-green outcome is a CASUALTY recorded under
|
|
463
|
+
its own name (`cap-halt`/`wall-halt` governance, `provider-red`/`step-stalled`
|
|
464
|
+
transport, `close-red` the close FAULTING and rendering no judgment, `recipe-stale`
|
|
465
|
+
refused at the door, `plan-red`/`step-red:*`/`check-red` stopping before the close
|
|
466
|
+
ever judged). Full detail still lands on the history row.
|
|
467
|
+
- **F45 made visible instead of thresholded.** A try's budget must fund the attempt
|
|
468
|
+
PLUS its close, and nothing in-process can know what a close costs — so **no
|
|
469
|
+
threshold is invented** (threshold-setting is arbiter territory, from a measured
|
|
470
|
+
base rate). Instead every try row carries `spentUsd` vs `capUsd`, `wallMs` vs
|
|
471
|
+
`wallCapMs`, `capBound`, `wallBound` and **`closeReached`**: a cap that died before
|
|
472
|
+
any grading is a fact on the row, not an inference.
|
|
473
|
+
- **New spine events:** `reuse-start`, `selection-result`, `try-start`, `try-end`,
|
|
474
|
+
`bridge-write`, `reuse-end`, plus `envelope-red`. **New escalation categories**
|
|
475
|
+
`reuse-exhausted` / `selection-refused` / `selection-red` / `registry-red` /
|
|
476
|
+
`envelope-red`, all added to the ledger's executable excluded-set — the envelope and
|
|
477
|
+
the operator speaking, never a library failing (a real transport fault out of the
|
|
478
|
+
selection call still classifies as `provider-red`).
|
|
479
|
+
- **`selectionPrompt(listing, ask, pinned?)`** gained the optional pin argument, so the
|
|
480
|
+
pin sentence has ONE spelling rather than being assembled by each caller.
|
|
481
|
+
- **`scripts/run-reuse.mjs`** — the reference operator runner: `--job` / `--registry` /
|
|
482
|
+
`--budget` / `--wall` / `--tries` (+ `--pin` / `--force-cold`), approval gate on the
|
|
483
|
+
RESOLVED per-try hash, seed refusal (it never resets the patient for you), F67 outside
|
|
484
|
+
watchdog sized for the SUM of the tries, gate-audit relocation, secrets scan, and a
|
|
485
|
+
sleep-inhibitor note in the header (F72 — a suspend freezes every guard, including the
|
|
486
|
+
watchdog).
|
|
487
|
+
- **RESUME AFTER A KILL (module C).** A killed reuse run comes back losing as little as
|
|
488
|
+
possible — hamr's ruling: *"money, signature and checkpoint (starts from where it
|
|
489
|
+
stopped) if mid loop, restart that loop"*.
|
|
490
|
+
- **`readResume(events, {deathAt?})`** turns a dead run's OWN spine back into the
|
|
491
|
+
state a resume continues from: which tries completed (never re-run, bridges still
|
|
492
|
+
spoken for), whether a try was mid-flight (a `try-start` with no `try-end` — never
|
|
493
|
+
graded, so never consumed), what that attempt already spent in money and wall, and
|
|
494
|
+
which signed envelope the spine belongs to. Spend is summed from `worker-round`
|
|
495
|
+
ONLY — the same event `runJob`'s ledger accounts — so a selection call's cost is
|
|
496
|
+
never attributed to a worker (F45); a restarted try DECLARES its inherited fold on
|
|
497
|
+
its own `try-start`, so a resume of a resume folds once rather than double-billing
|
|
498
|
+
an abandoned attempt. A try whose `job-end` landed is COMPLETE, not restarted, and
|
|
499
|
+
a registry row lost to the kill is named (`r1Missing`), never re-derived.
|
|
500
|
+
- **The remainder, never a fresh allotment.** `runJob` gained `priorSpentUsd` /
|
|
501
|
+
`priorSpendComplete` / `priorWallMs` and `createClock` gained `priorElapsedMs`: the
|
|
502
|
+
killed attempt's spend and time FOLD IN, so the restart runs under what is left of the
|
|
503
|
+
SIGNED per-try numbers. The caps themselves are never rewritten — they are in the spec
|
|
504
|
+
hash, and editing them would need a signature nobody typed. An unfundable remainder caps
|
|
505
|
+
honestly (`cap-halt`, or `wall-halt` below one close timeout) having launched
|
|
506
|
+
nothing. **A floor stays a floor across the resume:** if any round inside the dead
|
|
507
|
+
attempt came back unpriced, `priorSpendComplete: false` rides one-way onto every
|
|
508
|
+
`job-end` this run emits, so a resumed attempt states a floor instead of an
|
|
509
|
+
exact-looking total (F6 — it was previously computed and recorded on `try-start` but
|
|
510
|
+
never reached the component whose terminal says so). The restart fold reads ALL four
|
|
511
|
+
of `runJob`'s floor causes: a landed resumable `job-end` carrying
|
|
512
|
+
`spendComplete: false` (a stall, or a call cut mid-flight) floors the fold exactly
|
|
513
|
+
like a declared prior floor or an unpriced round (F83's laundering finding, fixed;
|
|
514
|
+
the no-`job-end` killed-mid-flight restart is control-pinned unchanged).
|
|
515
|
+
- **`runReuse({… resume})`** seeds the completed tries, restarts the mid-flight one
|
|
516
|
+
against the SAME workflow with no second selection call, and carries on into
|
|
517
|
+
whatever the envelope still authorizes. It refuses (`resume-red`, ledger-excluded)
|
|
518
|
+
a seed signed under a different envelope, or a restart whose workflow has left the
|
|
519
|
+
registry. New spine event `resume-start`; `try-start` gains the declared fold.
|
|
520
|
+
- **`resumeTreeGate({head, seed, dirty})`** — a resumed patient is CONTINUED, never
|
|
521
|
+
reset: the dead tries' edits are real progress. Only HEAD moving off the seed (a
|
|
522
|
+
commit or rebase under the dead run — operator intervention) stops it.
|
|
523
|
+
- **`makeSpine(file, {startSeq})`** so a resumed process continues one append-only
|
|
524
|
+
log with `seq` still monotonic, and **`scripts/run-reuse.mjs --resume <runid|path>`**:
|
|
525
|
+
it refuses a spine that is missing, corrupt mid-file, already terminal, or whose
|
|
526
|
+
process is STILL ALIVE (pid from the run's new `runner-start` record and the
|
|
527
|
+
watchdog report, discriminated by `/proc/<pid>/cmdline` because pids are recycled),
|
|
528
|
+
prints both hashes on an envelope mismatch, previews the whole reconstruction
|
|
529
|
+
before you sign, archives the killed run's watchdog record so it cannot be read as
|
|
530
|
+
this run's, and sizes the watchdog for the REMAINING work.
|
|
531
|
+
|
|
532
|
+
### Changed
|
|
533
|
+
- **`capRuns` retires as the CLOSE-FIX loop's governor** (PRD v1.46 §4). That loop now ends on
|
|
534
|
+
the same 2-strike no-progress rule the step ladder uses, read off the close's own per-stage
|
|
535
|
+
numbers rather than repeats and writes. Validated by a $0 replay over every archived fix
|
|
536
|
+
loop before the build: 0 greens harmed (all three historical fix-loop greens converted in
|
|
537
|
+
≤ 2 verdicts) and 1 real waste case caught (dead flat at 2 errors for 7 consecutive fix
|
|
538
|
+
verdicts until the wall killed it). `capRuns` survives ONLY as the bound for a close whose
|
|
539
|
+
output carries no number at all — a governor that cannot see the variable must not be the
|
|
540
|
+
governor — and lifts the moment a stage reports a comparable number. Money and the wall keep
|
|
541
|
+
every bit of their authority. The exhaustion terminal is unchanged in both category
|
|
542
|
+
(`cap-halt`) and outcome (`escalated`); `ralph`'s `ladder` option is now an INTERFACE two
|
|
543
|
+
governors implement, with an optional `terminal()` that overrides the exhaustion prose only
|
|
544
|
+
(the step ladder's copy offers a replan and there is no planner at the close). Every `ladder`
|
|
545
|
+
spine record now names its `governor` (`step-ladder` | `close-trend`). The wall-halt readout
|
|
546
|
+
no longer quotes `capRuns` as a denominator, because it no longer governs.
|
|
547
|
+
- **A plan step is bounded by PROGRESS, not by a count** (`src/ladder.js`; F77–F79). The step
|
|
548
|
+
loop's fixed iteration cap is replaced by a strike ladder: a *strike* is a red iteration that
|
|
549
|
+
repeats an already-seen gap (matched against a seen-set for the whole step, never just the
|
|
550
|
+
last one) or made no gate-audit writes (the F32 record-count instrument — never `git status`,
|
|
551
|
+
never a tree diff); strikes are sticky and `strikeLimit` of them (new shell option on
|
|
552
|
+
`runJob`/`runPlan`, **default 2**) ends the step. Gaps are normalized for comparison only —
|
|
553
|
+
timing bytes dropped, counts and file names kept — so a shrinking error count reads as
|
|
554
|
+
progress. The wallet, the wall, the variance meter and the stall fuse keep every bit of their
|
|
555
|
+
authority; the exhaustion terminal is the same `cap-halt`. Measured motivation: two
|
|
556
|
+
calibrations died `step-red` while still converging with money and wall unspent; a $0 replay
|
|
557
|
+
over 110 archived step ladders showed every converging red continuing and thrash ending
|
|
558
|
+
same-or-one-earlier. **`capRuns` now bounds the CLOSE-FIX loop only.** New per-iteration spine
|
|
559
|
+
record `ladder`; exhaustion records carry `strikes`/`strikeLimit`/`distinctGaps`.
|
|
560
|
+
- **The replan brief names the MECHANISM.** An exhausted step now tells the redrafting planner
|
|
561
|
+
which shape ended it (converging-cut / stalled-no-write / repeated-gap, with iteration numbers
|
|
562
|
+
as evidence), its gap trajectory, and how much money and wall the stop left unspent (time
|
|
563
|
+
reported UNBOUNDED when no wall was set, never `0`) — replacing *"it ran N attempts and its
|
|
564
|
+
exits were still red"*, which named no mechanism and read identically for opposite failures.
|
|
565
|
+
- **Layer R's VERBATIM ratchet stage is retired on the STEP path** (hamr's ruling): fixation is
|
|
566
|
+
a repeat, so a fixated step strikes out before the fourth iteration can open. The stage stays
|
|
567
|
+
reachable in the close-fix loop and fully unit-covered; Layer R still ships OFF by default.
|
|
568
|
+
|
|
569
|
+
### Deprecated
|
|
570
|
+
- **`steps[].attempts` is retired and INERT.** It is no longer offered in the drafting prompt
|
|
571
|
+
and the runner ignores it. It is still ACCEPTED by `validatePlan` — stored bridge plans carry
|
|
572
|
+
it, and a plan minted under one runner's number must not red under another's — with the shape
|
|
573
|
+
still checked and the old `<= capRuns` ceiling dropped. It was TIGHTEN-only, which made the
|
|
574
|
+
correct heal for a converging step inexpressible. `validatePlan`'s `capRuns?` option is
|
|
575
|
+
removed with the ceiling it fed (it existed only to bound `attempts`; an extra property is
|
|
576
|
+
ignored, so no direct caller breaks).
|
|
577
|
+
|
|
578
|
+
### Changed (the close-stage AXIS SPLIT — hamr: "#1 fix")
|
|
579
|
+
- **One POPULATION per close stage, so unlike numbers can never share a trend series.** The
|
|
580
|
+
trend reads one bucket per stage NAME, and a stage name is not an axis: `typecheck` red
|
|
581
|
+
either `N error(s) in <scope>` (the in-scope faults) or `the target files are clean but M
|
|
582
|
+
strict error(s) exist outside them` — a different population, reached only once the first is
|
|
583
|
+
zero — and `suite-green` mixed a failure count with an executed-count floor. A run crossing
|
|
584
|
+
either seam donated two genres to one series, so a 29 → 4 across it read **converging** and
|
|
585
|
+
would have recommended a top-up on work that had only swapped which wall it was behind.
|
|
586
|
+
Every mixed stage in the six `u-*` closes is now split: **`typecheck-outside`** carries the
|
|
587
|
+
outside-scope population immediately after `typecheck`, and **`tests-kept`** carries the
|
|
588
|
+
executed-count floor immediately before `suite-green`. Each new stage sits exactly where its
|
|
589
|
+
branch already ran, so the gate sequence is byte-order identical to the old single-stage
|
|
590
|
+
walk and first-red-wins is unchanged; `litectx`/`spawner` `typecheck` was verified
|
|
591
|
+
single-population and left alone. `src/trend.js`'s KNOWN LIMIT is rewritten as the historical
|
|
592
|
+
record plus **the law for close authors** — the remedy for a mixed stage is that close's stage
|
|
593
|
+
list, never a detector taught to tell two prose shapes apart (the F49 precedent).
|
|
594
|
+
- **All six `u-*` spec hashes flip**, by construction: a close's stage names are in the
|
|
595
|
+
signed spec, so the runner refuses every one of them until it is re-signed. That is
|
|
596
|
+
refuse-until-re-signed working, not a migration to smooth over.
|
|
597
|
+
- **Stored bridges whose `closeStageNames` predate the change refuse to load** — the load
|
|
598
|
+
gate's close-stage check is the doctrine that a changed close is a different KIND of job.
|
|
599
|
+
`aurora-u-spawner` and `litectx-u` are in that state now and are re-minted by a green under
|
|
600
|
+
the new close, never edited into agreement.
|
|
601
|
+
- **The F28 trim announcement is restored** where the split surfaced its loss: the
|
|
602
|
+
outside-scope branch pre-sliced its own echo, silently suppressing the "trimmed" notice a
|
|
603
|
+
gap bound must announce (a 341-line elide is visible again).
|
|
604
|
+
- **New `tests/close-stages.test.js` (20 tests)**, including a can-it-fail pre-flight against
|
|
605
|
+
the pre-split tree, spec↔script stage-list agreement over spawned real closes, and the
|
|
606
|
+
29 → 4 repro reading as two stages with a same-stage contrast proving the test can fail.
|
|
607
|
+
Live `$0` validation: pulselog green across all four split stages, baremobile
|
|
608
|
+
`typecheck-outside` RED at 381 reading correctly as its own series. **1007 tests.**
|
|
609
|
+
|
|
610
|
+
### Fixed (whole-branch review, 2026-08-05 — 4 opus finders over `main...layer-3-reuse`; F84)
|
|
611
|
+
|
|
612
|
+
*Every confirmed finding fixed, each verified against source before it was believed; no fix
|
|
613
|
+
forced, and three finder claims corrected in detail during the fixing.*
|
|
614
|
+
|
|
615
|
+
- **CRITICAL — the close trend's BLIND CAP was a one-way latch.** `capRuns` survives as the
|
|
616
|
+
fallback for a close whose output carries no comparable number, and it was armed by
|
|
617
|
+
"has this leg ever compared anything" — which a bare STAGE ADVANCE satisfies, on
|
|
618
|
+
essentially every run that does any work at all. One advance therefore disarmed the
|
|
619
|
+
fallback **permanently**: a later run of numberless reds (the shipped aurora TESTGEN
|
|
620
|
+
`verdict` stage is exactly that shape) accrued neither a strike nor a blind tick and was
|
|
621
|
+
bounded by money and the wall alone, where the retired count would have stopped it.
|
|
622
|
+
Reproduced by execution before the change — 27 iterations, no terminal. The cap now bounds
|
|
623
|
+
the **consecutive uncomparable streak**: it lifts the moment the instrument can read and
|
|
624
|
+
**re-arms the moment it cannot**, binding a recovered run never and a blind-from-birth run
|
|
625
|
+
on exactly the iteration it always did (pinned byte-stable by test, at every cap). The
|
|
626
|
+
`report()` `blind` flag follows the same question — a live streak IS blindness, so the
|
|
627
|
+
terminal no longer offers "0/2 strikes" as the reason a loop it did not strike out was
|
|
628
|
+
stopped.
|
|
629
|
+
- **`priorFloor` no longer requires money to have been spent.** `runJob` derived it as
|
|
630
|
+
`spentUsd > 0 && priorSpendComplete === false`, so a resumed leg inheriting a fold of **$0
|
|
631
|
+
that was not exact** — every round of the dead attempt unpriced — came back reading as an
|
|
632
|
+
exact zero. A floor OF zero is still a floor (F6). Read off the flag alone now, and the
|
|
633
|
+
onward `job-start` declaration is emitted for that case too (`spentUsd > 0 || priorFloor`),
|
|
634
|
+
which is the only field that can carry the unknown forward.
|
|
635
|
+
- **A reuse RESUME's halt readout now spans the chain.** `restart.grades` was computed by
|
|
636
|
+
`readResume`, declared on the record, and then handed to nobody: only `scripts/run-u.mjs`
|
|
637
|
+
passed `resumeGrades` on. `runReuse`'s own per-try execution now threads it into `runJob`,
|
|
638
|
+
so a restarted leg's `money-halt`/`wall-halt` trend judges the RUN rather than restarting at
|
|
639
|
+
this leg's first close and reporting `flat` — *revise the goal* — on a run that was
|
|
640
|
+
converging when its allowance ran out. `resume-start`'s `restart` states
|
|
641
|
+
`gradesInherited` (a COUNT; the seam carries stage names and numbers, never a close byte),
|
|
642
|
+
and only when there is one — the cold path is unchanged.
|
|
643
|
+
- **The `money-halt` record carries `spendComplete`.** `remainingUsd` is `budget − spent`, so
|
|
644
|
+
a run whose spend is a FLOOR has a remaining that is a CEILING; quoting it to four decimals
|
|
645
|
+
read as the number. `runPlan` takes the ledger's own `spendComplete` (one spelling of the
|
|
646
|
+
four causes, never a second arithmetic) and states it beside the figure — the same duty
|
|
647
|
+
`wall-halt` has carried since W-2.
|
|
648
|
+
- **A try row's ROUNDS fold across a resume, like its money and its wall.** A restarted try
|
|
649
|
+
reported only the restart's turns against a span its `$` already covered both legs of.
|
|
650
|
+
`try-start` declares `priorRounds` (including when it is `null`: absence means a spine older
|
|
651
|
+
than the field, an explicit null is a predecessor that could not count its own turns — and
|
|
652
|
+
an unfoldable count stays `null` rather than becoming the half this leg can see, F6). The
|
|
653
|
+
inherited-graded row's `wallMs` is likewise measured to the try's **own terminal** now, not
|
|
654
|
+
to a kill that may have landed minutes later.
|
|
655
|
+
- **The replan brief no longer calls an IDLE strike-out "converging".** The sentence was gated
|
|
656
|
+
on repeats alone, so a step that wrote nothing in any iteration was handed *"no file was
|
|
657
|
+
written in iteration(s) 1, 2"* and *"the step was converging"* in the same paragraph — to
|
|
658
|
+
the one channel the redrafting planner adapts to. Both signals gate it now; a moving exit
|
|
659
|
+
output on a step that wrote nothing is a close re-running, not work converging.
|
|
660
|
+
- **`planPrompt`'s JSDoc was detached from `planPrompt`.** An intervening helper landed between
|
|
661
|
+
the block and the function, so every parameter shipped as `any` in the generated `.d.ts`.
|
|
662
|
+
Reattached; no behaviour change, and the types are the contract.
|
|
663
|
+
- **W-2 on the LAUNCH side: a zero wall remainder now REFUSES, loudly.** `--wall-ms 0` reached
|
|
664
|
+
`scripts/u-watchdog.mjs`, which defaulted a non-positive value to `null` and armed **no
|
|
665
|
+
deadline at all** while the runner's own banner still advertised one — a guard reading zero
|
|
666
|
+
and saying nothing. The watchdog now refuses any PRESENT-but-unreadable numeric flag
|
|
667
|
+
(`0`, negative, garbage) with exit 2, while an OMITTED `--wall-ms` stays the legal unbounded
|
|
668
|
+
choice with the stale trigger armed. Both runners refuse above the spawn, before the key and
|
|
669
|
+
before the patient: `scripts/run-u.mjs` on an exhausted resume remainder, `scripts/run-reuse.mjs`
|
|
670
|
+
on `plannedWallMs <= 0`. **Two zeros, two levers** — a restart whose wall is burned wants
|
|
671
|
+
`--wall` (NO WALL LEFT), a run whose authorized attempts have all run wants `--tries` or
|
|
672
|
+
nothing (NOTHING TO ARM) — because naming the wrong lever sends the operator to re-sign a
|
|
673
|
+
number that buys no attempt. Both are in the approval hash, so either is a new signature.
|
|
674
|
+
- **`scripts/run-reuse.mjs` hardening.** A corrupt/truncated watchdog report can no longer abort
|
|
675
|
+
a signed resume at the last gate (it is parsed once, defensively, and reused; the archive
|
|
676
|
+
suffix falls back to the ARCHIVE moment so a second unreadable report cannot overwrite the
|
|
677
|
+
first). `runner-start`'s `argv` is redacted **at the write site** with the shipped
|
|
678
|
+
`redactSecrets` inventory — an append-only log that captures a key captures it forever, so an
|
|
679
|
+
after-the-fact whole-file scan is too late. A trailing value-flag (`--tries` with nothing
|
|
680
|
+
after it) now refuses instead of reading `Number('') === 0` and silently reshaping the run
|
|
681
|
+
being signed. The header's deliberate-differences list gains the fourth: the end-of-run leak
|
|
682
|
+
scan is a READOUT here, not a gate, because `runReuse` writes registry rows mid-run.
|
|
683
|
+
- **Archived runners annotated** (`reuse-preprobe`, `run-calibration-testgen`, `run-poc-layer2`,
|
|
684
|
+
`run-probe-testgen`): their `CAP_RUNS` comments described the retired drafting-prompt
|
|
685
|
+
interpolation, not the close-fix loop's blind fallback it actually is now.
|
|
686
|
+
|
|
687
|
+
- **Tests: 987 total** (from 959). The mutation-proven hole: a widening of `REUSE_GRADED_RED` — the
|
|
688
|
+
set whose members DEMOTE a proven bridge — survived the entire suite, so the demotion table
|
|
689
|
+
is now pinned outcome by outcome (`escalated` demotes; `step-red`/`cap-halt`/`wall-halt`/
|
|
690
|
+
`provider-red`/`close-red`/`step-stalled`/`pricing-red` are casualties and proven stays
|
|
691
|
+
proven). Plus the blind-cap backstop battery (blind-from-birth pin, streak-not-birth,
|
|
692
|
+
never-fires-while-reading, recovery resets), the launch-side refusals at all three sites,
|
|
693
|
+
the resume grade/round folds with their absent-baseline controls, the money-halt floor
|
|
694
|
+
readout, and a first battery for `scripts/migrate-bridge-patient-slugs.mjs` (dry-run
|
|
695
|
+
byte-identity, whole-file refusal over half-migration, name/filename disagreement — 4
|
|
696
|
+
mutants killed).
|
|
697
|
+
|
|
8
698
|
## [0.6.0] — 2026-07-31
|
|
9
699
|
|
|
10
700
|
### Added
|