bareloop 0.11.0 → 0.13.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 CHANGED
@@ -5,6 +5,258 @@ 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.13.0] — 2026-08-23
9
+
10
+ ### Fixed (2026-08-23 — the release round: a pointer that lied, a close with no home, a rule that charged for its own refusal)
11
+
12
+ - **The pointer no longer lies about a file emptied since it was read.** `src/readshim.js`'s
13
+ pointer branch tested `start >= total` alone, so `0 >= 0` held with **no ledger entry at all** —
14
+ the first read of an empty file and a re-read of a file TRUNCATED TO EMPTY since delivery were
15
+ indistinguishable, and both answered "you already hold all 0 bytes". A worker would have gone on
16
+ believing in content that was gone. The guard now requires a ledger entry whose hash still
17
+ matches. This is precisely the untruthful-pointer class the module exists to prevent, found
18
+ inside the module built to prevent it, and reproduced live before the fix.
19
+ - **`judge-round` carries its rate provenance.** The judged floor made it the first spend a CLOSE
20
+ has ever had, and `src/ledger.js` documented the write side as forwarding `rateSource` onto it —
21
+ but the emit never spread the field, so every judge round would have read UNKNOWN forever. NOT
22
+ rounded up: `src/kinds.js`'s `onJudgeCost` payload still does not carry the field, so it reads
23
+ UNKNOWN today, and both the emit and the ledger header now say so plainly.
24
+ - **A torn spine line stops reading as exact spend.** `scripts/run-battery-readshim.mjs` marked
25
+ unparseable lines `__corrupt` "never guessed at", then counted them, dropped them, and reported
26
+ the row's spend as EXACT while `corruptLines` reached no readout. `spendComplete` now goes false
27
+ when any line was unreadable (spend renders `>=`) and the audit prints the count per row.
28
+ - **G1's ceiling half is refused at $0 instead of after the drafter is paid.** `validatePlan` reds
29
+ `read-blind` when a capped step grants `read` without the retrieval pair — but a step cannot
30
+ grant what the SIGNED CEILING does not offer, so against a spec whose `tools` lack `recall`/`get`
31
+ **every** draft red identically and the drafter was paid for each doomed cycle. `runPlan` now
32
+ throws at the same $0 door as the arm guard, in the same class: an operator param-guard
33
+ `TypeError`, never a model-output red — nothing the agent authored is wrong when the operator
34
+ asks for a shim the signature cannot satisfy. Tighten-only; the only configuration it rejects
35
+ already failed 100% of the time, just later and for money. `RETRIEVAL_PAIR` is now exported from
36
+ `src/plan.js` rather than respelled, so the rule and its pre-flight cannot drift apart.
37
+
38
+ ### Changed (2026-08-23)
39
+
40
+ - **The aurora close moved to a tree development cannot mutate** (`jobs/aurora-u-spawner-types.json`,
41
+ F110). Its stages had pointed at a scratch worktree, which was removed during ordinary branch
42
+ work — every stage silently became `node: cannot find module`. The home before that was a shared
43
+ working checkout, where a close landing mid-edit dies as an instrument fault rather than a
44
+ verdict. Neither is a home: a checkout is *supposed* to go dirty and a scratch worktree is
45
+ *supposed* to be removable. **A close is the arbiter's instrument, so its host must have the
46
+ arbiter's lifetime.** Now a worktree pinned at a release tag, never edited, with its own
47
+ dependencies. Re-signing was required and given in-turn.
48
+
49
+ ### Added (2026-08-18 — the read shim, flag-gated OFF)
50
+
51
+ - **`src/readshim.js` — a per-worker delivery ledger over the read tool**, off unless the new
52
+ `readShim` flag is set on `runJob`/`runPlan`. A read over 24KB (`READ_SHIM_CAP`) delivers its
53
+ next unseen slice plus a trusted steer at `ctx_recall`/`ctx_get`; a re-read of a file the
54
+ worker holds WHOLE and unchanged returns a short pointer instead of the bytes; a re-read of
55
+ a file it holds only PART of returns the next slice, never a pointer; any content change
56
+ re-delivers from the start. Measured on a $0 replay of 1,844 real `shell_read` actions from
57
+ 143 archived gate audits: 48% of reads re-read unchanged bytes, and cap+pointer keyed on what
58
+ was DELIVERED saved 65.6% of read payload with **zero** untruthful responses — where the
59
+ naive path+hash key saved 8.2 points more by telling **250 lies**, median 73,348 bytes hidden
60
+ each (BA-17 read-blinding, reproduced).
61
+ - **G1 at the validation gate (`read-blind`)**: under the shim, a plan step granting `read` must
62
+ also grant `recall` and `get`. Capping a worker with no retrieval verb is BA-17 on purpose —
63
+ the cap is what makes the pair mandatory. Stated in the drafting prompt as well as enforced
64
+ (the mailbox precedent).
65
+ - **One flag, four ARMS, default OFF** (the `layerRoot` precedent, F41). `readShim` names which
66
+ levers run, one value per row of the frozen Phase 2 pre-registration: `false` = A0 (off),
67
+ `'cap'` = A1 (cap + pointer + next-unseen-slice + G1), `'diff'` = A2 (the diff lever alone — no
68
+ cap, no pointer, no G1), `true` = A3 (every lever). The two booleans mean exactly what they
69
+ meant before the arms existed, so nothing written against them changed meaning. With the shim
70
+ off every observable — validator reds, persona, delivered bytes — is byte-identical to the
71
+ pre-shim run: the frozen A0 arm has to be exactly today, and a guard firing under a disabled
72
+ shim would make the baseline a treatment arm. Each arm's persona line describes only the
73
+ machinery actually installed (A2 is never told about a 24KB limit that is not in force). An
74
+ unrecognised value THROWS at the entry (`readShimArm`) rather than coercing into a truthy
75
+ shim — a mis-spelled arm would run one treatment under another's label and be invisible in the
76
+ results afterwards. The default-flip is not in this change: it waits on a paid contrast.
77
+ - **A stale `ctx_get` pointer serves the requested line range from disk** (2026-08-19), under a
78
+ CAPPING arm only. The cap's own strategy line steers a capped worker at `ctx_recall` →
79
+ `ctx_get` for a whole function; `ctx_get` is content-hash gated and throws `StalePointerError`
80
+ the moment the file changed after indexing — which is the normal case for a worker that just
81
+ EDITED the file it is working on. Measured on the A1 arm's three green runs: 10 `ctx_get`
82
+ calls, 5 stale, 0 bytes each, every one a just-written file. A paid round returning nothing,
83
+ down a path the cap itself chose. The serve hands back the requested 0-based inclusive line
84
+ range read fresh from disk, under TRUSTED framing that says the pointer was stale, that the
85
+ range may now hold DIFFERENT code than the symbol asked for, which lines arrived, and that
86
+ nothing was recorded as delivered. litectx refuses to slice a drifted range itself because an
87
+ UNLABELLED slice can silently be another symbol's body — the label is what makes it legal.
88
+ Detection is on the exported `StalePointerError` CLASS, never on message prose. A vanished
89
+ file or a range now entirely past the end is a refusal RESULT naming what happened, never a
90
+ throw. It **never touches the delivery ledger**: a slice is not the file, and a ledger that
91
+ recorded one would answer the next read with "you already have it" to a worker holding 90
92
+ lines of 900 — the untruthful-pointer failure the shim exists to prevent. The cost of that is
93
+ a later `shell_read` may re-deliver bytes the serve already showed; the shim over-delivers
94
+ rather than lies. A0 and A2 are untouched: with no capping arm the hook is not wired and
95
+ `ctx_get`'s contract is byte-identical.
96
+ - On the native surface the shim REPLACES the `NATIVE_READ_CAP` wrapper rather than layering over
97
+ it (both bound the same seam at the same 24KB, and a shim outside the native wrapper would
98
+ ledger already-truncated text as a whole file — the exact lie it exists to prevent). L2
99
+ (diff-on-change) is deliberately not built: it never fired on the real corpus.
100
+
101
+ ## [0.12.0] — 2026-08-23
102
+
103
+ ### Added (N4 slice 2 — the softgreen rung: a close that JUDGES, and a signer who has the last word)
104
+
105
+ - **The judged floor is real: LOCATE + DECIDE (`src/judged.js`).** A judge (haiku-4.5, pinned —
106
+ never a step knob) extracts FACTS from the real artifact with quotes; an arbiter-owned `decide()`
107
+ renders pass/fail from a signed rubric card; *unsure* is a red, never a pass. The judge never
108
+ renders a verdict, so the rubric-is-self-consistency gotcha stays closed. Wired as the
109
+ `judged-floor` close kind — metered from the run's own wallet, `offer:false` by law, and skipping
110
+ the seed-verdict read (its bar comes from calibration instead).
111
+ - **`soft-green` is ADMITTED as a verdict class** — the class-scoped question set, the inherited
112
+ green guard battery (hamr: *"battery ties with job type"*), the class-scoped kind menu, and the
113
+ promise rule: composition stays ≤ the picked class, and exceeding it is an honest red rather than
114
+ a silent up- or downgrade.
115
+ - **The signer's two artifacts are AUTHORED and enumerated in the hash** (`src/cardauthor.js`): the
116
+ rubric card comes from Q6 and the calibration set from Q7, both handed to the compiler verbatim,
117
+ both SHOWN to the signer (D5 shown-and-fixed), and what the signer fixed is what is stored —
118
+ `source: 'proposal'` says *nobody changed this*, which is a different fact from *a person read it
119
+ and changed nothing*.
120
+ - **The calibration gate — ten graded truths and five resisted lies buy a signature**
121
+ (`src/calibrate.js`, gate 4 of `prepareSigning`). The whole pipe grades the signer's own ten cases
122
+ and faces a 5-artifact injection battery adapted to the locate axis; the floor is **10/10 with
123
+ itemized reds** (hamr, 2026-08-18) and all-or-nothing on both halves. The $0 legality check runs
124
+ first and buys no calls. A dead judge is a CASUALTY under the transport's name, never a reading of
125
+ the operator's rubric.
126
+ - **Quarantine: a judged green earns no learning credit until an `accept` releases it.** Minted
127
+ soft-green bridge rows are held at mint, the release is forward-only, a rerun re-quarantines, and
128
+ a held entry is never offered by selection — so a soft-green pass cannot feed N3 before the judged
129
+ floor is proven.
130
+ - **The review door — every run ends at the signer's three doors** (`accept` / `rerun` / `pause`).
131
+ The door records a DISPOSITION and never touches the loop's own verdict (hamr: *"it's important
132
+ not to change the loop self verdict"*): `runPlan` returns `green`/`already-green` exactly as
133
+ before. It opens at all three green sites (precheck `already-green`, the outer close, and the fix
134
+ loop's green). **Soft-green always** (`REVIEW_DOOR_CLASSES`), **green opt-in** via `reviewDoor:
135
+ true` / `--review-door` — the flag wins in both directions and is never inferred. An `accept`
136
+ re-runs the close's MECHANICAL stages (never a judged floor, never a person): a door keeps for 60
137
+ days and nothing freezes a tree.
138
+
139
+ - **The judge tier joins the SIGNATURE — a bump flips the hash and the stage refuses the
140
+ mismatch** (`649d8a5`). `JUDGE_MODEL`'s docstring has always said a judge-model bump forces a
141
+ full recalibration, and nothing detected one: `closeDecl.calibration` stored only its `cases`,
142
+ so the tier that graded them sat OUTSIDE the signed bytes, a bump flipped no hash, and the
143
+ judged stage never compared the model it was about to buy against the model the signer's set
144
+ was graded by. A rule with no wired detector is prose (F45). The tier now lives beside the
145
+ cases as **`calibration.judgeModel`** (`CALIBRATION_FIELDS` in `src/declaredclose.js`
146
+ enumerates a stored set as exactly those two and nothing else), written by
147
+ `foldJudgedArtifacts` from the PIN itself rather than from anything a caller may name — and
148
+ because it is inside the spec, `jobSpecHash` covers it by construction: bump the constant,
149
+ every signed judged spec hashes differently, the signature dies, and the re-sign is what
150
+ re-runs the calibration gate. `validateCloseDecl` REQUIRES it on any stored set (a set nobody
151
+ can attribute to a tier is a floor nobody can tell apart from a bumped one).
152
+ `declaredStages` carries the close-level fact onto the judged stage as
153
+ `calibrationJudgeModel` — the same seam, and for the same reason, that already stamps
154
+ `offer: false` — and it carries it from the SIGNED bytes, so a stage can never name a tier the
155
+ signature does not. `runJudgedFloor` then refuses to grade on a mismatch, naming both models
156
+ and the two things that fix it (*re-sign* and *recalibrate*), under the same `FAILED` fault the
157
+ absent judge seam already stops on: nothing is broken, something was never re-done. An ABSENT
158
+ stamp is deliberately NOT a runtime stop — a signed spec cannot reach a run without a
159
+ calibration (the signing gate) or without a model on it (the spec gate), so the only runtime
160
+ question is WHICH model. No signed spec on disk moves: `jobs/*.json` carries zero
161
+ `judged-floor` stages today.
162
+ - **The U runner mints the row its review door promises to release** (`e01d0af`). The door tells
163
+ a person that `accept` *"releases this run's learning credit"*; for every run
164
+ `scripts/run-u.mjs` has driven that sentence was false by construction — a cold green wrote a
165
+ standalone `bridge-<job>-<runid>.json` FILE and never a registry ROW, so
166
+ `--registry --workflow --decide accept` reached `recordDoor`, found no green row for the runid
167
+ and died `no-row-for-run`. The held credit could be described and never released. **STORAGE
168
+ ONLY:** nothing here selects, promotes or reuses a bridge — that rung stays parked on
169
+ `layer-3-reuse` (F88) — and exactly one thing reads the row back, the door. `writeGreenRow` is
170
+ `runReuse`'s own cold-leg green write LIFTED out of its closure unchanged (the
171
+ fork-on-a-different-close-shape rule, both collision refusals, the `appendGreen` leg), so the
172
+ two runners mint a green through ONE spelling; a second spelling of what a green writes is the
173
+ two-transforms class applied to the ledger itself. **`writeRunGreenRow`** (newly exported) is
174
+ the U runner's terminal policy on top of it, and every guard is a NAMED refusal rather than a
175
+ silence: no `--registry` (`no-registry`), an already-green run (`green-predates-run` — accept
176
+ confirms a verdict, it never mints one), a green with no executed plan (`no-plan-executed`), a
177
+ class whose credit is not held (`credit-not-held` — `green`-class runs mint nothing here,
178
+ which is what this runner has always done), and a spine leak blocks the row exactly as it
179
+ already blocks the bridge file. The row is born HELD because `greenParts` reads the SIGNED
180
+ class off the record — nothing new sets a flag — and `applyDoorDecision` releases it through
181
+ the path that already existed. `src/index.js`'s claim that `runReuse` is the only registry
182
+ writer is CORRECTED rather than left standing.
183
+ ### Fixed
184
+
185
+ - **F102 — a pending human decision now survives a wall-halt → resume** (`a6f62c9`). Two records
186
+ answering two different questions: `human-decision` (the door, the words, and by whom, with
187
+ `source: 'operator'|'checkpoint'` and the moment the PERSON spoke) and `human-decision-spent`
188
+ (what actually bought it). The spend marker fires on the **WORK** — a fix round bought, an accept
189
+ that greened, a pause honoured — **not on the fix loop opening**, which is precisely the shape of
190
+ the incident (`iterationsUsed: 0`). A resume carrying one re-enters the fix loop through the same
191
+ seam a fresh answer uses; the ask is never re-rendered. Two answers to one question are REFUSED
192
+ (`hitl-decision-red` naming what is held), never merged.
193
+ - **F103 — a rerun buys its own clock** (`a6f62c9`). Two counters that never do each other's job:
194
+ CHAIN (`chainWallMs`/`chainSpentUsd`, `job-end.spentUsd`) is every leg added up and is what the
195
+ halt readout reports; ENGAGEMENT (`priorElapsedMs`, `job-end.engagementSpentUsd`) is what bounds
196
+ THIS leg. Money is deliberately not symmetric — the signed budget stays the CHAIN's ceiling,
197
+ because a refilled wallet is the failure F45 measured. W-2 is untouched where it was designed for:
198
+ an ordinary resume folds exactly as it always has, and so does an `accept`.
199
+ - **F106 (HIGH, review) — the calibration gate now meets the one ceiling** (`0ae7693`). The run's
200
+ third and largest paid seam (up to 10 locate calls plus the 5-artifact battery, each under its own
201
+ retry ladder) ran with no bound while the scout and the declaration loop were both stopped by
202
+ `run-author --budget` — the advertised budget and the enforced budget were two different numbers.
203
+ `prepareSigning`/`calibrationGate` take `ceilingUsd` + `priorCalls`, a cost book absorbs prior
204
+ spend so the gate reads the REMAINING balance, `capStop` is asked before each paid call inside the
205
+ pipe (retries included), and a ceiling stop returns as an operator refusal naming money
206
+ (`cap-halt` / `pricing-red`) — never a casualty, and never a part-graded set presented as graded.
207
+ - **A review door's rerun is a fresh engagement too** (`0ae7693`): `resolveHumanRuling` takes
208
+ `doorRerun` as a third input, OR'd and never merged, so *"is this leg a fresh engagement"* keeps
209
+ ONE spelling and a door-rerun leg cannot inherit the rejected leg's wall.
210
+ - **`scripts/run-u.mjs` wires `judgeProvider`** (pinned to the judge model, never a step knob) — a
211
+ live soft-green run no longer instrument-stops at the judged stage.
212
+ - **Two mutation-blind gaps in `src/kinds.js` covered by tests** (found by the same review).
213
+
214
+ - **The medium whole-branch code review's five findings** (`e0c87d3`). An absent `job-end` in
215
+ `scripts/run-u.mjs` (a crash between the door record and `job-end`, or a legacy spine) no
216
+ longer launders into an exact complete `$0` that folds into the rerun ceiling — unknown is
217
+ reported unknown (F6), regression test watched failing first. One name per kind and per code:
218
+ the `'judged-floor'` literal at 7 sites becomes **`JUDGED_FLOOR_KIND`** (`src/kinds.js`,
219
+ exported) and the `'quarantined'` code at 3 sites becomes **`QUARANTINED_CODE`**
220
+ (`src/bridges.js` — deliberately not `reuse.js`, which would cycle reuse ↔ selection). One
221
+ `threeDoors()` builder replaces the copy-pasted door lines in `scripts/u-readout.mjs` (output
222
+ byte-identical against HEAD), and a `changedEvidence()` helper in `src/planrun.js` replaces
223
+ the duplicated evidence block and the double `verdictType` default, with `doorOpens`'
224
+ signature untouched. Two findings were ESCALATED rather than decided — the judge-model bump
225
+ with no detector, and the U runner's door promising to release credit it never minted — and
226
+ both are the `1A`/`2B` entries above.
227
+ - **The tail review's six findings over `1A`+`2B`** (`493b2ce`). The door PREVIEW path now
228
+ checks row-EXISTS before promising a credit release, and its printed commands carry
229
+ `--registry`/`--workflow` forward — the `e01d0af` fix had landed at the end-of-run readout and
230
+ not at this sibling site, so the operator's next invocation would have rediscovered
231
+ `no-row-for-run` one hop later. The registry row records **`rounds` + `roundsComplete`**: a
232
+ resume leg writes a fresh spine and the halted leg's restart record declares money and wall
233
+ but never rounds, so on a resume that count is a FLOOR and now says so the way the money
234
+ beside it already does (F6). A NAMED `--registry` that attempted no row prints the reason
235
+ rather than staying silent. The two near-identical record builders in `src/reuse.js`
236
+ consolidate into `commitWrite`, and the duplicated `worker-round` scan is hoisted to one
237
+ spelling.
238
+ - **The release gate's two findings** (`a5a4731`). `docs/UPSTREAM-ASKS.md`'s BA-21 carries a
239
+ dated CORRECTION note: the retracted 2.0–2.5× figure was a clipipe-surface number (F48 — a
240
+ provider-billed surface that never pools with `anthropic-api`), and the measured API-side
241
+ numbers are ~5.7% / 1.586×; the acceptance criteria are unaffected. In `src/judged.js`, two
242
+ literal NUL bytes inside composite-key template strings are spelled `\x00` — byte-identical
243
+ at runtime, but the literal made `grep`/`file` treat the source as binary (the documented git
244
+ binary-detector trap).
245
+
246
+ ### Docs (2026-08-23)
247
+
248
+ - **`docs/FINDINGS.md`: F102 and F103 move from NOT FIXED / NOT BUILT to FIXED**, each cited to
249
+ the commit that did it — `a6f62c9` for both, plus `0ae7693` for F103's extension (the review
250
+ door's rerun as `resolveHumanRuling`'s third input, OR'd and never merged). **F106 is minted**
251
+ from the 3-lens review's HIGH: F45's class in a money coat with the direction reversed — the
252
+ ENFORCED number was the looser one, which is the failure that leaves a bill instead of a red
253
+ (`cce3ce5`).
254
+ - **PRD Addendum v1.76** (this release): the three doors restated, the green-class review door
255
+ kept OPT-IN, rerun named as *not a replay*, the `1A`+`2B` escalations ruled, and the one
256
+ remaining door hole named and left deliberately.
257
+ - **`bareloop.context.md`**: the judge-tier paragraph rewritten from DEFERRED to the live
258
+ detector it now describes.
259
+
8
260
  ## [0.11.0] — 2026-08-18
9
261
 
10
262
  ### Changed (2026-08-18 — the review door's third button)
package/README.md CHANGED
@@ -48,8 +48,9 @@ finds in the real work, and fixed rules — not the judge — render the verdict
48
48
 
49
49
  ## You have the last word
50
50
 
51
- Every run ends at a **review door**: here is what changed, here is what every check said, here
52
- is what I'm asking you. **Accept** it, **rerun** it — what you type is the feedback the next
51
+ Every judged run ends at a **review door** (a purely mechanical green finishes on its own —
52
+ ask for the door with `--review-door`): here is what changed, here is what every check said,
53
+ here is what I'm asking you. **Accept** it, **rerun** it — what you type is the feedback the next
53
54
  attempt works from, funded fresh — or **pause** it and decide another day. A pause costs
54
55
  nothing, keeps everything, and picks up from the start of the last step whenever you come back.
55
56
  Never come back, and it quietly expires on its own.