bareloop 0.12.0 → 0.14.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 +139 -0
- package/README.md +1 -1
- package/bareloop.context.md +169 -7
- package/package.json +2 -2
- package/src/behaviour.js +150 -0
- package/src/bridges.js +1 -1
- package/src/calibrate.js +1 -1
- package/src/cardauthor.js +1 -1
- package/src/index.js +21 -1
- package/src/job.js +1 -1
- package/src/judged.js +1 -1
- package/src/ledger.js +103 -1
- package/src/plan.js +43 -2
- package/src/planrun.js +306 -16
- package/src/readshim.js +570 -0
- package/src/reuse.js +27 -3
- package/src/root.js +1 -1
- package/src/run.js +39 -4
- package/src/selection.js +1 -1
- package/src/tools.js +34 -2
- package/src/transport.js +71 -0
- package/types/behaviour.d.ts +46 -0
- package/types/index.d.ts +3 -1
- package/types/ledger.d.ts +58 -0
- package/types/plan.d.ts +19 -2
- package/types/planrun.d.ts +37 -2
- package/types/readshim.d.ts +128 -0
- package/types/reuse.d.ts +24 -0
- package/types/run.d.ts +15 -1
- package/types/tools.d.ts +17 -1
- package/types/transport.d.ts +19 -0
package/CHANGELOG.md
CHANGED
|
@@ -5,6 +5,145 @@ 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
|
+
## [Unreleased]
|
|
9
|
+
|
|
10
|
+
## [0.14.0] — 2026-08-25
|
|
11
|
+
|
|
12
|
+
### Added
|
|
13
|
+
|
|
14
|
+
- **One transport-class retry** (F115, hamr's ruling): the ONE worker-Loop seam in
|
|
15
|
+
`src/planrun.js` now retries exactly once on a transport-only throw (TLS fault,
|
|
16
|
+
`ECONNRESET`/`EPIPE`/`ETIMEDOUT`, `fetch failed` over a network cause — never an HTTP
|
|
17
|
+
4xx/5xx/429 response), classified by the new `src/transport.js` (`isTransportFailure`,
|
|
18
|
+
fixed `TRANSPORT_MAX_ATTEMPTS`, never operator-configurable). Emits a report-only
|
|
19
|
+
`transport-retry` spine record and forces `job-end.spendComplete: false` for the rest of the
|
|
20
|
+
run — the retried attempt's first throw may already have been billed — surfaced by
|
|
21
|
+
`scripts/run-u.mjs` as a `retries` line only when a retry occurred.
|
|
22
|
+
- **provider-red joins the resumable set** (F115, PRD v1.80 TODO #4): when the one transport
|
|
23
|
+
retry also fails and a `run-u` run ends `provider-red`, its tail now prints an honest
|
|
24
|
+
readout — spend so far as a floor, "died in step X of Y" (or "before a plan was accepted"),
|
|
25
|
+
and the exact `--resume` command — and `--resume` re-enters at the recorded step with the
|
|
26
|
+
accepted plan, folding prior spend in. No cost/step threshold gates the offer (the operator
|
|
27
|
+
decides, every time); verdict terminals (`escalated`/`step-red`/`plan-red`) still refuse.
|
|
28
|
+
Folded from a local `scripts/run-u.mjs` widening into the library's own canonical
|
|
29
|
+
`CHECKPOINT_OUTCOMES` (`src/reuse.js`) so the two lists cannot drift.
|
|
30
|
+
- **Run behaviour summary** (build-list item 2, agreed 2026-08-23): `runBehaviour(events, {
|
|
31
|
+
runId? })` and `formatBehaviour(summary)` in `src/behaviour.js`, plus
|
|
32
|
+
`scripts/behaviour-readout.mjs <gate-audit.jsonl> [run_id]`. A report-only text block —
|
|
33
|
+
"94 tool calls · 61 read, 28 grep, 5 recall" / "17 exact repeats (~18%)" — computed entirely
|
|
34
|
+
from a run's own gate-audit JSONL. Gates nothing, changes no verdict, writes nothing to the
|
|
35
|
+
spine. The exact-repeat key is the full recorded action (`type` + `path` + every `args` key),
|
|
36
|
+
never collapsed to `{type, path}` — sharper collapse would have merged different byte ranges
|
|
37
|
+
of the same file into false repeats.
|
|
38
|
+
- **`memory-cache` end-of-run readout** for the read shim: `runPlan` emits one spine record
|
|
39
|
+
(`{pointered, capped, bytesWithheld, approxTokens}`) summing what L1's pointer and L4's cap
|
|
40
|
+
withheld across the whole run, only when the shim is armed. `scripts/run-u.mjs` prints it as
|
|
41
|
+
`MEMORY-CACHE N re-reads answered from memory · M reads capped · X KB withheld (~Tk tokens
|
|
42
|
+
not re-sent)`; `scripts/behaviour-readout.mjs` prints the same line given the run's spine as
|
|
43
|
+
an optional third positional. No cost fields — report-only, not a spend record.
|
|
44
|
+
|
|
45
|
+
### Fixed
|
|
46
|
+
|
|
47
|
+
- **`scripts/readshim-battery.mjs`'s spend reader compared a resumed leg's own money against
|
|
48
|
+
the wrong job-end figure** (F116): `readSpend`'s `ledgerUsd` now reads
|
|
49
|
+
`job-end.engagementSpentUsd` (this leg's spend) when a spine carries one, falling back to
|
|
50
|
+
`job-end.spentUsd` only for a cold run — `spentUsd` on a resumed job-end is a CHAIN total,
|
|
51
|
+
not this spine's own spend. `chainUsd`/`priorUsd` are exposed as new, honest fields and never
|
|
52
|
+
folded into `accountedUsd`/`ledgerUsd`/`spendUsd`.
|
|
53
|
+
|
|
54
|
+
## [0.13.0] — 2026-08-23
|
|
55
|
+
|
|
56
|
+
### Fixed (2026-08-23 — the release round: a pointer that lied, a close with no home, a rule that charged for its own refusal)
|
|
57
|
+
|
|
58
|
+
- **The pointer no longer lies about a file emptied since it was read.** `src/readshim.js`'s
|
|
59
|
+
pointer branch tested `start >= total` alone, so `0 >= 0` held with **no ledger entry at all** —
|
|
60
|
+
the first read of an empty file and a re-read of a file TRUNCATED TO EMPTY since delivery were
|
|
61
|
+
indistinguishable, and both answered "you already hold all 0 bytes". A worker would have gone on
|
|
62
|
+
believing in content that was gone. The guard now requires a ledger entry whose hash still
|
|
63
|
+
matches. This is precisely the untruthful-pointer class the module exists to prevent, found
|
|
64
|
+
inside the module built to prevent it, and reproduced live before the fix.
|
|
65
|
+
- **`judge-round` carries its rate provenance.** The judged floor made it the first spend a CLOSE
|
|
66
|
+
has ever had, and `src/ledger.js` documented the write side as forwarding `rateSource` onto it —
|
|
67
|
+
but the emit never spread the field, so every judge round would have read UNKNOWN forever. NOT
|
|
68
|
+
rounded up: `src/kinds.js`'s `onJudgeCost` payload still does not carry the field, so it reads
|
|
69
|
+
UNKNOWN today, and both the emit and the ledger header now say so plainly.
|
|
70
|
+
- **A torn spine line stops reading as exact spend.** `scripts/run-battery-readshim.mjs` marked
|
|
71
|
+
unparseable lines `__corrupt` "never guessed at", then counted them, dropped them, and reported
|
|
72
|
+
the row's spend as EXACT while `corruptLines` reached no readout. `spendComplete` now goes false
|
|
73
|
+
when any line was unreadable (spend renders `>=`) and the audit prints the count per row.
|
|
74
|
+
- **G1's ceiling half is refused at $0 instead of after the drafter is paid.** `validatePlan` reds
|
|
75
|
+
`read-blind` when a capped step grants `read` without the retrieval pair — but a step cannot
|
|
76
|
+
grant what the SIGNED CEILING does not offer, so against a spec whose `tools` lack `recall`/`get`
|
|
77
|
+
**every** draft red identically and the drafter was paid for each doomed cycle. `runPlan` now
|
|
78
|
+
throws at the same $0 door as the arm guard, in the same class: an operator param-guard
|
|
79
|
+
`TypeError`, never a model-output red — nothing the agent authored is wrong when the operator
|
|
80
|
+
asks for a shim the signature cannot satisfy. Tighten-only; the only configuration it rejects
|
|
81
|
+
already failed 100% of the time, just later and for money. `RETRIEVAL_PAIR` is now exported from
|
|
82
|
+
`src/plan.js` rather than respelled, so the rule and its pre-flight cannot drift apart.
|
|
83
|
+
|
|
84
|
+
### Changed (2026-08-23)
|
|
85
|
+
|
|
86
|
+
- **The aurora close moved to a tree development cannot mutate** (`jobs/aurora-u-spawner-types.json`,
|
|
87
|
+
F110). Its stages had pointed at a scratch worktree, which was removed during ordinary branch
|
|
88
|
+
work — every stage silently became `node: cannot find module`. The home before that was a shared
|
|
89
|
+
working checkout, where a close landing mid-edit dies as an instrument fault rather than a
|
|
90
|
+
verdict. Neither is a home: a checkout is *supposed* to go dirty and a scratch worktree is
|
|
91
|
+
*supposed* to be removable. **A close is the arbiter's instrument, so its host must have the
|
|
92
|
+
arbiter's lifetime.** Now a worktree pinned at a release tag, never edited, with its own
|
|
93
|
+
dependencies. Re-signing was required and given in-turn.
|
|
94
|
+
|
|
95
|
+
### Added (2026-08-18 — the read shim, flag-gated OFF)
|
|
96
|
+
|
|
97
|
+
- **`src/readshim.js` — a per-worker delivery ledger over the read tool**, off unless the new
|
|
98
|
+
`readShim` flag is set on `runJob`/`runPlan`. A read over 24KB (`READ_SHIM_CAP`) delivers its
|
|
99
|
+
next unseen slice plus a trusted steer at `ctx_recall`/`ctx_get`; a re-read of a file the
|
|
100
|
+
worker holds WHOLE and unchanged returns a short pointer instead of the bytes; a re-read of
|
|
101
|
+
a file it holds only PART of returns the next slice, never a pointer; any content change
|
|
102
|
+
re-delivers from the start. Measured on a $0 replay of 1,844 real `shell_read` actions from
|
|
103
|
+
143 archived gate audits: 48% of reads re-read unchanged bytes, and cap+pointer keyed on what
|
|
104
|
+
was DELIVERED saved 65.6% of read payload with **zero** untruthful responses — where the
|
|
105
|
+
naive path+hash key saved 8.2 points more by telling **250 lies**, median 73,348 bytes hidden
|
|
106
|
+
each (BA-17 read-blinding, reproduced).
|
|
107
|
+
- **G1 at the validation gate (`read-blind`)**: under the shim, a plan step granting `read` must
|
|
108
|
+
also grant `recall` and `get`. Capping a worker with no retrieval verb is BA-17 on purpose —
|
|
109
|
+
the cap is what makes the pair mandatory. Stated in the drafting prompt as well as enforced
|
|
110
|
+
(the mailbox precedent).
|
|
111
|
+
- **One flag, four ARMS, default OFF** (the `layerRoot` precedent, F41). `readShim` names which
|
|
112
|
+
levers run, one value per row of the frozen Phase 2 pre-registration: `false` = A0 (off),
|
|
113
|
+
`'cap'` = A1 (cap + pointer + next-unseen-slice + G1), `'diff'` = A2 (the diff lever alone — no
|
|
114
|
+
cap, no pointer, no G1), `true` = A3 (every lever). The two booleans mean exactly what they
|
|
115
|
+
meant before the arms existed, so nothing written against them changed meaning. With the shim
|
|
116
|
+
off every observable — validator reds, persona, delivered bytes — is byte-identical to the
|
|
117
|
+
pre-shim run: the frozen A0 arm has to be exactly today, and a guard firing under a disabled
|
|
118
|
+
shim would make the baseline a treatment arm. Each arm's persona line describes only the
|
|
119
|
+
machinery actually installed (A2 is never told about a 24KB limit that is not in force). An
|
|
120
|
+
unrecognised value THROWS at the entry (`readShimArm`) rather than coercing into a truthy
|
|
121
|
+
shim — a mis-spelled arm would run one treatment under another's label and be invisible in the
|
|
122
|
+
results afterwards. The default-flip is not in this change: it waits on a paid contrast.
|
|
123
|
+
- **A stale `ctx_get` pointer serves the requested line range from disk** (2026-08-19), under a
|
|
124
|
+
CAPPING arm only. The cap's own strategy line steers a capped worker at `ctx_recall` →
|
|
125
|
+
`ctx_get` for a whole function; `ctx_get` is content-hash gated and throws `StalePointerError`
|
|
126
|
+
the moment the file changed after indexing — which is the normal case for a worker that just
|
|
127
|
+
EDITED the file it is working on. Measured on the A1 arm's three green runs: 10 `ctx_get`
|
|
128
|
+
calls, 5 stale, 0 bytes each, every one a just-written file. A paid round returning nothing,
|
|
129
|
+
down a path the cap itself chose. The serve hands back the requested 0-based inclusive line
|
|
130
|
+
range read fresh from disk, under TRUSTED framing that says the pointer was stale, that the
|
|
131
|
+
range may now hold DIFFERENT code than the symbol asked for, which lines arrived, and that
|
|
132
|
+
nothing was recorded as delivered. litectx refuses to slice a drifted range itself because an
|
|
133
|
+
UNLABELLED slice can silently be another symbol's body — the label is what makes it legal.
|
|
134
|
+
Detection is on the exported `StalePointerError` CLASS, never on message prose. A vanished
|
|
135
|
+
file or a range now entirely past the end is a refusal RESULT naming what happened, never a
|
|
136
|
+
throw. It **never touches the delivery ledger**: a slice is not the file, and a ledger that
|
|
137
|
+
recorded one would answer the next read with "you already have it" to a worker holding 90
|
|
138
|
+
lines of 900 — the untruthful-pointer failure the shim exists to prevent. The cost of that is
|
|
139
|
+
a later `shell_read` may re-deliver bytes the serve already showed; the shim over-delivers
|
|
140
|
+
rather than lies. A0 and A2 are untouched: with no capping arm the hook is not wired and
|
|
141
|
+
`ctx_get`'s contract is byte-identical.
|
|
142
|
+
- On the native surface the shim REPLACES the `NATIVE_READ_CAP` wrapper rather than layering over
|
|
143
|
+
it (both bound the same seam at the same 24KB, and a shim outside the native wrapper would
|
|
144
|
+
ledger already-truncated text as a whole file — the exact lie it exists to prevent). L2
|
|
145
|
+
(diff-on-change) is deliberately not built: it never fired on the real corpus.
|
|
146
|
+
|
|
8
147
|
## [0.12.0] — 2026-08-23
|
|
9
148
|
|
|
10
149
|
### Added (N4 slice 2 — the softgreen rung: a close that JUDGES, and a signer who has the last word)
|
package/README.md
CHANGED
|
@@ -162,7 +162,7 @@ package, and gaps get fixed upstream rather than shimmed.
|
|
|
162
162
|
bareloop is the productization of **[adaptlearn](https://github.com/hamr0/adaptlearn)**, a
|
|
163
163
|
closed experimental record on whether agent-authored workflows improve under an honest gate.
|
|
164
164
|
The design laws it inherited, and the open questions it has not answered yet, are in
|
|
165
|
-
[`docs/
|
|
165
|
+
[`docs/product/PRD.md`](docs/product/PRD.md).
|
|
166
166
|
|
|
167
167
|
## License
|
|
168
168
|
|
package/bareloop.context.md
CHANGED
|
@@ -26,7 +26,7 @@
|
|
|
26
26
|
> where `hitl` IS admitted, `human-confirms` IS live, and the three `hitl-*` terminals exist —
|
|
27
27
|
> integrate against what is written here, and expect the class-level rename when the next rung
|
|
28
28
|
> lands. Records: PRD Addendum v1.71 and
|
|
29
|
-
> `docs/
|
|
29
|
+
> `docs/product/2026-08-17-softgreen-review-door-design.md` (repo-only, not shipped);
|
|
30
30
|
> evidence F102–F105.
|
|
31
31
|
|
|
32
32
|
## What this is
|
|
@@ -918,7 +918,7 @@ Reserved spine vocabulary (V7, machinery-free until job #1 surfaces one):
|
|
|
918
918
|
`coordination-red` — a failure between units (scope contention, step order, store
|
|
919
919
|
races), never to be folded into worker/interpreter reds.
|
|
920
920
|
|
|
921
|
-
### `runJob(spec, { approvals, workdir, provider, nativeProvider?, providerFor?, emit, capRuns?, strikeLimit?, shellCapUsd?, closeTimeoutMs?, layerRoot?, bridge?, priorSpentUsd?, priorSpendComplete?, priorWallMs?, resumeSeed?, resumeGrades?, resumeReplans?, resumeBranch?, humanRuling?, heldRuling?, reviewDoor?, doorRerun? })` → outcome — `src/run.js`
|
|
921
|
+
### `runJob(spec, { approvals, workdir, provider, nativeProvider?, providerFor?, emit, capRuns?, strikeLimit?, shellCapUsd?, closeTimeoutMs?, layerRoot?, readShim?, bridge?, priorSpentUsd?, priorSpendComplete?, priorWallMs?, resumeSeed?, resumeGrades?, resumeReplans?, resumeBranch?, humanRuling?, heldRuling?, reviewDoor?, doorRerun? })` → outcome — `src/run.js`
|
|
922
922
|
|
|
923
923
|
The last seven are the RESUME fold and are documented under *Resuming a killed run* below; they
|
|
924
924
|
default to `0` / `true` / `0` / `null` / `[]` / `null` / `null`, so a fresh run passes none of them.
|
|
@@ -937,6 +937,56 @@ plan-red | check-red | close-red | close-unsupported | recipe-stale | branch-red
|
|
|
937
937
|
provider-red | interpreter-red | cap-halt | wall-halt | step-stalled | hitl-pause |
|
|
938
938
|
hitl-decision-red | step-red:<id>`.
|
|
939
939
|
|
|
940
|
+
**`readShim` (default `false`) — the capped read seam, per ARM.** The value names which levers
|
|
941
|
+
run — the four arms of the frozen Phase 2 pre-registration:
|
|
942
|
+
|
|
943
|
+
| value | arm | cap + next-unseen-slice | pointer | diff on a changed re-read | G1 (`read-blind`) | persona line |
|
|
944
|
+
|---|---|---|---|---|---|---|
|
|
945
|
+
| `false` *(default)* | A0 | — | — | — | — | none |
|
|
946
|
+
| `'cap'` | A1 | ✓ | ✓ | — | ✓ | `READ_SHIM_STRATEGY` |
|
|
947
|
+
| `'diff'` | A2 | — | — | ✓ | — | `READ_SHIM_DIFF_STRATEGY` |
|
|
948
|
+
| `true` | A3 | ✓ | ✓ | ✓ | ✓ | `READ_SHIM_STRATEGY` |
|
|
949
|
+
|
|
950
|
+
Anything else **throws** (`readShimArm`) at `runJob`/`runPlan`/`validatePlan` before a token is
|
|
951
|
+
spent — a mis-spelled arm coerced by truthiness would run one treatment under another's label and
|
|
952
|
+
be invisible in the results afterwards.
|
|
953
|
+
|
|
954
|
+
A **capping arm against a signed ceiling that offers no `recall`/`get` also throws at that same
|
|
955
|
+
$0 door**, and for the same reason: G1 below requires the retrieval pair on any step granting
|
|
956
|
+
`read`, but a step cannot grant what `spec.tools` does not offer — so every draft would red
|
|
957
|
+
`read-blind` identically while the drafter was paid for each doomed cycle. The message names the
|
|
958
|
+
missing verbs and both exits (re-sign the spec with them, or run with the shim off). Like the arm
|
|
959
|
+
throw, it is an **operator param-guard `TypeError`, never a model-output red**: nothing the agent
|
|
960
|
+
authored is wrong when the operator asks for a shim the signature cannot satisfy. It narrows
|
|
961
|
+
nothing that works — the only configuration it rejects already failed 100% of the time, later and
|
|
962
|
+
for money. `RETRIEVAL_PAIR` is exported from `src/plan.js` so the rule and this pre-flight read one
|
|
963
|
+
spelling of which verbs count.
|
|
964
|
+
|
|
965
|
+
Under a CAPPING arm (A1/A3) the shim also answers a **stale `ctx_get` pointer**: the cap steers the
|
|
966
|
+
worker at `ctx_recall`/`ctx_get`, and `ctx_get` refuses a file that changed after indexing — the
|
|
967
|
+
normal case once the worker has edited it. Instead of nothing, the shim serves that pointer's
|
|
968
|
+
0-based inclusive line range read fresh from disk, capped the same way, under a trusted notice
|
|
969
|
+
saying the pointer was stale, that the range may now hold DIFFERENT code than the symbol asked
|
|
970
|
+
for, and that nothing was recorded as delivered. A vanished file or a range past the end is a
|
|
971
|
+
refusal result, never a throw. The serve never writes the delivery ledger, so it can never mint a
|
|
972
|
+
"you already hold it" pointer for bytes the read seam did not hand over. A0 and A2 do not wire it
|
|
973
|
+
and `ctx_get` behaves exactly as it always did.
|
|
974
|
+
|
|
975
|
+
`false` is the shipped behaviour and is byte-identical to a run without the flag, guards included;
|
|
976
|
+
`true` means exactly what it meant before the arms existed, so nothing written against the boolean
|
|
977
|
+
changed meaning. Where the CAP runs, `shell_read` delivers at most `READ_SHIM_CAP` (24KB) per call
|
|
978
|
+
with a trusted steer at `ctx_recall`/`ctx_get`; a re-read continues from where the last one
|
|
979
|
+
stopped, and once the worker holds the whole unchanged file a re-read returns a pointer instead of
|
|
980
|
+
the bytes (keyed on what was DELIVERED — a partly-seen file NEVER gets a pointer, which is the
|
|
981
|
+
measured correctness point); and `validatePlan` reds `read-blind` on any step granting `read`
|
|
982
|
+
without `recall` and `get`. Where only the DIFF runs, nothing is capped (a first read delivers
|
|
983
|
+
whole, whatever the size), an unchanged re-read re-delivers rather than pointing, and G1 does not
|
|
984
|
+
exist — G1 is the cap's compensation, and an arm that caps nothing has nothing to compensate for.
|
|
985
|
+
The ledger is per worker, so it resets with every step. On the native (clipipe) surface a CAPPING
|
|
986
|
+
arm replaces the CLI-display cap rather than stacking on it; a non-capping arm leaves that wrapper
|
|
987
|
+
installed *inside* the shim, so native never loses its bound. Default-flip pending a paid contrast;
|
|
988
|
+
see CHANGELOG for the replay numbers behind it.
|
|
989
|
+
|
|
940
990
|
**The two hitl terminals (N4 slice 1, doors re-cut 2026-08-18)** are the class's whole surface
|
|
941
991
|
at this layer, and each is a CLEAN exit (`spendComplete` stays true — only the two casualties floor). `hitl-pause` is a
|
|
942
992
|
decision-ready CHECKPOINT: the close reached a stage no machine can render, so the run stops
|
|
@@ -1043,13 +1093,25 @@ raises it.
|
|
|
1043
1093
|
of PRICED rounds ONLY — never an estimate from token counts or averages (cap-not-estimate) —
|
|
1044
1094
|
and it is stated even on the pre-token reds, where the honest figure is a real `0`.
|
|
1045
1095
|
`spendComplete` says whether that figure is EXACT; `false` means `spentUsd` is a FLOOR ("at
|
|
1046
|
-
least $X", true total unknowable) and must not be read as a total.
|
|
1096
|
+
least $X", true total unknowable) and must not be read as a total. Five things set it, all
|
|
1047
1097
|
the same class — a call whose cost is unknowable from here: a round that came back unpriced
|
|
1048
1098
|
(F6); a run that ABSORBED a stall, because the fuse's silent reissue may pay twice for one
|
|
1049
1099
|
answer and the flag is one-way whatever the outcome; a `wall-halt` that cut a call
|
|
1050
|
-
mid-flight (`cutMidCall`);
|
|
1100
|
+
mid-flight (`cutMidCall`); a RESUME whose fold was itself a floor
|
|
1051
1101
|
(`priorSpendComplete: false` — an unknown does not heal by being carried forward, so every
|
|
1052
|
-
round of this attempt being priced repairs nothing about the one before it)
|
|
1102
|
+
round of this attempt being priced repairs nothing about the one before it); and a
|
|
1103
|
+
**transport retry** (F115, hamr's ruling: "one retry for transport layer failure and
|
|
1104
|
+
reporting with the rest end of gate"). Every worker Loop (scout, drafter, each step, the fix
|
|
1105
|
+
worker — the ONE seam in `planrun.js`) gets exactly ONE extra attempt, and only for a
|
|
1106
|
+
transport-class throw — fetch itself throwing with no HTTP response at all (a TLS fault,
|
|
1107
|
+
`ECONNRESET`/`EPIPE`/`ETIMEDOUT`, `fetch failed` wrapping a network cause); an HTTP response
|
|
1108
|
+
(4xx/5xx/429) is never retried here and rides bare-agent's own unchanged policy. The budget is
|
|
1109
|
+
fixed (`TRANSPORT_MAX_ATTEMPTS` in `src/transport.js`) — never a job-spec or CLI knob, tighten-only
|
|
1110
|
+
doctrine. Each retry emits a report-only `transport-retry` spine record (`{phase, attempt, error,
|
|
1111
|
+
recovered}`, no cost field — the ledger is unaffected); the FIRST attempt threw before any usage
|
|
1112
|
+
figure came back, so **any** transport retry floors `spendComplete` for the rest of the run even
|
|
1113
|
+
when the retry recovers and the run finishes green — the same one-way flag class as `stalled`
|
|
1114
|
+
and `cutMidCall`. A wall stop read BETWEEN iterations or steps has no call in flight,
|
|
1053
1115
|
so it stays exact. Both fields are present on all outcomes, so a consumer never branches on
|
|
1054
1116
|
field presence and never has to launder a missing `spentUsd` into `$0`.
|
|
1055
1117
|
|
|
@@ -1393,6 +1455,53 @@ reserved round bound, with the LAST round reserved for a toolless summary: the b
|
|
|
1393
1455
|
a scout mid-tool-use, and text is its only deliverable, so without that reservation it
|
|
1394
1456
|
spends its whole allowance exploring and reports nothing (F59 — measured on 15 of 18 runs).
|
|
1395
1457
|
|
|
1458
|
+
**Every cost figure bareloop reports is an ESTIMATE, not a billed amount (BA-21).** No
|
|
1459
|
+
vanilla LLM API returns a price — providers report TOKENS — so `spentUsd`, every
|
|
1460
|
+
`worker-round` cost, every halt readout and every ledger dollar is a token count
|
|
1461
|
+
multiplied by a RATE, never a provider invoice. Expect it to read somewhat high, and do
|
|
1462
|
+
not reconcile it against a bill expecting a match. There is deliberately **no per-model
|
|
1463
|
+
price table** behind it, and the reason is worth knowing before you decide how far to
|
|
1464
|
+
trust the number: a hand-curated table is a treadmill that rots SILENTLY — a price moves
|
|
1465
|
+
or a new model ships, the row goes stale, and the figure under-reports with no detector to
|
|
1466
|
+
say so. bare-agent instead matches a recognized Claude TIER (haiku / sonnet) by model id,
|
|
1467
|
+
and falls back to a conservative ceiling rate for anything it does not recognize. Both
|
|
1468
|
+
deliberately OVER-report rather than under-report, which is the fail-safe direction for a
|
|
1469
|
+
cap: a run halts slightly early rather than overspending unnoticed. **The consequence
|
|
1470
|
+
bites at the budget, and it is not softened here:** `budgetUsd` and `shellCapUsd` are
|
|
1471
|
+
enforced against these estimates, so an inaccurate rate is a cap that binds earlier or
|
|
1472
|
+
later than you intended. The cap is exact about the number it holds; the number is a
|
|
1473
|
+
guess.
|
|
1474
|
+
|
|
1475
|
+
**If you need accuracy, pass your own rates — the supported path, not a workaround.**
|
|
1476
|
+
bare-agent's `Loop` takes `rates: {in, out, cacheReadMult?, cacheWriteMult?}` — USD per 1K
|
|
1477
|
+
tokens, the two multipliers applying to the input rate for the cache-read and cache-write
|
|
1478
|
+
tiers (defaulting to Anthropic's 0.1× / 1.25×). A caller-supplied rate is recorded as
|
|
1479
|
+
VOUCHED rather than as a guess, and it silences bare-agent's own guesstimate warning.
|
|
1480
|
+
**Named, not papered over:** `runJob`/`runPlan` construct their own `Loop` and do not yet
|
|
1481
|
+
accept a `rates` passthrough, so today that option is reachable only by a caller driving
|
|
1482
|
+
bare-agent directly — a bareloop-run job is priced by the guesstimate, full stop. The
|
|
1483
|
+
passthrough is an open follow-up, and until it lands every paragraph above describes the
|
|
1484
|
+
only pricing a `runJob` adopter gets.
|
|
1485
|
+
|
|
1486
|
+
**The provenance is on the record, per round (`rateSource`).** The field arrives with
|
|
1487
|
+
bare-agent **>= 0.37**; under the pinned `^0.36.0` no provider payload carries it yet, so
|
|
1488
|
+
every priced round this library writes today reads UNKNOWN provenance — correctly, and by
|
|
1489
|
+
the same rule that governs the archive. (The one exception is the native per-turn
|
|
1490
|
+
`worker-turn`, whose `null` is bareloop's OWN statement rather than a forwarded one: that
|
|
1491
|
+
surface prices the SESSION, so a turn had no rate to guess.) Once the pin moves, every `worker-round` /
|
|
1492
|
+
`worker-turn` the plan flow writes carries bare-agent's own label beside `pricing`:
|
|
1493
|
+
`'provider'` (the provider reported its own authoritative cost — the native CLI surface)
|
|
1494
|
+
and `'caller'` (you passed the rate) are VOUCHED; `'tier'` (a recognized Claude tier,
|
|
1495
|
+
where **both** of bareloop's own production models land) and `'default'` (the blind
|
|
1496
|
+
ceiling) are GUESSES; `null` means nothing was priced, so there was no rate to have
|
|
1497
|
+
guessed. An ABSENT field is UNKNOWN provenance — every round archived before this signal
|
|
1498
|
+
existed, and it is never backfilled, never reconstructed, and never rounded up to vouched.
|
|
1499
|
+
Read it with `rateProvenance(record)` and `spendProvenance(events)` (`src/ledger.js`),
|
|
1500
|
+
whose predicate is an ALLOW-LIST of the two vouched values: anything else, including a
|
|
1501
|
+
value a future bare-agent adds, reads as a GUESS. **Reporting only** — `pricing` stays
|
|
1502
|
+
strictly two-valued (`'priced'`|`'unpriced'`), the `pricing-red` halt still keys on cost
|
|
1503
|
+
alone, and nothing about provenance decides, halts or refuses anything.
|
|
1504
|
+
|
|
1396
1505
|
**What the worker is told (tool mode):** the absolute repository root (F10 — bare-agent's
|
|
1397
1506
|
shell tools resolve relative paths against the PROCESS cwd, so a worker with no root is
|
|
1398
1507
|
blind), the close's CURRENT output as the tree's state (F13 — never framed as "your previous
|
|
@@ -1977,9 +2086,62 @@ the same refusal emits, so counting the escalation too would double every refusa
|
|
|
1977
2086
|
`suggestedAsk` on every row is a template seed for an upstream ask — filing stays human;
|
|
1978
2087
|
status rows (`open → filed → fixed → consumed`) are human-appended, and the fold shows
|
|
1979
2088
|
the latest per key. Pure pieces exported for custom folds: `classifyIncidents(events,
|
|
1980
|
-
{spine?})`, `foldLedger(rows)`, `ledgerDeltas(fold, occurrences)`.
|
|
2089
|
+
{spine?})`, `foldLedger(rows)`, `ledgerDeltas(fold, occurrences)`. Riding with them,
|
|
2090
|
+
and deliberately NOT a ledger class: `rateProvenance(record)` → `vouched|guessed|unpriced|unknown`
|
|
2091
|
+
and `spendProvenance(events)` → per-provenance `{rounds, usd, unpricedRounds}` buckets answer
|
|
2092
|
+
"how much of this run's spend was priced by a rate nobody vouched for" (BA-21 — see *Every cost
|
|
2093
|
+
figure bareloop reports is an ESTIMATE* above). A bucket's `usd` sums FINITE costs only and is a
|
|
2094
|
+
FLOOR whenever its `unpricedRounds` is non-zero (F6 — an unknown cost is counted, never summed as
|
|
2095
|
+
$0). `VOUCHED_RATE_SOURCES` is the frozen allow-list they key on. **Reporting only**: no incident,
|
|
2096
|
+
no class, no halt. CLI lands at N5; the
|
|
1981
2097
|
panel reads the same file at N6.
|
|
1982
2098
|
|
|
2099
|
+
### `runBehaviour(events, { runId? })` / `formatBehaviour(summary)` — `src/behaviour.js`
|
|
2100
|
+
|
|
2101
|
+
Build-list item 2 (agreed 2026-08-23): one report-only text block summarizing what a run
|
|
2102
|
+
DID with its tools, computed entirely from the run's own gate-audit JSONL. **Gates nothing,
|
|
2103
|
+
changes no verdict, writes NOTHING to the spine** — a pure function over records that already
|
|
2104
|
+
exist, same posture as `spendProvenance` above. `runBehaviour` counts and returns the summary
|
|
2105
|
+
object; `formatBehaviour` renders it as the printed block:
|
|
2106
|
+
|
|
2107
|
+
```
|
|
2108
|
+
94 tool calls · 61 read, 28 grep, 5 recall
|
|
2109
|
+
17 exact repeats (~18%)
|
|
2110
|
+
```
|
|
2111
|
+
|
|
2112
|
+
`llm` action rows are provider rounds, not tool calls, and are always excluded; only
|
|
2113
|
+
`decision: 'allow'` rows count toward the total (a `deny` is tallied separately as `N denied`,
|
|
2114
|
+
shown only when nonzero). **The exact-repeat key is the FULL recorded action — `type` + `path`
|
|
2115
|
+
+ every key of `args`** (sorted, stably serialized), never collapsed to `{type, path}`: if the
|
|
2116
|
+
gate ever starts recording byte ranges or offsets, two different slices of one file must not
|
|
2117
|
+
read as the same repeated call. Repeat detection is therefore exactly as sharp as what the gate
|
|
2118
|
+
records, no sharper — a tool that reads identical bytes through two differently-shaped argument
|
|
2119
|
+
objects will not be caught by this. `scripts/behaviour-readout.mjs <gate-audit.jsonl> [run_id]`
|
|
2120
|
+
prints the block for one run or every run_id found in the file, skipping malformed lines with
|
|
2121
|
+
a count rather than throwing.
|
|
2122
|
+
|
|
2123
|
+
```js
|
|
2124
|
+
import { runBehaviour, formatBehaviour } from 'bareloop';
|
|
2125
|
+
const events = fs.readFileSync(auditFile, 'utf8').trim().split('\n').map(JSON.parse);
|
|
2126
|
+
console.log(formatBehaviour(runBehaviour(events, { runId })));
|
|
2127
|
+
```
|
|
2128
|
+
|
|
2129
|
+
#### `memory-cache` — the read shim's own end-of-run readout (`src/readshim.js`, `src/planrun.js`)
|
|
2130
|
+
|
|
2131
|
+
A single spine record, emitted once by `runPlan` right before it returns (so it lands on the
|
|
2132
|
+
spine before the caller's `job-end`), reporting what the READ SHIM (L1 pointer + L4 cap) saved
|
|
2133
|
+
this run: `{pointered, capped, bytesWithheld, approxTokens}`. `pointered` counts re-reads of
|
|
2134
|
+
unchanged content answered with a pointer instead of the bytes; `capped` counts reads served as
|
|
2135
|
+
a bounded slice; `bytesWithheld` sums exactly the bytes NOT re-sent in both cases (never
|
|
2136
|
+
estimated) — the diff lever (L2) isn't counted, it's a different response shape. `approxTokens`
|
|
2137
|
+
is `Math.round(bytesWithheld / 4)`, a bytes→tokens ESTIMATE named accordingly so nothing reads
|
|
2138
|
+
it as measured. **Emitted only when the shim is armed** (any `readShim` arm but the off one) —
|
|
2139
|
+
an unarmed run emits no record at all, never a fabricated zero. It carries no cost fields, so it
|
|
2140
|
+
is not a spend record and no spend-slicing instrument needs to account for it.
|
|
2141
|
+
`scripts/run-u.mjs` prints it as a `MEMORY-CACHE` line right after `BEHAVIOUR` when armed
|
|
2142
|
+
(`no record` if the run ended before the summary could fire); `scripts/behaviour-readout.mjs`
|
|
2143
|
+
prints the same line when handed the run's spine file as its optional third positional.
|
|
2144
|
+
|
|
1983
2145
|
## Architecture
|
|
1984
2146
|
|
|
1985
2147
|
Three layers. An **outer shell** (dumb, permanent): per-run budget cap via bareguard,
|
|
@@ -2011,7 +2173,7 @@ is disclosed to the authoring agent; only admitted verbs are callable per job.
|
|
|
2011
2173
|
## Gotchas
|
|
2012
2174
|
|
|
2013
2175
|
*TBD from real adopter friction; recorded here as they're found (repo-side friction goes
|
|
2014
|
-
to `docs/FINDINGS.md`).*
|
|
2176
|
+
to `docs/logs/FINDINGS.md`).*
|
|
2015
2177
|
|
|
2016
2178
|
## Constraints
|
|
2017
2179
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "bareloop",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"description": "Workflows that earn their own design, with receipts — agent-authored workflows for repeated, long, verifiable jobs that improve across runs under an un-gameable gate.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./src/index.js",
|
|
@@ -23,7 +23,7 @@
|
|
|
23
23
|
"prepublishOnly": "npm run build:types"
|
|
24
24
|
},
|
|
25
25
|
"dependencies": {
|
|
26
|
-
"bare-agent": "^0.
|
|
26
|
+
"bare-agent": "^0.39.0",
|
|
27
27
|
"bareguard": "^0.13.0",
|
|
28
28
|
"litectx": "^0.32.0"
|
|
29
29
|
},
|
package/src/behaviour.js
ADDED
|
@@ -0,0 +1,150 @@
|
|
|
1
|
+
// The run behaviour summary (PRD build-list §2, agreed 2026-08-23): ONE
|
|
2
|
+
// report-only text block computed entirely from a run's gate-audit JSONL.
|
|
3
|
+
// It gates NOTHING, mints no verdict, writes NOTHING to the spine — a pure
|
|
4
|
+
// read of records that already exist. "We can see what a run cost, never
|
|
5
|
+
// what it wasted" is the gap this closes; item 4's five-minute-replay test
|
|
6
|
+
// gets this as its cheap first slice.
|
|
7
|
+
//
|
|
8
|
+
// Gate-audit rows this reads (observed shape, this repo's real archive):
|
|
9
|
+
// {ts, seq, run_id, phase, action: {type, path?, args?}, decision, ...}
|
|
10
|
+
// `action.type` is one of `llm` (a provider round, NOT a tool call — always
|
|
11
|
+
// excluded), `read`, `edit`, `ctx_remember`. `action.args.tool` names the
|
|
12
|
+
// concrete tool for read-shaped actions (`shell_read`, `shell_grep`,
|
|
13
|
+
// `ctx_recall`, `ctx_get`, `ctx_recent`, `ctx_impact`, `ctx_related`);
|
|
14
|
+
// edit/ctx_remember rows carry no `args.tool`. `decision` is `allow` for a
|
|
15
|
+
// gated tool call, `null` for an llm row, or `deny`.
|
|
16
|
+
|
|
17
|
+
/**
|
|
18
|
+
* Stable JSON — keys sorted at every level — so two calls with the same
|
|
19
|
+
* arguments in a different insertion order (or a different property visit
|
|
20
|
+
* order out of JSON.parse) hash to the same string. Used only to build the
|
|
21
|
+
* exact-repeat key below; it is not a general-purpose canonicalizer.
|
|
22
|
+
* @param {any} v
|
|
23
|
+
* @returns {string}
|
|
24
|
+
*/
|
|
25
|
+
function stableStringify(v) {
|
|
26
|
+
if (v === null || typeof v !== 'object') return JSON.stringify(v);
|
|
27
|
+
if (Array.isArray(v)) return `[${v.map(stableStringify).join(',')}]`;
|
|
28
|
+
const keys = Object.keys(v).sort();
|
|
29
|
+
return `{${keys.map((k) => `${JSON.stringify(k)}:${stableStringify(v[k])}`).join(',')}}`;
|
|
30
|
+
}
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* The exact-repeat key for one tool-call action: `type` + `path` + a stable
|
|
34
|
+
* serialization of the WHOLE `args` object.
|
|
35
|
+
*
|
|
36
|
+
* Load-bearing, and deliberately not `{type, path}`: this repo's own history
|
|
37
|
+
* is a blind-instrument catalogue of collapsed keys reading two DIFFERENT
|
|
38
|
+
* things as the same thing (a ledger that summed input+output with no cache
|
|
39
|
+
* tiers, a gate audit that collapsed reads to `{type,path}`, `git status` as
|
|
40
|
+
* "did it write?"). If the gate ever starts recording byte ranges or offsets
|
|
41
|
+
* in `args`, two different slices of the same file must NOT count as a
|
|
42
|
+
* repeat — dropping `args` from the key would silently manufacture that
|
|
43
|
+
* exact confound here. This detector is exactly as sharp as what the gate
|
|
44
|
+
* records, no sharper: a tool that reads the same bytes through two
|
|
45
|
+
* differently-shaped argument objects will not be caught, and that is an
|
|
46
|
+
* honest limit, not a bug.
|
|
47
|
+
* @param {{type?: string, path?: string, args?: any}} action
|
|
48
|
+
* @returns {string}
|
|
49
|
+
*/
|
|
50
|
+
function repeatKey(action) {
|
|
51
|
+
return `${action?.type ?? ''} ${action?.path ?? ''} ${stableStringify(action?.args ?? null)}`;
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* The display label for one tool-call breakdown row. Kept SEPARATE from the
|
|
56
|
+
* counting key (`args.tool || action.type`, computed inline in
|
|
57
|
+
* {@link runBehaviour}): the summary object stores the raw tool name so a
|
|
58
|
+
* consumer can trace a count back to its source, and only {@link
|
|
59
|
+
* formatBehaviour} shortens it for the printed line — the agreed shape reads
|
|
60
|
+
* "read"/"grep"/"recall", not "shell_read"/"shell_grep"/"ctx_recall".
|
|
61
|
+
* @param {string} key
|
|
62
|
+
* @returns {string}
|
|
63
|
+
*/
|
|
64
|
+
function displayLabel(key) {
|
|
65
|
+
return key.replace(/^shell_/, '').replace(/^ctx_/, '');
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Summarize what a run DID with its tools, from its gate-audit records
|
|
70
|
+
* alone. Pure and read-only: no IO, no spine writes, no verdict.
|
|
71
|
+
*
|
|
72
|
+
* Counting rules:
|
|
73
|
+
* - `llm` rows are provider rounds, never tool calls — always excluded.
|
|
74
|
+
* - a tool call counts toward `totalCalls`/`byTool`/`uniquePaths`/repeats
|
|
75
|
+
* only when `decision === 'allow'`; a `deny` is tallied separately in
|
|
76
|
+
* `denied` and touches nothing else (denied ≠ done).
|
|
77
|
+
* - `byTool` is keyed by `action.args.tool` when present, else
|
|
78
|
+
* `action.type` (an edit or ctx_remember has no `args.tool`).
|
|
79
|
+
* - exact repeats: see {@link repeatKey}. A first occurrence of an action
|
|
80
|
+
* is not itself a "repeat" — only the 2nd, 3rd, ... occurrences count,
|
|
81
|
+
* so `repeats` can never exceed `totalCalls - byTool-group-count`.
|
|
82
|
+
* @param {Array<{run_id?: string, action?: {type?: string, path?: string, args?: any}, decision?: string|null}>} events
|
|
83
|
+
* @param {{runId?: string}} [opts]
|
|
84
|
+
* @returns {{totalCalls: number, byTool: Record<string, number>, denied: number,
|
|
85
|
+
* uniquePaths: number, repeats: number, repeatPct: number|null}}
|
|
86
|
+
*/
|
|
87
|
+
export function runBehaviour(events, { runId } = {}) {
|
|
88
|
+
const list = Array.isArray(events) ? events : [];
|
|
89
|
+
const scoped = runId === undefined ? list : list.filter((e) => e?.run_id === runId);
|
|
90
|
+
|
|
91
|
+
const byTool = /** @type {Record<string, number>} */ ({});
|
|
92
|
+
const paths = new Set();
|
|
93
|
+
const keyCounts = new Map();
|
|
94
|
+
let totalCalls = 0;
|
|
95
|
+
let denied = 0;
|
|
96
|
+
|
|
97
|
+
for (const e of scoped) {
|
|
98
|
+
const action = e?.action ?? {};
|
|
99
|
+
if (action.type === 'llm') continue; // a provider round, never a tool call
|
|
100
|
+
if (e?.decision === 'deny') { denied += 1; continue; }
|
|
101
|
+
if (e?.decision !== 'allow') continue; // neither allow nor deny: not a counted call
|
|
102
|
+
|
|
103
|
+
totalCalls += 1;
|
|
104
|
+
const tool = action.args?.tool ?? action.type ?? 'unknown';
|
|
105
|
+
byTool[tool] = (byTool[tool] ?? 0) + 1;
|
|
106
|
+
if (action.path) paths.add(action.path);
|
|
107
|
+
|
|
108
|
+
const k = repeatKey(action);
|
|
109
|
+
keyCounts.set(k, (keyCounts.get(k) ?? 0) + 1);
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
let repeats = 0;
|
|
113
|
+
for (const count of keyCounts.values()) if (count > 1) repeats += count - 1;
|
|
114
|
+
|
|
115
|
+
return {
|
|
116
|
+
totalCalls,
|
|
117
|
+
byTool,
|
|
118
|
+
denied,
|
|
119
|
+
uniquePaths: paths.size,
|
|
120
|
+
repeats,
|
|
121
|
+
repeatPct: totalCalls > 0 ? Math.round((repeats / totalCalls) * 100) : null,
|
|
122
|
+
};
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Render a {@link runBehaviour} summary as the agreed printable block
|
|
127
|
+
* (2–4 short lines). A zero-call run formats plainly ("0 tool calls") rather
|
|
128
|
+
* than a NaN-laced percentage — an empty run is a complete, reportable
|
|
129
|
+
* answer, not a division error.
|
|
130
|
+
* @param {ReturnType<typeof runBehaviour>} summary
|
|
131
|
+
* @returns {string}
|
|
132
|
+
*/
|
|
133
|
+
export function formatBehaviour(summary) {
|
|
134
|
+
const { totalCalls, byTool, denied, repeats, repeatPct } = summary; // uniquePaths: data-only, not in the agreed printed shape
|
|
135
|
+
const lines = [];
|
|
136
|
+
|
|
137
|
+
if (totalCalls === 0) {
|
|
138
|
+
lines.push('0 tool calls');
|
|
139
|
+
} else {
|
|
140
|
+
const breakdown = Object.entries(byTool)
|
|
141
|
+
.sort((a, b) => b[1] - a[1])
|
|
142
|
+
.map(([tool, n]) => `${n} ${displayLabel(tool)}`)
|
|
143
|
+
.join(', ');
|
|
144
|
+
lines.push(`${totalCalls} tool call${totalCalls === 1 ? '' : 's'} · ${breakdown}`);
|
|
145
|
+
lines.push(`${repeats} exact repeat${repeats === 1 ? '' : 's'} (~${repeatPct}%)`);
|
|
146
|
+
}
|
|
147
|
+
if (denied > 0) lines.push(`${denied} denied`);
|
|
148
|
+
|
|
149
|
+
return lines.join('\n');
|
|
150
|
+
}
|
package/src/bridges.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Layer 3 — the REUSE registry (design record:
|
|
2
|
-
// docs/
|
|
2
|
+
// docs/product/2026-08-01-layer-3-reuse-design.md, D1/D2/D6, frozen 2026-08-01).
|
|
3
3
|
//
|
|
4
4
|
// A BRIDGE is the plan a green actually executed, kept so the next run of the same
|
|
5
5
|
// SHAPE starts from it instead of cold. This module is the box and the label on it:
|
package/src/calibrate.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// THE CALIBRATION GATE — softgreen module 5 (design record
|
|
2
|
-
// docs/
|
|
2
|
+
// docs/product/2026-08-17-softgreen-review-door-design.md §4.4, the
|
|
3
3
|
// 2026-08-18 threshold addendum rulings 1–3, and the second same-day addendum's
|
|
4
4
|
// rulings 1 and 3).
|
|
5
5
|
//
|
package/src/cardauthor.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// THE TWO SIGNED ARTIFACTS — softgreen module 4 (design record
|
|
2
|
-
// docs/
|
|
2
|
+
// docs/product/2026-08-17-softgreen-review-door-design.md §4.3 and §4.4,
|
|
3
3
|
// plus the 2026-08-18 threshold addendum).
|
|
4
4
|
//
|
|
5
5
|
// Module 1 built the judged floor's core, module 2 wired the stage into a close,
|
package/src/index.js
CHANGED
|
@@ -82,7 +82,12 @@ export { renderListing, selectionPrompt } from './selection.js';
|
|
|
82
82
|
// credit it has no way to release. STORAGE ONLY — nothing here selects, promotes or reuses
|
|
83
83
|
// a bridge; those stay parked on `layer-3-reuse`.
|
|
84
84
|
export { validateEnvelope, resolveTrySpec, resolveReuse, reuseSpecHash, selectBridge, runReuse, REUSE_GRADED_RED, readResume, resumeTreeGate, CHECKPOINT_OUTCOMES, PAUSE_TTL_MS, checkpointAgeGate, applyDoorDecision, writeRunGreenRow } from './reuse.js';
|
|
85
|
-
|
|
85
|
+
// BA-21 pricing provenance rides out with the ledger because it is the same job: reading
|
|
86
|
+
// a run's own spend record honestly. `rateProvenance`/`spendProvenance` answer "how much of
|
|
87
|
+
// this was priced by a rate nobody vouched for" — REPORTING ONLY, no halt, no refusal.
|
|
88
|
+
// Exported because an adopter enforcing a budget against these numbers is entitled to know
|
|
89
|
+
// which of them are guesses (see bareloop.context.md, "Every cost figure … is an ESTIMATE").
|
|
90
|
+
export { classifyIncidents, foldLedger, ledgerDeltas, updateLedger, LEDGER_CLASSES, VOUCHED_RATE_SOURCES, rateProvenance, spendProvenance } from './ledger.js';
|
|
86
91
|
// THE WORK BRANCH (PRD v1.57 §3). `workBranchName` is exported so an operator runner can
|
|
87
92
|
// SHOW, before the approval gate, which branch the run will work on — the same reason the
|
|
88
93
|
// resolved per-try hash is printed there: a bound the human is agreeing to should not be
|
|
@@ -91,6 +96,16 @@ export { classifyIncidents, foldLedger, ledgerDeltas, updateLedger, LEDGER_CLASS
|
|
|
91
96
|
// still there before spending anything. The runner applies the rule itself either way —
|
|
92
97
|
// neither export is a way to opt out of it.
|
|
93
98
|
export { workBranchName, prepareWorkBranch, WORK_BRANCH_PREFIX, WORK_BRANCH_RE } from './workbranch.js';
|
|
99
|
+
// THE READ SHIM (arm-gated, OFF by default via `runJob`'s `readShim`). The cap is exported
|
|
100
|
+
// because bareloop.context.md names it, and documented-but-unexported is a false contract;
|
|
101
|
+
// the wrapper goes with it so a caller building its own worker seam wraps the SAME ledger
|
|
102
|
+
// rather than reimplementing the one rule that matters (a pointer keys on what was
|
|
103
|
+
// DELIVERED, never on path+hash).
|
|
104
|
+
//
|
|
105
|
+
// `readShimArm` is exported for the same reason the cap is: a battery driver has to be able
|
|
106
|
+
// to name and check the arm it is about to run BEFORE it launches, and the legal set living
|
|
107
|
+
// only inside a runner would leave the driver guessing at spellings the guard will reject.
|
|
108
|
+
export { createReadShim, wrapReadTool, readShimArm, readShimStrategy, READ_SHIM_CAP, READ_SHIM_STRATEGY, READ_SHIM_DIFF_STRATEGY, READ_SHIM_ARMS } from './readshim.js';
|
|
94
109
|
// ── CLOSE AUTHORING v1 (gate 4) — the user declares what done means ──────────
|
|
95
110
|
// The public surface is settled ONCE, here, at M4 (M2's header flagged the
|
|
96
111
|
// naming collision and deferred it rather than exporting piecemeal).
|
|
@@ -213,6 +228,11 @@ export {
|
|
|
213
228
|
HUMAN_PAUSE, HITL_PAUSE, HITL_DECISION_RED, HUMAN_CHECKPOINTS,
|
|
214
229
|
} from './declaredclose.js';
|
|
215
230
|
export { closeStagesOf } from './plan.js';
|
|
231
|
+
// Build-list item 2 (2026-08-23) — the run behaviour summary: a report-only
|
|
232
|
+
// read of a run's gate-audit records. Gates nothing, mints no verdict,
|
|
233
|
+
// writes nothing to the spine; `runBehaviour` computes, `formatBehaviour`
|
|
234
|
+
// renders the printable block.
|
|
235
|
+
export { runBehaviour, formatBehaviour } from './behaviour.js';
|
|
216
236
|
// M4b — the interview, the refusal, the composition, and D9's three gates.
|
|
217
237
|
// `prepareSigning` returns the resolved spec's HASH and the seed evidence; it
|
|
218
238
|
// never signs — the approvals array and the human's word are unchanged.
|
package/src/job.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// Job-spec validator — the OPERATOR-owned half of the config story (N1,
|
|
2
|
-
// design record: docs/
|
|
2
|
+
// design record: docs/product/2026-07-12-n1-job-close-schema-design.md).
|
|
3
3
|
// A job spec is the arbiter's rulebook: the close chain, the budget, the
|
|
4
4
|
// outer write fence, the escalation route. It is pure declarative data —
|
|
5
5
|
// no freeform code is expressible anywhere in it — and it validates
|
package/src/judged.js
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
// THE JUDGED FLOOR'S CORE — softgreen module 1 (design record
|
|
2
|
-
// docs/
|
|
2
|
+
// docs/product/2026-08-17-softgreen-review-door-design.md §4, plus the
|
|
3
3
|
// 2026-08-18 threshold addendum). POC: poc/softgreen-judge/poc.mjs — validated
|
|
4
4
|
// logic, rewritten here, never copied (graduation is a rewrite).
|
|
5
5
|
//
|