bareloop 0.5.1 → 0.7.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 +1129 -1
- package/README.md +40 -13
- package/bareloop.context.md +799 -245
- package/package.json +3 -3
- package/src/bridges.js +571 -0
- package/src/clock.js +233 -0
- package/src/exits.js +8 -2
- package/src/index.js +52 -17
- package/src/job.js +231 -108
- package/src/ladder.js +196 -0
- package/src/ledger.js +55 -3
- package/src/plan.js +334 -20
- package/src/planrun.js +1519 -134
- package/src/ralph.js +373 -34
- package/src/reuse.js +1602 -0
- package/src/root.js +32 -12
- package/src/run.js +129 -391
- package/src/selection.js +160 -0
- package/src/spine.js +7 -2
- package/src/stall.js +252 -0
- package/src/tools.js +611 -0
- package/src/trend.js +405 -0
- package/src/validate.js +35 -250
- package/types/bridges.d.ts +198 -0
- package/types/clock.d.ts +131 -0
- package/types/index.d.ts +7 -6
- package/types/job.d.ts +83 -8
- package/types/ladder.d.ts +86 -0
- package/types/ledger.d.ts +3 -1
- package/types/plan.d.ts +86 -7
- package/types/planrun.d.ts +142 -5
- package/types/ralph.d.ts +143 -7
- package/types/reuse.d.ts +388 -0
- package/types/root.d.ts +10 -2
- package/types/run.d.ts +56 -17
- package/types/selection.d.ts +46 -0
- package/types/spine.d.ts +8 -1
- package/types/stall.d.ts +80 -0
- package/types/tools.d.ts +262 -0
- package/types/trend.d.ts +108 -0
- package/types/validate.d.ts +21 -38
- package/src/extract.js +0 -103
- package/src/interpret.js +0 -887
- package/types/extract.d.ts +0 -25
- package/types/interpret.d.ts +0 -201
package/src/clock.js
ADDED
|
@@ -0,0 +1,233 @@
|
|
|
1
|
+
// T's wall clock — the shell's time instrument (PRD v1.27/v1.29; materials
|
|
2
|
+
// design record 2026-07-26, addenda 1 and 3).
|
|
3
|
+
//
|
|
4
|
+
// Time is arbiter territory with exactly `budgetUsd`'s status: operator input,
|
|
5
|
+
// the agent may tighten and never widen, never self-raised. The agent has no time
|
|
6
|
+
// field in the plan vocabulary at all (addendum 2), so nothing here is reachable
|
|
7
|
+
// from an agent-authored document — this module is read by the runner only.
|
|
8
|
+
//
|
|
9
|
+
// Three measured facts shape it, none of them assumed:
|
|
10
|
+
//
|
|
11
|
+
// 1. `loop.stop()` CANNOT cut an in-flight call. Measured (F61/C1): fired at
|
|
12
|
+
// 500ms against a 4,000ms hanging generate(), run() returned at 4,018ms.
|
|
13
|
+
// bare-agent reads the flag at the round boundary (loop.js:661) and between
|
|
14
|
+
// tool calls (:916). So a wall deadline is a BETWEEN-ROUND check, and
|
|
15
|
+
// enforcement is `maxWallMs + closeStages × closeTimeoutMs` — reported, never
|
|
16
|
+
// rounded down to the requested number (F6 in a time coat). The multiplier is
|
|
17
|
+
// W5: a STAGED close (PRD v1.28) runs its stages one at a time and hands EACH
|
|
18
|
+
// one the full `closeTimeoutMs` (`runStages` → `runClose`, ralph.js:257), so a
|
|
19
|
+
// 4-stage close at 900s is 60 minutes of close, not 15. The runner's own
|
|
20
|
+
// outside watchdog already sized its stale window this way
|
|
21
|
+
// (scripts/run-u.mjs: `CLOSE_TIMEOUT_MS * spec.close.length`); the clock
|
|
22
|
+
// quoting the 1× number was the same arithmetic disagreeing with itself.
|
|
23
|
+
// That figure counts ONE close, and the RUNNER is what holds both of its loops
|
|
24
|
+
// to one: past the deadline it refuses to open a new fix iteration and stops on
|
|
25
|
+
// the verdict the last close already minted (W-2), and it refuses to open a
|
|
26
|
+
// STEP's attempt on the same reading — a step that would begin past the deadline
|
|
27
|
+
// is never funded. Before that second refusal a step starting with zero time left
|
|
28
|
+
// burned every attempt it was allowed (measured on the real flow: three attempts,
|
|
29
|
+
// three judged exits, all past the deadline), because the variance meter scores a
|
|
30
|
+
// step's time share against what the run had left when the step BEGAN and so is
|
|
31
|
+
// structurally blind to a step that began with nothing.
|
|
32
|
+
// It is still not a universal bound and is not claimed as one. Two overshoots
|
|
33
|
+
// remain outside this number, both deliberate. A step ALREADY UNDER WAY when the
|
|
34
|
+
// deadline trips finishes its attempt and runs its own exit checks (themselves
|
|
35
|
+
// close stages, at the full timeout each): grading real work always completes, or
|
|
36
|
+
// the run is unreadable after the money is spent. And that same mid-step deadline
|
|
37
|
+
// still routes through the ONE replan — the meter's own terminal, unchanged — so
|
|
38
|
+
// it pays a drafting call (floored at MIN_CALL_TIMEOUT_MS) before the new plan's
|
|
39
|
+
// first step is the one refused.
|
|
40
|
+
// 2. BA-18's provider timeout is the only instrument that fires on the ABSENCE
|
|
41
|
+
// of events. Measured (F61/C2): 1,259ms against a socket that accepts and
|
|
42
|
+
// never answers; without it the bound is the OS TCP timeout (~2h). Deriving
|
|
43
|
+
// it from the remaining budget is what stops the LAST round outliving the run.
|
|
44
|
+
// 3. ~90% accuracy is the bar, by hamr's ruling (addendum 3). This module does
|
|
45
|
+
// not chase precision; it reports honestly at the precision it has.
|
|
46
|
+
//
|
|
47
|
+
// Unbounded is a REAL state, not a large number: hamr ruled `maxWallMs` has no
|
|
48
|
+
// default, so a run without one is time-unbounded by explicit operator choice and
|
|
49
|
+
// says so (F45 — a defaulted cap is a silent second ceiling).
|
|
50
|
+
|
|
51
|
+
/** BA-18's default request/idle timeout (`AnthropicProvider.timeoutMs`), the
|
|
52
|
+
* ceiling every derived per-call timeout starts from. Kept in sync deliberately:
|
|
53
|
+
* a value ABOVE the provider's own default would be inert. */
|
|
54
|
+
export const PROVIDER_TIMEOUT_MS = 600_000;
|
|
55
|
+
|
|
56
|
+
/**
|
|
57
|
+
* The floor for a derived per-call timeout. Below this a call cannot plausibly
|
|
58
|
+
* succeed, and a timeout that is certain to trip manufactures a `provider-red`
|
|
59
|
+
* out of a time budget — a transport CASUALTY, which is never evidence
|
|
60
|
+
* (F45/F48). The deadline check is what must stop a run out of time; the per-call
|
|
61
|
+
* timeout exists to bound a HANG, not to enforce the budget's last second.
|
|
62
|
+
*/
|
|
63
|
+
export const MIN_CALL_TIMEOUT_MS = 30_000;
|
|
64
|
+
|
|
65
|
+
/** bare-agent's rejection code when its request/idle timeout trips (BA-18,
|
|
66
|
+
* `provider-http.js` → `TimeoutError`). The error carries a `code` and NO
|
|
67
|
+
* `category`, so every consumer that defaults an uncategorised throw would file
|
|
68
|
+
* it as transport. */
|
|
69
|
+
export const TIMEOUT_CODE = 'ETIMEDOUT';
|
|
70
|
+
|
|
71
|
+
/** bare-agent's rejection code when its TOTAL-duration deadline trips (BA-19,
|
|
72
|
+
* `provider-http.js` → `applyRequestDeadline`). A DIFFERENT code from the idle
|
|
73
|
+
* bound's on purpose — upstream splits them precisely so a consumer routing
|
|
74
|
+
* governance stops apart from transport casualties can tell which timer fired. Like
|
|
75
|
+
* `TIMEOUT_CODE` it carries no `category`, so every default-to-transport catch site
|
|
76
|
+
* would file it as a casualty. */
|
|
77
|
+
export const DEADLINE_CODE = 'EDEADLINE';
|
|
78
|
+
|
|
79
|
+
/**
|
|
80
|
+
* F64 — is this throw the run's OWN deadline coming back dressed as a provider
|
|
81
|
+
* error? `callTimeoutMs()` hands the provider a bound derived from the wall, so
|
|
82
|
+
* when the wall is what shrank that bound, the resulting `TimeoutError` is a
|
|
83
|
+
* GOVERNANCE stop, not a casualty. Filing it as `provider-red` would discard it
|
|
84
|
+
* (a casualty is never evidence, F45/F48) and attach the F44
|
|
85
|
+
* `spendComplete:false` floor to a run that simply ran out of time.
|
|
86
|
+
*
|
|
87
|
+
* The discriminator is the wall itself, not the derived number: on a bounded
|
|
88
|
+
* clock a trip can only precede expiry when the bound came from the provider's
|
|
89
|
+
* own default (`remainingMs() > PROVIDER_TIMEOUT_MS`), and that case is transport
|
|
90
|
+
* by construction. So "already expired" IS the wall-derived set. An unbounded run
|
|
91
|
+
* can never produce one, and needs no separate `bounded` term to say so —
|
|
92
|
+
* `expired()` is false by construction with no cap. (A mutation run proved the
|
|
93
|
+
* extra term inert; the unbounded case is guarded by its own test instead, which
|
|
94
|
+
* is where that invariant belongs.)
|
|
95
|
+
*
|
|
96
|
+
* A genuine dead socket on a run whose wall has ALSO passed reads as a wall-halt.
|
|
97
|
+
* That is deliberate and honest: the run is out of time either way, the remedy is
|
|
98
|
+
* the same (raise `maxWallMs`), and the alternative — calling a governance stop
|
|
99
|
+
* transport on the chance that it was — is the failure this closes.
|
|
100
|
+
*
|
|
101
|
+
* BOTH derived bounds land here, because both are derived from the same wall:
|
|
102
|
+
* BA-18's idle timeout (`callTimeoutMs`) and BA-19's total-duration deadline
|
|
103
|
+
* (`callDeadlineMs`). Adding the second CODE does not widen the rule — the
|
|
104
|
+
* discriminator is still `clock.expired()`, never the code. That matters for the
|
|
105
|
+
* deadline specifically: a provider someone else constructed with its own
|
|
106
|
+
* `deadlineMs` could throw this code on a run with time left, and that is transport,
|
|
107
|
+
* exactly as it was before.
|
|
108
|
+
*
|
|
109
|
+
* @param {any} err the throw as caught
|
|
110
|
+
* @param {Clock} clock the run's clock
|
|
111
|
+
* @returns {boolean}
|
|
112
|
+
*/
|
|
113
|
+
export function isWallTimeout(err, clock) {
|
|
114
|
+
return !!err && (err.code === TIMEOUT_CODE || err.code === DEADLINE_CODE) && clock.expired();
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
/**
|
|
118
|
+
* @typedef {object} Clock
|
|
119
|
+
* @property {boolean} bounded whether the operator set a cap at all
|
|
120
|
+
* @property {number|null} requestedMs the operator's number; `null` when unbounded
|
|
121
|
+
* @property {number} closeStages how many close stages one verdict can pay for (W5)
|
|
122
|
+
* @property {() => number} elapsedMs wall time since the clock was created
|
|
123
|
+
* @property {() => number} remainingMs what is left, floored at 0; `Infinity` when unbounded
|
|
124
|
+
* @property {() => boolean} expired whether the deadline has passed (always false when unbounded)
|
|
125
|
+
* @property {(closeTimeoutMs: number) => number|null} enforcedMs the HONEST worst case
|
|
126
|
+
* @property {(providerDefaultMs?: number) => number} callTimeoutMs the derived per-call bound
|
|
127
|
+
* @property {() => number|null} callDeadlineMs the derived per-call TOTAL-duration ceiling; `null` when unbounded
|
|
128
|
+
* @property {(closeTimeoutMs: number) => object} report one spine-safe shape carrying both numbers
|
|
129
|
+
*/
|
|
130
|
+
|
|
131
|
+
/**
|
|
132
|
+
* Start a wall clock for one run.
|
|
133
|
+
* @param {{ maxWallMs?: number|null, closeStages?: number, now?: () => number, priorElapsedMs?: number }} [opts]
|
|
134
|
+
* `maxWallMs`: the signed operator cap, or absent/null for an honestly unbounded
|
|
135
|
+
* run. `closeStages`: how many stages the job's close runs (W5) — each one gets
|
|
136
|
+
* the FULL `closeTimeoutMs`, so this is the multiplier on the overshoot, not a
|
|
137
|
+
* decoration. Defaults to 1, the single-predicate close. `now`: injected for
|
|
138
|
+
* tests — the default is the real clock. `priorElapsedMs`: time this attempt's
|
|
139
|
+
* PREDECESSOR already consumed (see below).
|
|
140
|
+
* @returns {Clock}
|
|
141
|
+
*/
|
|
142
|
+
export function createClock({ maxWallMs = null, closeStages = 1, now = () => Date.now(), priorElapsedMs = 0 } = {}) {
|
|
143
|
+
// RESUME (module C) — hamr's checkpoint ruling in a time coat: a run killed
|
|
144
|
+
// mid-try restarts THAT try under the REMAINDER of its signed per-try numbers,
|
|
145
|
+
// because "a budget ceiling folds in prior spend so re-invoking cannot silently
|
|
146
|
+
// widen it". The fold happens HERE rather than by editing `maxWallMs`, and that
|
|
147
|
+
// is the whole point: the cap the operator signed stays the number reported and
|
|
148
|
+
// the number hashed (a rewritten cap is a new spec version needing a new
|
|
149
|
+
// signature), while the clock simply starts already partly spent. A fresh
|
|
150
|
+
// allotment on every kill would let a resumed run outlive the worst case the
|
|
151
|
+
// operator signed, one kill at a time.
|
|
152
|
+
//
|
|
153
|
+
// Belted like `maxWallMs` above: a non-finite, negative or garbage fold is 0. A
|
|
154
|
+
// NaN here would make every comparison false and silently unbound the run, which
|
|
155
|
+
// is the inverse of what the fold exists to do.
|
|
156
|
+
const prior = typeof priorElapsedMs === 'number' && Number.isFinite(priorElapsedMs) && priorElapsedMs > 0 ? priorElapsedMs : 0;
|
|
157
|
+
const startedAt = now() - prior;
|
|
158
|
+
// A cap is only a cap if it is a usable positive number. validateJob already
|
|
159
|
+
// reds anything else, so this is the belt (a caller that hand-builds a spec
|
|
160
|
+
// must not be able to turn a garbage value into a silent unbounded run OR a
|
|
161
|
+
// silent instant expiry).
|
|
162
|
+
const bounded = typeof maxWallMs === 'number' && Number.isFinite(maxWallMs) && maxWallMs > 0;
|
|
163
|
+
const cap = bounded ? maxWallMs : null;
|
|
164
|
+
// The same belt on the stage count. A close always runs at least once, so 1 is
|
|
165
|
+
// the floor: a 0 or a NaN sneaking in here would quote an enforced number BELOW
|
|
166
|
+
// the requested cap, which is the dishonesty this multiplier exists to remove.
|
|
167
|
+
const stages = typeof closeStages === 'number' && Number.isFinite(closeStages) && closeStages >= 1
|
|
168
|
+
? Math.floor(closeStages) : 1;
|
|
169
|
+
/** the ONE arithmetic, so the advertised record and the enforced worst case
|
|
170
|
+
* cannot be computed two ways (the two-transforms class, F9). */
|
|
171
|
+
const enforced = (/** @type {number} */ closeTimeoutMs) => (cap === null ? null : cap + stages * closeTimeoutMs);
|
|
172
|
+
|
|
173
|
+
const elapsedMs = () => now() - startedAt;
|
|
174
|
+
const remainingMs = () => (cap === null ? Infinity : Math.max(0, cap - elapsedMs()));
|
|
175
|
+
// Inclusive: AT the deadline is out of budget. A run resumes to cap, and the
|
|
176
|
+
// stop IS the checkpoint (v1.12) — so stopping one round early is correct and
|
|
177
|
+
// stopping one round late is the overshoot addendum 1 quantifies.
|
|
178
|
+
const expired = () => cap !== null && elapsedMs() >= cap;
|
|
179
|
+
|
|
180
|
+
return {
|
|
181
|
+
bounded,
|
|
182
|
+
requestedMs: cap,
|
|
183
|
+
closeStages: stages,
|
|
184
|
+
elapsedMs,
|
|
185
|
+
remainingMs,
|
|
186
|
+
expired,
|
|
187
|
+
enforcedMs: enforced,
|
|
188
|
+
callTimeoutMs: (providerDefaultMs = PROVIDER_TIMEOUT_MS) => {
|
|
189
|
+
if (cap === null) return providerDefaultMs;
|
|
190
|
+
// Clamped BOTH ways. Upper: never above the provider's own default, which
|
|
191
|
+
// would be inert. Lower: never below MIN_CALL_TIMEOUT_MS, and in
|
|
192
|
+
// particular never 0 — `0` DISABLES the provider timeout entirely
|
|
193
|
+
// (provider-anthropic.js), which is the exact opposite of the intent and
|
|
194
|
+
// would restore the ~2h OS-TCP hang on the last round of a tight budget.
|
|
195
|
+
return Math.min(providerDefaultMs, Math.max(MIN_CALL_TIMEOUT_MS, remainingMs()));
|
|
196
|
+
},
|
|
197
|
+
// BA-19 (F72 park D) — the TOTAL-duration ceiling beside the idle one. The idle
|
|
198
|
+
// bound resets on every byte, so a response that trickles forever never trips it
|
|
199
|
+
// (F66's 274-minute call, and F67's 81.5-minute freeze happened above it); this
|
|
200
|
+
// one is a plain timer that no byte resets.
|
|
201
|
+
//
|
|
202
|
+
// Three properties, each a rule this repo already paid for:
|
|
203
|
+
// - NULL when unbounded. An operator who set no wall gets no ceiling — a
|
|
204
|
+
// defaulted cap is a silent second ceiling (F45, and the reason `maxWallMs`
|
|
205
|
+
// has no default). The caller must OMIT the knob on null, not pass a number.
|
|
206
|
+
// - REMAINING, read at call time. A constant would bound the last call by the
|
|
207
|
+
// first call's budget, which is the thing `callTimeoutMs` already refuses.
|
|
208
|
+
// - NOT clamped to the provider default. That upper clamp exists on the idle
|
|
209
|
+
// bound because a value above the provider's own default is inert; a
|
|
210
|
+
// total-duration ceiling above it is the entire point of BA-19.
|
|
211
|
+
// The lower floor is shared with `callTimeoutMs` and for the same two reasons: a
|
|
212
|
+
// bound certain to trip manufactures a stop out of arithmetic, and `0` DISABLES
|
|
213
|
+
// the bound upstream (`if (!(deadlineMs > 0)) return`) — the exact inverse of the
|
|
214
|
+
// intent. A trip past the floor is still honest: the wall has expired by then, so
|
|
215
|
+
// `isWallTimeout` reads it as the governance stop it is.
|
|
216
|
+
callDeadlineMs: () => (cap === null ? null : Math.max(MIN_CALL_TIMEOUT_MS, remainingMs())),
|
|
217
|
+
report: (closeTimeoutMs) => ({
|
|
218
|
+
bounded,
|
|
219
|
+
requestedMs: cap,
|
|
220
|
+
// On the record, not just in the arithmetic: without it a reader seeing
|
|
221
|
+
// enforced 90min against a requested 30min cannot tell a 60-minute close
|
|
222
|
+
// timeout from four 15-minute stages, and an unexplained gap is exactly the
|
|
223
|
+
// kind of number a later reader rounds back down to the requested one.
|
|
224
|
+
closeStages: stages,
|
|
225
|
+
enforcedMs: enforced(closeTimeoutMs),
|
|
226
|
+
elapsedMs: elapsedMs(),
|
|
227
|
+
// null, not Infinity: JSON.stringify turns Infinity into null anyway, and
|
|
228
|
+
// an append-only record must not depend on that coincidence — worse, a
|
|
229
|
+
// downstream `?? 0` on an Infinity would read as "no time left" (F6 class).
|
|
230
|
+
remainingMs: cap === null ? null : remainingMs(),
|
|
231
|
+
}),
|
|
232
|
+
};
|
|
233
|
+
}
|
package/src/exits.js
CHANGED
|
@@ -9,8 +9,14 @@
|
|
|
9
9
|
// F45) and never gate intent (the F43 identical-refire trap: an allowed
|
|
10
10
|
// write that lands the same bytes is NOT a change).
|
|
11
11
|
// - a failing exit's detail is a MECHANICAL gap (a named wall, a count) —
|
|
12
|
-
// the genre that converts (F38/F46)
|
|
13
|
-
//
|
|
12
|
+
// the genre that converts (F38/F46). One detail is NOT names-and-counts:
|
|
13
|
+
// `json-valid` embeds `JSON.parse`'s own message and V8 quotes a window of
|
|
14
|
+
// the SOURCE inside it, so it can carry file bytes the worker chose. The
|
|
15
|
+
// CALLER scrubs, not this module: `runPlan` redacts every detail (the ONE
|
|
16
|
+
// `SECRET_PATTERNS` inventory) at the emission boundary, before anything
|
|
17
|
+
// rides the spine, which is append-only forever. `evalExits` is a public
|
|
18
|
+
// export — a direct caller wiring results into its own log applies its own
|
|
19
|
+
// redaction.
|
|
14
20
|
// - `check-passes` delegates through the `runCheck` seam the runner wires
|
|
15
21
|
// to the full runClose machinery; the evaluator itself never spawns. An
|
|
16
22
|
// unwired or throwing check fails CLOSED (the broken-close class: an
|
package/src/index.js
CHANGED
|
@@ -1,29 +1,64 @@
|
|
|
1
|
-
// bareloop public surface — grows one rung at a time (PRD §10).
|
|
2
|
-
//
|
|
3
|
-
//
|
|
4
|
-
//
|
|
5
|
-
//
|
|
6
|
-
// of human-signs-always (the
|
|
7
|
-
//
|
|
8
|
-
//
|
|
9
|
-
//
|
|
10
|
-
//
|
|
11
|
-
//
|
|
1
|
+
// bareloop public surface — grows one rung at a time (PRD §10). The modules
|
|
2
|
+
// compose bottom-up: makeSpine feeds ralph, ralph is the wheel every layer is
|
|
3
|
+
// made of, and runJob is the one entry.
|
|
4
|
+
// The operator-owned job spec (job-v1) is the arbiter's rulebook — validateJob
|
|
5
|
+
// and validatePlan are siblings, never extensions of each other; jobSpecHash /
|
|
6
|
+
// checkApproval are the pure half of human-signs-always (the runner enforces).
|
|
7
|
+
// runJob: approval gate → primitive smoke → the plan flow under the ONE ledger
|
|
8
|
+
// (unpriced is never free, F6). The upstream ledger folds spines into one
|
|
9
|
+
// append-only incident file; filing stays human (suggestedAsk is a seed, never
|
|
10
|
+
// an auto-file).
|
|
11
|
+
//
|
|
12
|
+
// The config-v1 exports (`validateConfig`, `interpret`, `extractRules`,
|
|
13
|
+
// `diffPaths`, `LOOP_SHAPES`, `SLOTS`, `VERBS`, `STALL_REDS`, `CLASSES`,
|
|
14
|
+
// `STEP_MODES`) were REMOVED 2026-07-26 with the operator-authored steps[] path
|
|
15
|
+
// (PRD v1.32) — a breaking change, and the reason for the version bump.
|
|
12
16
|
|
|
13
17
|
export { makeSpine } from './spine.js';
|
|
14
18
|
// CLOSE_FAULTS/scanSecrets: bareloop.context.md documents both as public API —
|
|
15
19
|
// the exports make the adopter contract true (release review 2026-07-19; the
|
|
16
20
|
// exports map admits only ".", so a deep import cannot reach them).
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
21
|
+
// runStages/checkMenu/STORE_VERBS: same reason, one rung later (review 2026-07-30)
|
|
22
|
+
// — the CHANGELOG announces runStages async beside runClose and STORE_VERBS beside
|
|
23
|
+
// the exported WRITE_VERBS, and bareloop.context.md points adopters at checkMenu by
|
|
24
|
+
// name. Documented-but-unexported is a false contract; the additions are additive.
|
|
25
|
+
export { ralph, runClose, runStages, CLOSE_FAULTS } from './ralph.js';
|
|
26
|
+
export { globToPrefix, scanSecrets } from './validate.js';
|
|
27
|
+
export { validateJob, jobSpecHash, checkApproval, checkMenu, CLOSE_TYPES, CLASS_BY_CLOSE, GOLD_COMPARE, CADENCE_UNITS, PROVIDERS, CONDITION_KEYS, TOOL_MENU, LOCKED_TOOLS, STORE_VERBS, VERDICT_TYPES, LOCKED_VERDICTS } from './job.js';
|
|
20
28
|
// Layer 2: the plan-v1 validator — the agent-authored plan doc's gate; the
|
|
21
29
|
// two-doc split's third validator never happens (plan-v1 gates the PLAN, the
|
|
22
30
|
// job spec stays the arbiter's only home).
|
|
23
|
-
export { validatePlan, EXIT_TYPES, MAX_EXITS_PER_STEP, MAX_PLAN_STEPS, WRITE_VERBS } from './plan.js';
|
|
31
|
+
export { validatePlan, stageClose, EXIT_TYPES, MAX_EXITS_PER_STEP, MAX_PLAN_STEPS, WRITE_VERBS } from './plan.js';
|
|
24
32
|
export { snapshotScope, evalExits } from './exits.js';
|
|
25
33
|
export { runPlan } from './planrun.js';
|
|
26
|
-
export { interpret, STALL_REDS } from './interpret.js';
|
|
27
|
-
export { extractRules, MAX_RULES, MAX_RULE_CHARS } from './extract.js';
|
|
28
34
|
export { runJob } from './run.js';
|
|
35
|
+
// Layer 3 — the REUSE registry (bridge-v1) and D3's display half. The registry is a
|
|
36
|
+
// directory of plain files at an OPERATOR-supplied path (never a default location), the
|
|
37
|
+
// load gate is the code half of D2's split, and `renderListing`/`selectionPrompt` are
|
|
38
|
+
// pure text: they send nothing, parse nothing and decide nothing. The selection CALL and
|
|
39
|
+
// the pin/shortlist/force-cold flow are the adopter's, which is why the pieces they need
|
|
40
|
+
// are exported rather than kept internal to the runner.
|
|
41
|
+
export {
|
|
42
|
+
BRIDGE_SCHEMA, validateBridge, deriveStatus, listingRow, loadGate,
|
|
43
|
+
mintBridge, appendGreen, appendRed,
|
|
44
|
+
loadBridge, loadRegistry, saveBridge, makeRegistry, registryExists,
|
|
45
|
+
} from './bridges.js';
|
|
46
|
+
export { renderListing, selectionPrompt } from './selection.js';
|
|
47
|
+
// Layer 3 modules 4+5 — the D7 envelope and the reuse runner. `runReuse` composes
|
|
48
|
+
// `runJob` under an operator-signed envelope (three explicit numbers, tighten-only) and
|
|
49
|
+
// is the only thing in the library that WRITES the registry — minting is a graded
|
|
50
|
+
// green's privilege (R1), never a caller's. `validateEnvelope`/`resolveTrySpec` are
|
|
51
|
+
// exported because an operator runner has to show the resolved per-try spec's HASH at
|
|
52
|
+
// its approval gate: a tightened envelope is a new spec version, and the human signs it.
|
|
53
|
+
// `resolveReuse`/`reuseSpecHash` are the pair that gate must actually PRINT: the try
|
|
54
|
+
// count multiplies the worst case, so a signature over the per-try spec alone leaves it
|
|
55
|
+
// unsigned — a runner showing `jobSpecHash(resolveTrySpec(...))` prints the same hash for
|
|
56
|
+
// 0 tries and for 9.
|
|
57
|
+
// Module C (resume after a kill) exports its two operator-side halves for the same
|
|
58
|
+
// reason: `readResume` is what turns a dead run's spine back into the state a resume
|
|
59
|
+
// continues from (which tries completed, which one restarts, on what remainder), and
|
|
60
|
+
// `resumeTreeGate` is the ruling that a resumed patient is continued dirty and never
|
|
61
|
+
// reset. Both are read by a runner BEFORE the approval gate, so neither can live behind
|
|
62
|
+
// `runReuse`'s own entry.
|
|
63
|
+
export { validateEnvelope, resolveTrySpec, resolveReuse, reuseSpecHash, selectBridge, runReuse, REUSE_GRADED_RED, readResume, resumeTreeGate } from './reuse.js';
|
|
29
64
|
export { classifyIncidents, foldLedger, ledgerDeltas, updateLedger, LEDGER_CLASSES } from './ledger.js';
|