bareloop 0.9.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 +204 -0
- package/README.md +101 -117
- package/bareloop.context.md +139 -12
- package/package.json +1 -1
- package/src/authorflow.js +189 -22
- package/src/authoring.js +190 -0
- package/src/authorjob.js +11 -3
- package/src/authorscout.js +70 -9
- package/src/bridges.js +14 -1
- package/src/index.js +3 -0
- package/src/kinds.js +102 -11
- package/src/ledger.js +5 -1
- package/src/planrun.js +210 -25
- package/src/ralph.js +5 -0
- package/src/reuse.js +52 -2
- package/src/run.js +2 -1
- package/src/text.js +79 -0
- package/src/tools.js +11 -0
- package/src/validate.js +168 -11
- package/types/authorflow.d.ts +52 -8
- package/types/authoring.d.ts +112 -0
- package/types/authorjob.d.ts +3 -2
- package/types/authorscout.d.ts +16 -3
- package/types/index.d.ts +1 -1
- package/types/kinds.d.ts +58 -6
- package/types/planrun.d.ts +43 -0
- package/types/reuse.d.ts +1 -1
- package/types/text.d.ts +67 -0
- package/types/validate.d.ts +74 -9
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,210 @@ 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.10.0] — 2026-08-13
|
|
9
|
+
|
|
10
|
+
### Added
|
|
11
|
+
|
|
12
|
+
- **The close-AUTHORING pipeline gets a money ceiling** (`--budget`, `ceilingUsd`) — the lever
|
|
13
|
+
parked at v0.9.0 as arbiter territory, approved by hamr. Until now the flow **metered** spend
|
|
14
|
+
and nothing **bounded** it: the runner printed a total and no number anywhere could stop a
|
|
15
|
+
call. It has **NO DEFAULT** — omitting it runs UNBOUNDED and the runner **prints that** before
|
|
16
|
+
the provider is built, because a defaulted cap is a silent second ceiling (the `maxWallMs`
|
|
17
|
+
precedent) and an unbounded run must be a visible operator choice. A malformed value is an
|
|
18
|
+
error, never a silent fall back to unbounded. The ceiling **binds BETWEEN metered calls** —
|
|
19
|
+
the survey's attempts and its F59 recovery round, and the declaration loop's author call,
|
|
20
|
+
each revise, and each malformed-emission retry — because a cap that binds mid-call kills the
|
|
21
|
+
row before it can be graded (F45). ONE number reaches BOTH paid seams, and spend already
|
|
22
|
+
incurred folds in, so re-entering cannot silently widen it. **F6 keeps its own axis**: a
|
|
23
|
+
null/unknown cost never counts as $0, so spend that cannot be *known* stops as `pricing-red`
|
|
24
|
+
rather than passing silently — while known spend at or over the cap is `cap-halt` even when
|
|
25
|
+
the total is unknown, since that breach is certain on the priced half alone. The stop is a
|
|
26
|
+
governance stop, not an error: it names the cap and the spend, nothing retries, partial
|
|
27
|
+
artifacts stay on disk, and it is DISTINCT from `artifact-red` and `provider-red`. A survey
|
|
28
|
+
the ceiling stopped is named a money stop (`budgetStop`, typed cause `not-funded`, outside
|
|
29
|
+
`SCOUT_RETRY_CAUSES`) instead of blaming a model that never spoke. One predicate, one
|
|
30
|
+
spelling (`capStop`/`tallyCalls` in `src/text.js`, beside `priceOf` and for the same reason).
|
|
31
|
+
|
|
32
|
+
- **A resume may DERIVE `wall-halt` from a recorded `step-red`.** Only when that run's OWN
|
|
33
|
+
spine shows the wall crossed BEFORE the terminal was minted — a derivation from the primary
|
|
34
|
+
record, which never rewrites the recorded outcome and announces itself with a banner. The
|
|
35
|
+
terminal's NAME is what gates resume (`readResume`'s `resumableOutcomes`, which the operator
|
|
36
|
+
runner supplies as `cap-halt` / `wall-halt`), so a run mislabelled `step-red` past its own
|
|
37
|
+
wall silently lost its resumability. **It is a PERMANENT read-side safety net, not a
|
|
38
|
+
migration shim** (ruled 2026-08-13; the expiry question that was parked here is closed). The
|
|
39
|
+
defect that minted the shape is fixed at source, but spines are append-only forever, so the
|
|
40
|
+
population it protects can never shrink; it fires only on the run's own un-forgeable
|
|
41
|
+
`wall-bounded` record, cannot rescue a `step-red` that has no wall record in front of it, and
|
|
42
|
+
is a never-taken branch on a healthy spine.
|
|
43
|
+
|
|
44
|
+
### Changed
|
|
45
|
+
|
|
46
|
+
- **The two revise-loop tighten-only clamps get a detector.** `maxRevisions` and
|
|
47
|
+
`structureRetries` have been clamped since `40672ae`, but nothing could *fail* if they
|
|
48
|
+
stopped clamping: MEASURED, three mutations survived the whole suite — including deleting
|
|
49
|
+
the `structureRetries` clamp outright, a straight revert of the fix that added it. The two
|
|
50
|
+
existing tests pinned only the ceiling and the floor, and neither can fail when the clamp
|
|
51
|
+
stops honouring values *between* them; the direction the rule exists to permit — the
|
|
52
|
+
operator **lowering** a bound — was the untested one. Seven tests now cover the middle of
|
|
53
|
+
both ranges, the non-numeric floor, and `Infinity` (a widening, which clamps to the ceiling,
|
|
54
|
+
matching `runAuthorScout`'s own reading). The three floors are pinned as *deliberately
|
|
55
|
+
different* (`SCOUT_ATTEMPTS` at 1, both revise axes at 0) so a later "harmonisation" has to
|
|
56
|
+
argue with a test. No production behaviour changed — this is the detector the rule was
|
|
57
|
+
missing (F45's class: a frozen rule without a wired detector is prose, not protection).
|
|
58
|
+
|
|
59
|
+
- **A stop past the wall never funds a replan draft, on ANY trigger.** W-2's *"no new step
|
|
60
|
+
starts"* now covers the drafting call itself (hamr: *"it's a replan that is doomed to
|
|
61
|
+
die"*), and the guard is trigger-agnostic rather than wired to one path. A declined grant is
|
|
62
|
+
not consumed. With time left on the clock every one of these paths is byte-identical to
|
|
63
|
+
before — pinned as a control.
|
|
64
|
+
- **The drafter is told its worker has no shell.** A replan wrote an action opening *"Run
|
|
65
|
+
`npx tsc --strict --noEmit`…"*; `run` is locked out of `TOOL_MENU` permanently, so the
|
|
66
|
+
worker obeyed as far as its verbs allowed — 25 reads and greps, zero edits, a whole step
|
|
67
|
+
spent hunting six errors by hand, on a plan that validated clean. The no-shell law now sits
|
|
68
|
+
in the DRAFTER prompt unconditionally, beside the exit-freedom law, with `read`/`grep`
|
|
69
|
+
glossed as the worker's only eyes; the scout and worker prompts already carried it, and the
|
|
70
|
+
one component that WRITES instructions did not. **A regex over action prose was explicitly
|
|
71
|
+
refused** (F86's anti-precedent): the same sentence is legitimate in an operator's signed
|
|
72
|
+
goal, so the instrument is a prompt register, never a matcher. F95.
|
|
73
|
+
- **The worker persona REGISTERS the arbiter's books instead of leaving them to be
|
|
74
|
+
discovered.** `PERSONA_TOOLS` now names `gate-audit.jsonl`, `.smoke`, `.litectx` and the
|
|
75
|
+
run's spine as always-denied — records of how the worker is judged, holding nothing about
|
|
76
|
+
its task — beside the absolute-path law it is the twin of, so it renders for every worker on
|
|
77
|
+
every grant (a `write`-only grant gets no component strategy paragraph at all and must still
|
|
78
|
+
be told). **The fence is unchanged**: nothing widened, no book became readable. The only way
|
|
79
|
+
to learn the rule was previously to spend rounds of a bounded attempt on it. F98.
|
|
80
|
+
- **Both signing surfaces show the GOAL and the judged stages as ONE reading.** F87's law is
|
|
81
|
+
that the goal must state everything the close judges while NOTHING derives one from the
|
|
82
|
+
other or checks them against each other — so the sole defence is the signer reading both
|
|
83
|
+
halves at once, and neither surface offered it: `run-author` printed the declaration and
|
|
84
|
+
never the goal, `run-u --approve` printed the goal and never the declaration. `run-u`'s gate
|
|
85
|
+
now names every close stage (name + kind) under the goal, **for BOTH spec forms** — the
|
|
86
|
+
pairing was first gated on `closeDecl`, so 10 of the 11 shipped specs (the command `close[]`
|
|
87
|
+
form) still met the signer with a goal and none of the stages that judge it, the exact
|
|
88
|
+
half-reading this entry claimed to end; `closeStagesOf(spec)` now renders either form, and a
|
|
89
|
+
command stage prints `[command]` rather than the catalogue kind name `command-exit` it does
|
|
90
|
+
not carry (spec hashes and exit codes byte-identical, proven live). `run-author`'s block moves to
|
|
91
|
+
**`scripts/author-readout.mjs`** (`declarationLines(spec)`) and gains the goal above it,
|
|
92
|
+
rendered from the RESOLVED spec — the bytes that get hashed. It moved because the block was
|
|
93
|
+
otherwise reachable only after a paid scout and a paid model call, and a readout no test can
|
|
94
|
+
reach is a readout nothing checks. An absent goal renders as ABSENT, never as a bare label.
|
|
95
|
+
Still a READING and not a validator — comparing the two is the move F87 forbids.
|
|
96
|
+
- **README rewritten value-first** (hamr's framing, straight to `main`). The pitch now leads
|
|
97
|
+
with the trust-but-verify lineage (the ralph loop, code machine MCP, and RLMs converged on
|
|
98
|
+
the same principle), the three kinds of done (green / softgreen / hitl, one everyday
|
|
99
|
+
example each), why the agent authors the workflow (forty-plus primitives — counted 51+
|
|
100
|
+
across the suite's real surfaces — and no one recipe picked), and "build it once, keep it"
|
|
101
|
+
(localhost UI, whole-workflow export with its self-healing harness). One `[WIP]` badge is
|
|
102
|
+
the only status marker; all mechanics, layer tables, findings numbers and rung history
|
|
103
|
+
moved out of the pitch — `bareloop.context.md` stays the contract, the README the pitch.
|
|
104
|
+
- **`step-stalled` joins the RESUMABLE halts** (hamr's go, 2026-08-13). A stall that trips with
|
|
105
|
+
time left and only reaches the replan gate after the wall has expired kept its NAME — `run.js`
|
|
106
|
+
keys the F44 spend floor on that exact outcome, and renaming it would report unknown spend as
|
|
107
|
+
an exact total — but the name is also what `readResume` gates on, so keeping it cost the run
|
|
108
|
+
its checkpoint. A `step-stalled` spine now previews as **RESUME**, folds the spend floor in as
|
|
109
|
+
`>=$x`, and re-enters at the recorded branch and step. A pure widening of the resumable set:
|
|
110
|
+
nothing else about the terminal moved, and `reuse.js`'s D4a path was checked unaffected.
|
|
111
|
+
- **The arbiter-book names get ONE home.** `gate-audit.jsonl`, `.smoke` and `.litectx` were
|
|
112
|
+
spelled independently in `isArbiterBook`, in BOTH fence deny lists, in the `PERSONA_TOOLS`
|
|
113
|
+
prose and in `run.js`'s smoke root — five sites, the exact drift class F98 had just paid for,
|
|
114
|
+
with the prose copy unguardable by any test. `kinds.js` now exports
|
|
115
|
+
`SMOKE_STORE`/`LITECTX_STORE`/`GATE_AUDIT_FILE`/`ARBITER_BOOK_STORES` and all five consume it.
|
|
116
|
+
Purely mechanical and verified as such: the deny-list joins and the persona prose are
|
|
117
|
+
**byte-identical** to the strings they replace, so no fence decision and no worker prompt
|
|
118
|
+
changed, and there is no import cycle (`kinds.js` sits below `tools.js`/`run.js`).
|
|
119
|
+
`tools.test.js`'s literal-spelling guard now asserts the fence consumes the constant — the
|
|
120
|
+
drift it guarded is impossible by construction.
|
|
121
|
+
|
|
122
|
+
### Fixed
|
|
123
|
+
|
|
124
|
+
- **A time-stop is NAMED a time-stop — W-2 closes over the sibling terminals.** The ruling
|
|
125
|
+
(*"when time is up, keep the grade we already have and stop"*) was implemented on the
|
|
126
|
+
cap-halt path and not on the variance path: one run minted `step-red` **9.9 seconds past its
|
|
127
|
+
own wall**, having recorded the crossing in its own spine 5.6 seconds earlier. Now **every
|
|
128
|
+
variance and cap-halt terminal re-reads the clock before minting**, through ONE shared
|
|
129
|
+
`wallHaltTerminal` emission site — one site, one spelling —
|
|
130
|
+
and the ladder's cap-halt past the wall is a `wall-halt`. **`step-stalled` is pinned OUT of
|
|
131
|
+
the class by test:** `run.js` keys `spendComplete:false` on that exact outcome name because
|
|
132
|
+
an abandoned-and-reissued call may already be billed, and relabelling it would report a
|
|
133
|
+
floor as an exact total (F6 in a self-heal coat). **With both allowances exhausted the MONEY
|
|
134
|
+
cut is reported** (`cap-halt` wins over `wall-halt`) — hamr: *"understood"*; recorded so the
|
|
135
|
+
choice stays visible. F96 / PRD v1.59.
|
|
136
|
+
- **A declared `count-not-worse` red gap carries the LINES it counted, not just how many.**
|
|
137
|
+
`"8 match(es)"` with no file, no line and no error code is a number with nowhere to open —
|
|
138
|
+
F28's rule (*the close's output format is part of the contract*) recurring one rung later
|
|
139
|
+
inside the authored-close executor, with the `gapKeep` machinery it needed sitting in the
|
|
140
|
+
same file. `parseValue` now returns `matched`: the **KEPT** lines only — those that survived
|
|
141
|
+
the scope filter and reached aggregation, so a scope-DROPPED line is never named and the
|
|
142
|
+
worker is never aimed outside its own population (F84). The harvest is **per-term**, not
|
|
143
|
+
global: each term's kept lines nest under that term's own breakdown row — the one place its
|
|
144
|
+
`+n`/`-n` already prints — a `first` aggregate echoes only the one line it read, and dedup is
|
|
145
|
+
WITHIN a term, so a line feeding two terms plays both roles and appears under each. Harvested
|
|
146
|
+
globally, a negative-sign term's lines and a `first` term's never-read lines were echoed flat
|
|
147
|
+
as *the lines the count is made of*, handing the worker a line to go fix when that term
|
|
148
|
+
SUBTRACTED; labelling per term rather than dropping negatives keeps the subtracted term
|
|
149
|
+
visible, because under higher-is-better that is where the news is. The value is
|
|
150
|
+
still computed from the parsed values and never from the list. The lines ride the EXISTING
|
|
151
|
+
channel and invent none: the stage's `gapKeep` prefix on every line (Layer R's `redKeep` is
|
|
152
|
+
DERIVED from it), the existing `GAP_LINE_CAP`, and the announced trim on overflow.
|
|
153
|
+
**Nothing here can flip a signed spec hash** — verdict logic, baselines, counting,
|
|
154
|
+
`closeDecl` schema and `detail` untouched. Measured, not asserted: the identical job, same
|
|
155
|
+
patient, same hash, went from stalling at 8 and dying to **67 → 8 → 1 → 0** in three
|
|
156
|
+
iterations. F98.
|
|
157
|
+
- **A fence deny streak ends the ATTEMPT, never the RUN — `gate-red` is extinct as a mintable
|
|
158
|
+
terminal.** A converging run (close reading 46 → 10 → 8, no strikes) probed three of the
|
|
159
|
+
arbiter's own books, the fence denied all three CORRECTLY, bare-agent's BA-11 deny-spin
|
|
160
|
+
guard ended the loop, and the plan flow mapped that to a terminal `gate-red` — escalate,
|
|
161
|
+
never retry. The run died with **$1.77 and 21.6 wall-minutes unspent, on a fence that had
|
|
162
|
+
worked perfectly.** BA-11's stop now joins `max_turns` in the **bounded-attempt lane on BOTH
|
|
163
|
+
worker surfaces**: `attempt-bounded` carries the `reason`, the close judges the partial
|
|
164
|
+
work, the gap feeds forward, caps unchanged, the loop continues. **And the next attempt is
|
|
165
|
+
told which bound actually fired.** All three attempt bounds wrote the same bare iteration
|
|
166
|
+
number, so the following prompt rendered *"CUT OFF after N tool rounds"* for a denial streak —
|
|
167
|
+
false on cause and count, aiming the worker at its read budget instead of at the fence, while
|
|
168
|
+
`r.error` reached only the spine. The bound now carries `{iteration, cause, reason}`, and the
|
|
169
|
+
denial branch quotes the recorded `denied:<tool>` terminal verbatim — bare-agent returns the
|
|
170
|
+
tool name only, so no path and no streak count is invented — trimmed under `GAP_TRIM_MARKER`
|
|
171
|
+
and scrubbed once at capture, one scrub shared by prompt and spine (an append-only spine that
|
|
172
|
+
captures a key captures it forever). The round-bound sentence stays **byte-identical**, pinned
|
|
173
|
+
by block-equality after an `includes` guard survived a sabotage prefix. This is F32's routing rule
|
|
174
|
+
applied to a new stop cause, not a new one — a stop arriving AFTER gate-audited worker
|
|
175
|
+
writes is non-terminal by construction, and a denial is its most benign member. **Nothing is
|
|
176
|
+
widened**: the same actions are denied and the same audit rows written; a genuine scope
|
|
177
|
+
escape now costs one bounded attempt instead of the whole run. The category stays in
|
|
178
|
+
`EXCLUDED_ESCALATIONS` and in `ralph`'s passthrough decision table — that set is EXECUTABLE,
|
|
179
|
+
so dropping the name would re-file any future emission as a counted capability gap rather
|
|
180
|
+
than delete it. F98.
|
|
181
|
+
- **A malformed money ceiling is an ERROR at the library seam, never a silent UNBOUNDED.**
|
|
182
|
+
`capStop` read `'2.50'`, `NaN`, `Infinity`, `true` and `{}` as *no ceiling* while
|
|
183
|
+
`makeCostBook` advertised the same value straight back — the advertised and the enforced
|
|
184
|
+
budget were different numbers, the one thing a budget may never be — and the only guard
|
|
185
|
+
(`parseCeiling`) was CLI-side, so every library caller ran unprotected. A non-finite non-null
|
|
186
|
+
now throws **before the first paid call**: it costs $0 and propagates uncaught, so no catch
|
|
187
|
+
can launder it into an ABSENT survey. `null`/`undefined` stay the stated operator choice for
|
|
188
|
+
unbounded; `0` and negatives stay finite and cap-halt immediately, pinned as they were.
|
|
189
|
+
- **`budgetStop` latches only when the ceiling actually ended the ladder.** The recovery-seam
|
|
190
|
+
latch fired for EVERY final cause: a 1-turn transport death and even a healthy PRESENT survey
|
|
191
|
+
that spent past the ceiling both came back `budgetStop:'cap-halt'`, on a field whose own
|
|
192
|
+
docstring says *"the ceiling, when it is what ended the ladder"* (the second instance was not
|
|
193
|
+
in the finding). The recovery predicate is settled first now, and `capStop` consulted only
|
|
194
|
+
when a recovery call was actually PENDING. The genuine cap-plus-transport-death case keeps its
|
|
195
|
+
`cap-halt`, but the refusal detail **concedes the concurrent cause instead of contradicting
|
|
196
|
+
the `ENETUNREACH` it quotes** — and concedes it for every INCOMPLETE cause, not only transport
|
|
197
|
+
death: a SHORT/EMPTY/UNPARSEABLE survey whose repair round the ceiling refused was still
|
|
198
|
+
emitting *"the survey stopped on the authoring ceiling, not on anything it read"* directly
|
|
199
|
+
above the reply it was quoting. `CALL_FAILED` keeps the transport wording; `NOT_FUNDED` — no
|
|
200
|
+
call ever made — keeps the ceiling-only wording, the one case where it is true. No budget
|
|
201
|
+
semantics moved: the PRD already said only the former is why the loop ended, and the code now
|
|
202
|
+
honours its own sentence.
|
|
203
|
+
- **The authoring spine event rides its own stop.** `scripts/run-author.mjs` hardcoded
|
|
204
|
+
`type:'cap-halt'` on the governance readout even when the stop was `pricing-red` — the only
|
|
205
|
+
site in the repo where a spine event's type contradicted its own category, and a `pricing-red`
|
|
206
|
+
event carrying *"not under cap"* sends the operator off to raise a number when the repair is a
|
|
207
|
+
priced provider. The old spelling could also arm `ledger.js`'s capability-gap fuse on a run
|
|
208
|
+
whose wallet was never empty (latent — no script feeds author spines to `updateLedger` today,
|
|
209
|
+
but type-keyed slicing is exactly how F45's misread happened). The emit now spends
|
|
210
|
+
`authored.stop` for both fields, so meaning derives from the stop once.
|
|
211
|
+
|
|
8
212
|
## [0.9.0] — 2026-08-09
|
|
9
213
|
|
|
10
214
|
The **close-authoring rung**. The close stops being a hand-written script and becomes a
|
package/README.md
CHANGED
|
@@ -13,144 +13,126 @@
|
|
|
13
13
|
<p align="center">
|
|
14
14
|
<img src="https://img.shields.io/github/package-json/v/hamr0/bareloop?label=version&color=2a4f8c" alt="version (auto from package.json)">
|
|
15
15
|
<img src="https://img.shields.io/badge/license-Apache%202.0-2a4f8c" alt="license: Apache 2.0">
|
|
16
|
-
<img src="https://img.shields.io/badge/status-
|
|
16
|
+
<img src="https://img.shields.io/badge/status-WIP-orange" alt="status: WIP">
|
|
17
17
|
</p>
|
|
18
18
|
|
|
19
|
-
**"Automate this job — I don't know the best workflow."**
|
|
20
|
-
long, and verifiable**: an agent authors the workflow scaffolding (a constrained,
|
|
21
|
-
validated config — never freeform code); runs execute under an un-gameable outer gate;
|
|
22
|
-
and the scaffolding *improves across runs* through verdict-gated, run-as-executed
|
|
23
|
-
inheritance with ledger-counted attribution.
|
|
24
|
-
|
|
25
|
-
The pitch in one line: **workflows that earn their own design, with receipts** — every
|
|
26
|
-
inherited rule carries the green that minted it and the contrast that attributed it.
|
|
27
|
-
|
|
28
|
-
**What you keep is the workflow, not the session.** An agent figures your recurring,
|
|
29
|
-
verifiable job out once; what survives is a verified, self-healing workflow you can run
|
|
30
|
-
again — with bounded spend under an envelope *you* sign, deterministic verification you
|
|
31
|
-
don't author, and cost and time reported honestly (an unknown is reported unknown, never
|
|
32
|
-
rounded to zero). *Direction, not yet shipped:* a local workbench where you experiment with
|
|
33
|
-
a job, graduate a workflow once it greens, and **export** it — signed spec, workflow, close
|
|
34
|
-
scripts and a thin runner that depends on bareloop, so the whole self-healing library
|
|
35
|
-
(bare-agent · bareguard · litectx) comes along as npm dependencies. That bundle runs
|
|
36
|
-
headless as an enclosed CLI and asks you the same signing questions at the prompt that the
|
|
37
|
-
workbench would. It is a dependency, never generated standalone code: the gate travels with
|
|
38
|
-
it and never disappears.
|
|
39
|
-
|
|
40
|
-
> **Status: Layer 2 — accepted.** The agent now authors its own workflow: `runJob()`
|
|
41
|
-
> takes a signed job spec, surveys the repo, **drafts a validated plan of bounded steps**
|
|
42
|
-
> (each with an operator-signed self-check it may reference but never author), and runs it
|
|
43
|
-
> under an un-gameable outer close and one budget it can only tighten. On the real-model
|
|
44
|
-
> acceptance battery (F47) the emergent flow converted a job the same worker failed 4/4
|
|
45
|
-
> before — 3/3 runs, clearing the owned bar every time, composing its own check exits. The
|
|
46
|
-
> within-run ratchet (Layer R) ships armed-and-inert by measurement (F41). **Then N3 —
|
|
47
|
-
> executed inheritance**, where a workflow that greened is kept, offered to the next
|
|
48
|
-
> similar job, and improved as executed instead of drafted from scratch (the ladder's
|
|
49
|
-
> kill-switch question). As of 0.7.0 N3's machinery **ships** — the bridge registry, the
|
|
50
|
-
> operator-signed reuse envelope, the selection call and step-level resume are all in the
|
|
51
|
-
> public API — but shipping is not acceptance: one execution green proves reuse safe and
|
|
52
|
-
> legal, never *beneficial*. The contrast that was to prove the lift is now retired (F88): a
|
|
53
|
-
> validation rule hands every cold run the plan shape a bridge was carrying, so the gap it
|
|
54
|
-
> would have measured is gone. Treat reuse as available and unproven — what a bridge should
|
|
55
|
-
> carry instead is open. **The rung in flight is close authoring** — the last layer where a
|
|
56
|
-
> human still wrote code. You answer a short interview about what *done* means; an LLM then
|
|
57
|
-
> composes a close **declaration** over a catalogue of stage kinds bareloop implements — never
|
|
58
|
-
> code, never prose parsed back — which is validated, run once against your untouched repo so
|
|
59
|
-
> every ruler is seen measuring something real, and gated before you sign its hash. It is
|
|
60
|
-
> proven live on both a JavaScript and a Python patient, and its designed failure mode is an
|
|
61
|
-
> honest refusal: a close that would grade nothing, or a stage whose population does not exist
|
|
62
|
-
> in your tree, comes back decision-ready instead of green.
|
|
63
|
-
|
|
64
|
-
## Quick start
|
|
19
|
+
**"Automate this job — I don't know the best workflow."**
|
|
65
20
|
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
21
|
+
For work that is repeated, long, and verifiable: an agent authors the workflow scaffolding
|
|
22
|
+
itself, as a constrained and validated config rather than freeform code. Every run executes
|
|
23
|
+
under an outer gate the agent cannot reach or argue with. And the workflow gets better across
|
|
24
|
+
runs, because what actually worked is kept with receipts and carried into the next run.
|
|
69
25
|
|
|
70
|
-
|
|
26
|
+
Agents got good at doing the work. The open problem is knowing when the work is actually
|
|
27
|
+
done. Three of the strongest results in long-running agent work landed on the same answer
|
|
28
|
+
independently: the ralph loop, code machine MCP, and recursive language models all trust the
|
|
29
|
+
agent to do the work and verify it with something the agent cannot reach. Trust, but verify.
|
|
71
30
|
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
31
|
+
bareloop takes that principle and makes it the product. You describe a repeated, verifiable
|
|
32
|
+
job and what *done* means for it. The agent handles the rest, under verification it never
|
|
33
|
+
gets to touch.
|
|
34
|
+
|
|
35
|
+
## Three kinds of done
|
|
36
|
+
|
|
37
|
+
Jobs differ by who or what decides the result is good. That difference is the product's
|
|
38
|
+
spine, so you choose it when you create the job.
|
|
75
39
|
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
40
|
+
**green** — a predicate decides. Tests pass, the build exits clean, the type checker is
|
|
41
|
+
quiet.
|
|
42
|
+
> *"Fix the failing suite."* · *"Migrate this package to strict types."*
|
|
79
43
|
|
|
80
|
-
|
|
44
|
+
**softgreen** — an assessment decides against a rubric.
|
|
45
|
+
> *"Reserve a ticket from SF to LA under $400."*
|
|
81
46
|
|
|
82
|
-
|
|
47
|
+
**hitl** — a human makes the final call.
|
|
48
|
+
> *"Improve my resume."*
|
|
83
49
|
|
|
84
|
-
|
|
50
|
+
## Why the agent authors the workflow
|
|
85
51
|
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
52
|
+
The bare suite gave us forty-plus primitives, and there are at least as many opinions about
|
|
53
|
+
how context engineering should be broken into steps. Instead of picking one opinion and
|
|
54
|
+
shipping it as the recipe, bareloop makes the agent the author. It chooses its own path
|
|
55
|
+
through the primitives and then has to survive verification it cannot edit, widen, or
|
|
56
|
+
disable.
|
|
91
57
|
|
|
92
|
-
|
|
93
|
-
|
|
58
|
+
The lessons from ralph, RLMs, and code machine MCP are in here, applied to the small parts
|
|
59
|
+
rather than adopted whole. None of them is the workflow; each one is available to the agent
|
|
60
|
+
building it.
|
|
94
61
|
|
|
95
|
-
|
|
96
|
-
|---|---|---|---|
|
|
97
|
-
| **Hard green** | predicate / exit-code (tests, build, lint) | automatically | **built** |
|
|
98
|
-
| **Soft green** | rubric / assessment | only with HITL confirm or N consistent repeats | declared, not built (rung N4) |
|
|
99
|
-
| **HITL green** | a human is the close (PR merge, "publish") | yes — and merge stays human, forever | declared, not built (rung N4) |
|
|
62
|
+
## Install
|
|
100
63
|
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
64
|
+
```bash
|
|
65
|
+
npm install bareloop
|
|
66
|
+
```
|
|
104
67
|
|
|
105
|
-
##
|
|
68
|
+
## Usage
|
|
69
|
+
|
|
70
|
+
A signed job spec goes in. A verdict comes out.
|
|
71
|
+
|
|
72
|
+
```js
|
|
73
|
+
import { runJob, jobSpecHash, makeSpine } from 'bareloop';
|
|
74
|
+
import { AnthropicProvider } from 'bare-agent';
|
|
75
|
+
|
|
76
|
+
const spec = {
|
|
77
|
+
schema: 'job-v1',
|
|
78
|
+
job: 'my-maintainer',
|
|
79
|
+
description: 'fix src until the suite greens',
|
|
80
|
+
goal: 'Fix any failure in src/ so the suite passes.',
|
|
81
|
+
verdictType: 'green',
|
|
82
|
+
close: [{ name: 'suite-green', cmd: 'npm test', expect: 0 }],
|
|
83
|
+
provider: 'anthropic-api',
|
|
84
|
+
cadence: { unit: 'day', every: 1 },
|
|
85
|
+
budgetUsd: 1.5,
|
|
86
|
+
writeScope: ['src/**'],
|
|
87
|
+
tools: ['read', 'grep', 'write', 'edit'],
|
|
88
|
+
escalation: { mode: 'decision-ready' },
|
|
89
|
+
};
|
|
90
|
+
|
|
91
|
+
// you sign the spec; an edited spec is unsigned by construction
|
|
92
|
+
const approvals = [
|
|
93
|
+
{ specHash: jobSpecHash(spec), signer: 'you', ts: new Date().toISOString() },
|
|
94
|
+
];
|
|
95
|
+
|
|
96
|
+
const outcome = await runJob(spec, {
|
|
97
|
+
approvals,
|
|
98
|
+
workdir: '/path/to/checkout',
|
|
99
|
+
provider: new AnthropicProvider({ model: 'claude-sonnet-5' }), // key from env
|
|
100
|
+
emit: makeSpine('/path/to/checkout/run.jsonl'),
|
|
101
|
+
});
|
|
102
|
+
// 'green' means your close passed. Anything else is a named, decision-ready stop.
|
|
103
|
+
```
|
|
106
104
|
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
the worker just wrote, gets its environment **stripped of credential-shaped variables**
|
|
110
|
-
(provider keys, `AWS_*`, anything named `*_TOKEN`/`*_SECRET`/`*_PASSWORD`/`*_API_KEY`):
|
|
111
|
-
nothing that judges a tree needs them. That is exposure reduction, **not a sandbox** — the
|
|
112
|
-
worker's code still runs with your OS permissions and network access, so treat a bareloop
|
|
113
|
-
run as inside your trust boundary and give it a repo you'd let an agent touch.
|
|
105
|
+
You do not have to hand-write that `close` yourself. bareloop can author it with you from a
|
|
106
|
+
short interview about what done means for the job, then hand you the result to sign.
|
|
114
107
|
|
|
115
|
-
|
|
108
|
+
**Integrating an agent?** Point it at the contract, not this file:
|
|
116
109
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
110
|
+
```
|
|
111
|
+
Read bareloop.context.md from node_modules/bareloop/bareloop.context.md
|
|
112
|
+
```
|
|
120
113
|
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
| Mid-run revision recovers stuck runs | M5 (F11: 3/3 vs 1/3) |
|
|
125
|
-
| Verdict-gated inheritance beats ungated on pass/fail | F19: gated late 1.00 vs ungated 0.13 |
|
|
126
|
-
| Run-as-executed inheritance transmits in-run learning | F20: 6/6 lineages, ~½ cost |
|
|
127
|
-
| Which-knob attribution is countable from the ledger | V2: contrast bit 16/16 gens |
|
|
128
|
-
| Where memory pays: regularities outside the worker's prior | F17/F18: ~8× under acquisition cost |
|
|
114
|
+
That single file is the complete adopter contract: the API, the boundary, the refusals, the
|
|
115
|
+
constraints. The README is the pitch. (Suite-wide pattern: every bare package ships its
|
|
116
|
+
`*.context.md`.)
|
|
129
117
|
|
|
130
|
-
|
|
118
|
+
## Build it once, keep it
|
|
131
119
|
|
|
132
|
-
|
|
120
|
+
A simple localhost UI lets you build a job by talking about it and watch it run. When it
|
|
121
|
+
works, **export the whole workflow** — with its self-healing harness — and run it again
|
|
122
|
+
anywhere. The point is to stop trial-and-erroring your way to the workflow that fits a job.
|
|
123
|
+
Find it once, keep it, reuse it. It is a seed of something bigger over time.
|
|
133
124
|
|
|
134
|
-
|
|
135
|
-
ladder, and the stop is a result.
|
|
125
|
+
## Running it
|
|
136
126
|
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
| **N1** ✅ | Job/close schema + validator — v0.2.0 |
|
|
141
|
-
| **N2** ✅ | Single-job headless loop — job #1 minimal (review→fix→PR, hard greens only) — v0.3.0 |
|
|
142
|
-
| **Layer R** ✅ | Within-run ratchet: shell-side fixation detector (intent) + rejected-edit feedback (outcome), **shipped OFF by default** (F41: fixation in remission, so ON has never won its A/B; F43 split the two axes; field read + default decision defer to Layer 2) — v0.4.0 |
|
|
143
|
-
| **Layer 2** ✅ | plan-v1 micro-wheels — the road: bounded steps with in-run verifiable exits (the F39-measured semantic converter), accepted on the real-model battery (F47) — v0.5.0, then finished by T·A·P·U (a wall clock, variance-triggered replan, the 14-verb palette, user-mode e2e) |
|
|
144
|
-
| **N3** | Executed inheritance + contrast-bit extractor — **kill-switch: rules must transmit across non-identical runs** |
|
|
145
|
-
| **N4** | Verdict classes complete (soft/HITL minting) |
|
|
146
|
-
| **N5** | Scheduler + budget ops |
|
|
147
|
-
| **N6** | The panel ([spec](docs/01-product/PRD.md#appendix-a--panel-spec-provisional): left chat + command bar, right progress over results, context-graph reserved) |
|
|
127
|
+
bareloop runs locally, as you. Secrets load from the environment and never enter the tree or
|
|
128
|
+
the run's records. It reduces exposure but it is not a sandbox: the code your job writes runs
|
|
129
|
+
with your permissions, so give bareloop a repo you would let an agent touch.
|
|
148
130
|
|
|
149
131
|
## The bare ecosystem
|
|
150
132
|
|
|
151
|
-
Local-first, composable agent infrastructure. Same API patterns throughout
|
|
152
|
-
|
|
153
|
-
|
|
133
|
+
Local-first, composable agent infrastructure. Same API patterns throughout. Mix and match,
|
|
134
|
+
each module works standalone. bareloop is the suite's flagship consumer: it exercises every
|
|
135
|
+
package, and gaps get fixed upstream rather than shimmed.
|
|
154
136
|
|
|
155
137
|
**Core** — the brain, the gate, the memory.
|
|
156
138
|
|
|
@@ -164,10 +146,12 @@ it exercises every package and gaps get fixed upstream, never shimmed.
|
|
|
164
146
|
- **[baremobile](https://npmjs.com/package/baremobile)** — Android + iOS device control. *Screen in → pruned snapshot out.* Replaces Appium, Espresso, XCUITest.
|
|
165
147
|
- **[beeperbox](https://github.com/hamr0/beeperbox)** — 50+ messaging networks via one MCP server. *Chat in → unified message stream out.* Replaces Twilio, per-platform bot APIs.
|
|
166
148
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
149
|
+
## Background
|
|
150
|
+
|
|
151
|
+
bareloop is the productization of **[adaptlearn](https://github.com/hamr0/adaptlearn)**, a
|
|
152
|
+
closed experimental record on whether agent-authored workflows improve under an honest gate.
|
|
153
|
+
The design laws it inherited, and the open questions it has not answered yet, are in
|
|
154
|
+
[`docs/01-product/PRD.md`](docs/01-product/PRD.md).
|
|
171
155
|
|
|
172
156
|
## License
|
|
173
157
|
|