bareloop 0.11.0 → 0.13.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +252 -0
- package/README.md +3 -2
- package/bareloop.context.md +387 -9
- package/package.json +1 -1
- package/src/authorflow.js +176 -33
- package/src/authoring.js +170 -25
- package/src/authorjob.js +340 -26
- package/src/bridges.js +237 -10
- package/src/calibrate.js +597 -0
- package/src/cardauthor.js +479 -0
- package/src/declaredclose.js +108 -10
- package/src/index.js +100 -9
- package/src/job.js +27 -13
- package/src/judged.js +765 -0
- package/src/kinds.js +390 -6
- package/src/ledger.js +102 -0
- package/src/plan.js +43 -2
- package/src/planrun.js +492 -26
- package/src/readshim.js +538 -0
- package/src/reuse.js +322 -90
- package/src/reviewdoor.js +246 -0
- package/src/run.js +95 -6
- package/src/selection.js +22 -6
- package/src/tools.js +34 -2
- package/types/authorflow.d.ts +147 -9
- package/types/authoring.d.ts +54 -8
- package/types/authorjob.d.ts +36 -6
- package/types/bridges.d.ts +95 -1
- package/types/calibrate.d.ts +191 -0
- package/types/cardauthor.d.ts +352 -0
- package/types/declaredclose.d.ts +34 -5
- package/types/index.d.ts +12 -7
- package/types/job.d.ts +22 -9
- package/types/judged.d.ts +280 -0
- package/types/kinds.d.ts +151 -4
- package/types/ledger.d.ts +58 -0
- package/types/plan.d.ts +19 -2
- package/types/planrun.d.ts +91 -2
- package/types/readshim.d.ts +117 -0
- package/types/reuse.d.ts +146 -3
- package/types/reviewdoor.d.ts +81 -0
- package/types/run.d.ts +63 -1
- package/types/tools.d.ts +17 -1
package/types/job.d.ts
CHANGED
|
@@ -153,16 +153,29 @@ export const MIN_WALL_MS: 120000;
|
|
|
153
153
|
* counts admission demand, and a generic invalid-value would bury it as a typo. */
|
|
154
154
|
export const LOCKED_TOOLS: readonly string[];
|
|
155
155
|
/** Layer 2 (design record 2026-07-21, decision 5): the verdict-type radio of
|
|
156
|
-
* the four-field plan shape.
|
|
157
|
-
* declared-but-locked
|
|
158
|
-
*
|
|
159
|
-
*
|
|
156
|
+
* the four-field plan shape. All three are ADMITTED as of softgreen module 3;
|
|
157
|
+
* the declared-but-locked mechanism below (`LOCKED_VERDICTS`) is what carried
|
|
158
|
+
* them here one rung at a time — declaring a locked one is a `request-red` (the
|
|
159
|
+
* tool-menu pattern: disclosure ≠ admission), so the ledger counted the demand
|
|
160
|
+
* instead of the schema being reshaped when the rung finally landed. */
|
|
160
161
|
export const VERDICT_TYPES: readonly string[];
|
|
161
|
-
/** N4 slice 1
|
|
162
|
-
*
|
|
163
|
-
*
|
|
164
|
-
*
|
|
165
|
-
*
|
|
162
|
+
/** N4 slice 1 admitted `hitl`; softgreen module 3 admits `soft-green`, and the
|
|
163
|
+
* menu is now EMPTY — every class the radio names is built.
|
|
164
|
+
*
|
|
165
|
+
* `soft-green`'s lock was never a policy, it was a missing floor: a judged
|
|
166
|
+
* verdict with no ruler mints nothing honestly. Modules 1 and 2 built the ruler
|
|
167
|
+
* (a pinned judge that only LOCATES, an arbiter-owned `decide()`, a calibration
|
|
168
|
+
* gate) and wired it as the `judged-floor` kind, so the lock has nothing left to
|
|
169
|
+
* protect (design record 2026-08-17 §4, and the 2026-08-18 thresholds addendum).
|
|
170
|
+
*
|
|
171
|
+
* EMPTY here means "nothing is locked", which is a TRUE statement about a closed
|
|
172
|
+
* menu — not F59's absent-read-as-empty, where a `{}` stood in for a fact nobody
|
|
173
|
+
* gathered. The mechanism below stays exactly as it is: it is read at call time
|
|
174
|
+
* from this constant, so re-locking a class (or admitting a fourth) is one edit
|
|
175
|
+
* and the counted `request-red` path comes back with it. `LOCKED_CLASSES` in
|
|
176
|
+
* src/authoring.js is the twin and moved in the same commit; the suite pins the
|
|
177
|
+
* two identical. */
|
|
178
|
+
/** @type {readonly string[]} */
|
|
166
179
|
export const LOCKED_VERDICTS: readonly string[];
|
|
167
180
|
export const CADENCE_UNITS: readonly string[];
|
|
168
181
|
/** SP-2: API-first; local deferred (PRD §5/§8). `clipipe-subscription` (F42,
|
|
@@ -0,0 +1,280 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The rubric card: Q6's answer compiled into enumerated items, signed by the
|
|
3
|
+
* person and enumerated in the spec hash. `rule` selects from `JUDGE_RULES`;
|
|
4
|
+
* `text` is the signer's own words, carried so a red can be explained in them.
|
|
5
|
+
*
|
|
6
|
+
* Wherever a schema takes free text but only a closed set can satisfy it, hand
|
|
7
|
+
* over the enumerated set — so `rule` is a SELECT and `text` is the only free
|
|
8
|
+
* field, and it decides nothing.
|
|
9
|
+
* @param {any} card
|
|
10
|
+
* @returns {{ok: boolean, reds: string[]}}
|
|
11
|
+
*/
|
|
12
|
+
export function validateCard(card: any): {
|
|
13
|
+
ok: boolean;
|
|
14
|
+
reds: string[];
|
|
15
|
+
};
|
|
16
|
+
/**
|
|
17
|
+
* THE CALIBRATION SET's gate. Never throws on proposed input — every failure is a
|
|
18
|
+
* named red in the shipped `{ok, reds}` shape.
|
|
19
|
+
*
|
|
20
|
+
* A CASE is `{id, artifact, expect: {verdict, reds: [{rule, fn}]}}`:
|
|
21
|
+
* `artifact` is REAL TEXT — what LOCATE will actually be pointed at, never a
|
|
22
|
+
* description of it; `verdict` is what `decide()` must render; and `reds` is the
|
|
23
|
+
* ITEMIZED expectation, because the floor is *graded correctly WITH ITEMIZED
|
|
24
|
+
* REDS* — a pipe that reds the right case for the wrong reason has not been
|
|
25
|
+
* calibrated, it has been lucky. `rule` selects from the CARD's own lines (the
|
|
26
|
+
* §4.3 ceiling made mechanical: the judge only ever checks lines the card
|
|
27
|
+
* names) and `fn` is the address `decide()` itself reports.
|
|
28
|
+
*
|
|
29
|
+
* THE SIZE and THE POLARITY are separate reds on purpose: one is hamr's
|
|
30
|
+
* threshold, the other is *the test must be able to FAIL*. A set of ten that can
|
|
31
|
+
* only pass is exactly as unusable as a set of nine, and one red for both would
|
|
32
|
+
* send an operator to fix the wrong half.
|
|
33
|
+
*
|
|
34
|
+
* `card` is REQUIRED and its absence is a red rather than a skipped check — a
|
|
35
|
+
* validator handed no card would report a set it never examined against anything.
|
|
36
|
+
* @param {any} cases @param {{card?: any}} [o]
|
|
37
|
+
* @returns {{ok: boolean, reds: {code: string, path: string, detail: string, [k: string]: any}[]}}
|
|
38
|
+
*/
|
|
39
|
+
export function validateCalibrationSet(cases: any, { card }?: {
|
|
40
|
+
card?: any;
|
|
41
|
+
}): {
|
|
42
|
+
ok: boolean;
|
|
43
|
+
reds: {
|
|
44
|
+
code: string;
|
|
45
|
+
path: string;
|
|
46
|
+
detail: string;
|
|
47
|
+
[k: string]: any;
|
|
48
|
+
}[];
|
|
49
|
+
};
|
|
50
|
+
/**
|
|
51
|
+
* BOTH signed artifacts, judged independently and reported together. The card
|
|
52
|
+
* goes through `validateCard` rather than a second spelling of it.
|
|
53
|
+
* @param {{card: any, cases: any}} o
|
|
54
|
+
* @returns {{ok: boolean, reds: {code: string, path: string, detail: string, [k: string]: any}[]}}
|
|
55
|
+
*/
|
|
56
|
+
export function validateJudgedArtifacts({ card, cases }: {
|
|
57
|
+
card: any;
|
|
58
|
+
cases: any;
|
|
59
|
+
}): {
|
|
60
|
+
ok: boolean;
|
|
61
|
+
reds: {
|
|
62
|
+
code: string;
|
|
63
|
+
path: string;
|
|
64
|
+
detail: string;
|
|
65
|
+
[k: string]: any;
|
|
66
|
+
}[];
|
|
67
|
+
};
|
|
68
|
+
/**
|
|
69
|
+
* A `decide()` result reduced to the shape a case STORES. ONE spelling, so the
|
|
70
|
+
* expectation a signer signs and the reading module 5 takes off the live pipe are
|
|
71
|
+
* one shape read twice rather than two that can disagree.
|
|
72
|
+
*
|
|
73
|
+
* `why` and `quote` are deliberately dropped: both are the judge's prose about
|
|
74
|
+
* one emission, no signer can predict either, and pinning them would fail an
|
|
75
|
+
* honest pipe on wording. What survives is the ADDRESS — which card line, which
|
|
76
|
+
* function — which is what "itemized reds" means.
|
|
77
|
+
* @param {any} decision
|
|
78
|
+
* @returns {{verdict: string, reds: {rule: string, fn: string}[]}}
|
|
79
|
+
*/
|
|
80
|
+
export function expectedOf(decision: any): {
|
|
81
|
+
verdict: string;
|
|
82
|
+
reds: {
|
|
83
|
+
rule: string;
|
|
84
|
+
fn: string;
|
|
85
|
+
}[];
|
|
86
|
+
};
|
|
87
|
+
/**
|
|
88
|
+
* The SHAPE gate on a locate emission, and only the shape: is this the object
|
|
89
|
+
* the rest of the pipe can read at all? A shape miss is an ARTIFACT-RED — the
|
|
90
|
+
* emission is broken and the caller may buy another one.
|
|
91
|
+
*
|
|
92
|
+
* What is deliberately NOT here: per-rule field presence, and an empty function
|
|
93
|
+
* list. Both are well-formed emissions whose CONTENT is unusable, and content
|
|
94
|
+
* is `decide()`'s to red on (contract (a)) — routing them here would let a
|
|
95
|
+
* retry ladder spend money re-asking a model that answered perfectly clearly.
|
|
96
|
+
* It takes the facts and NOTHING else — no card. The shape of a locate emission
|
|
97
|
+
* is the same shape whatever the card asked for, and a second parameter nobody
|
|
98
|
+
* reads is a signature promising a coupling that does not exist.
|
|
99
|
+
* @param {any} facts
|
|
100
|
+
* @returns {{ok: boolean, reds: string[]}}
|
|
101
|
+
*/
|
|
102
|
+
export function validateFacts(facts: any): {
|
|
103
|
+
ok: boolean;
|
|
104
|
+
reds: string[];
|
|
105
|
+
};
|
|
106
|
+
/**
|
|
107
|
+
* The locate prompt for ONE card — the only derivation in this module, and it
|
|
108
|
+
* runs card → prompt, one hop, one direction. A rule the card did not name buys
|
|
109
|
+
* no prompt tokens and produces no facts nobody reads.
|
|
110
|
+
* @param {any} card @returns {string}
|
|
111
|
+
*/
|
|
112
|
+
export function locatePrompt(card: any): string;
|
|
113
|
+
/**
|
|
114
|
+
* ONE locate attempt. The caller owns the ladder — this function has no retry,
|
|
115
|
+
* no cap and no opinion about how many times it should be called, because a
|
|
116
|
+
* retry is a spend decision and spend decisions are the arbiter's.
|
|
117
|
+
*
|
|
118
|
+
* THE AXIS PRECEDENCE, and the order is load-bearing:
|
|
119
|
+
* 1. THE CALL FAILED → `provider-red`. A thrown or errored call has no
|
|
120
|
+
* knowable cost, so asking the meter about it would mislabel a transport
|
|
121
|
+
* casualty as a broken meter and send the operator to fix the wrong thing.
|
|
122
|
+
* 2. THE COST IS NULL → `pricing-red`. Checked BEFORE the emission is even
|
|
123
|
+
* looked at: a retry of an unpriced call buys more spend nobody can see,
|
|
124
|
+
* so this is the non-retryable stop and it outranks every retryable one.
|
|
125
|
+
* 3. TRUNCATED → `provider-red`, on its own field, never folded into a parse
|
|
126
|
+
* error. "The model was cut off" and "the model wrote nonsense" are two
|
|
127
|
+
* findings, and one field spelling both is how a reader confuses them.
|
|
128
|
+
* 4. UNPARSEABLE or SHAPE-BROKEN → `artifact-red`. No repair, ever.
|
|
129
|
+
*
|
|
130
|
+
* On every route out, red or clean, `onCost` fires exactly once with the honest
|
|
131
|
+
* read: a paid call that leaves no meter record is F12 wearing a judge's coat.
|
|
132
|
+
* @param {{artifactText: string, card: any, loopFactory: (o: {provider?: any, system: string}) => any,
|
|
133
|
+
* maxTokens?: number, attempt?: number, onCost?: (c: {costUsd: number|null, unpricedRounds: number}) => void}} o
|
|
134
|
+
* @returns {Promise<{ok: boolean, facts: any|null, red: {axis: string, detail: string}|null,
|
|
135
|
+
* costUsd: number|null, unpricedRounds: number, truncated: boolean, parseError: boolean,
|
|
136
|
+
* raw: ReturnType<typeof scrubRaw>}>}
|
|
137
|
+
*/
|
|
138
|
+
export function runLocate({ artifactText, card, loopFactory, maxTokens, attempt, onCost }: {
|
|
139
|
+
artifactText: string;
|
|
140
|
+
card: any;
|
|
141
|
+
loopFactory: (o: {
|
|
142
|
+
provider?: any;
|
|
143
|
+
system: string;
|
|
144
|
+
}) => any;
|
|
145
|
+
maxTokens?: number;
|
|
146
|
+
attempt?: number;
|
|
147
|
+
onCost?: (c: {
|
|
148
|
+
costUsd: number | null;
|
|
149
|
+
unpricedRounds: number;
|
|
150
|
+
}) => void;
|
|
151
|
+
}): Promise<{
|
|
152
|
+
ok: boolean;
|
|
153
|
+
facts: any | null;
|
|
154
|
+
red: {
|
|
155
|
+
axis: string;
|
|
156
|
+
detail: string;
|
|
157
|
+
} | null;
|
|
158
|
+
costUsd: number | null;
|
|
159
|
+
unpricedRounds: number;
|
|
160
|
+
truncated: boolean;
|
|
161
|
+
parseError: boolean;
|
|
162
|
+
raw: ReturnType<typeof scrubRaw>;
|
|
163
|
+
}>;
|
|
164
|
+
/**
|
|
165
|
+
* Render the per-item verdicts over one locate result. PURE: same input, same
|
|
166
|
+
* output, forever — which is what makes a calibration set mean anything.
|
|
167
|
+
*
|
|
168
|
+
* FIRST-RED-WINS, in SIGNED CARD ORDER. Every item is still EVALUATED after the
|
|
169
|
+
* first red (calibration demands itemized reds, not a paragraph and not a short
|
|
170
|
+
* circuit); `firstRed` is the composed stage verdict's reason, and its
|
|
171
|
+
* stability comes from the card's own order rather than from whatever order the
|
|
172
|
+
* model happened to emit its functions in.
|
|
173
|
+
*
|
|
174
|
+
* `artifactText` is OPTIONAL and its absence is honest rather than lenient:
|
|
175
|
+
* with the artifact in hand every quote a rule consumes is verified to exist in
|
|
176
|
+
* it, and without it `decide()` simply does not make a claim it cannot support.
|
|
177
|
+
* Callers that HAVE the authoritative artifact (which is every real caller,
|
|
178
|
+
* since the same text was just judged) should always pass it.
|
|
179
|
+
* @param {any} facts a `runLocate` result's `facts`, or null
|
|
180
|
+
* @param {any} card
|
|
181
|
+
* @param {{artifactText?: string|null}} [opts]
|
|
182
|
+
* @returns {{verdict: 'pass'|'red', items: {rule: string, text: string, ok: boolean, reds: RuleRed[]}[],
|
|
183
|
+
* firstRed: string|null, reason: string|null}}
|
|
184
|
+
*/
|
|
185
|
+
export function decide(facts: any, card: any, { artifactText }?: {
|
|
186
|
+
artifactText?: string | null;
|
|
187
|
+
}): {
|
|
188
|
+
verdict: "pass" | "red";
|
|
189
|
+
items: {
|
|
190
|
+
rule: string;
|
|
191
|
+
text: string;
|
|
192
|
+
ok: boolean;
|
|
193
|
+
reds: RuleRed[];
|
|
194
|
+
}[];
|
|
195
|
+
firstRed: string | null;
|
|
196
|
+
reason: string | null;
|
|
197
|
+
};
|
|
198
|
+
/**
|
|
199
|
+
* THE JUDGE MODEL, PINNED. Never agent-selectable and never a step knob: it is
|
|
200
|
+
* the only tier with established injection resistance upstream (BA-20's
|
|
201
|
+
* INJECTION_BATTERY is haiku-4.5 only), and §4.2's whole safety argument is
|
|
202
|
+
* worth exactly as much as that evidence. A judge-model bump forces a full
|
|
203
|
+
* recalibration — mandatory, per the model-bump dead-weight replay rule.
|
|
204
|
+
*
|
|
205
|
+
* THAT IS ENFORCED, not asked for, and it is enforced in three places because a
|
|
206
|
+
* rule with no wired detector is prose (F45):
|
|
207
|
+
* 1. the value here is STORED with the set it graded — `foldJudgedArtifacts`
|
|
208
|
+
* writes it as `closeDecl.calibration.judgeModel`, inside the spec, so
|
|
209
|
+
* `jobSpecHash` covers it by construction. Bump this constant and every
|
|
210
|
+
* signed judged spec hashes differently: the signature dies, and re-signing
|
|
211
|
+
* is what re-runs the calibration gate.
|
|
212
|
+
* 2. `validateCloseDecl` REQUIRES it on any stored set — a set nobody can
|
|
213
|
+
* attribute to a tier is a floor nobody can tell apart from a bumped one.
|
|
214
|
+
* 3. `runJudgedFloor` refuses to grade when the stored tier is not this one,
|
|
215
|
+
* naming both models. It does not degrade in either direction: grading on
|
|
216
|
+
* the new tier judges against a floor nobody calibrated, and the old tier is
|
|
217
|
+
* not on offer.
|
|
218
|
+
*/
|
|
219
|
+
export const JUDGE_MODEL: "claude-haiku-4-5";
|
|
220
|
+
/**
|
|
221
|
+
* The locate call's token ceiling. The POC's two real artifacts came back well
|
|
222
|
+
* inside 2000; this doubles that for headroom on a longer artifact, and the
|
|
223
|
+
* direction is fail-safe either way — a ceiling that binds produces a TRUNCATED
|
|
224
|
+
* emission, which is a red with its own field, never a silent partial pass. A
|
|
225
|
+
* caller may override it per call; it is a default, not a threshold.
|
|
226
|
+
*/
|
|
227
|
+
export const JUDGE_MAX_TOKENS: 4000;
|
|
228
|
+
/** the label every locate call is metered and persisted under — one spelling */
|
|
229
|
+
export const LOCATE_LABEL: "judged-locate";
|
|
230
|
+
/**
|
|
231
|
+
* The THREE axes a locate attempt can fail on, enumerated because the caller
|
|
232
|
+
* ROUTES on them and a route decided by reading prose is the class this
|
|
233
|
+
* programme keeps paying for. All three are shipped bareloop classes, borrowed
|
|
234
|
+
* with their existing meanings intact:
|
|
235
|
+
* - `pricing-red` the meter is broken. NOT retryable: a retry buys more
|
|
236
|
+
* spend nobody can see.
|
|
237
|
+
* - `provider-red` the call failed or was cut off. Retryable.
|
|
238
|
+
* - `artifact-red` the call succeeded and its emission is unusable.
|
|
239
|
+
* Retryable, under the CALLER's cap — this module runs one
|
|
240
|
+
* attempt and owns no ladder.
|
|
241
|
+
*/
|
|
242
|
+
export const LOCATE_AXES: Readonly<{
|
|
243
|
+
PRICING: "pricing-red";
|
|
244
|
+
PROVIDER: "provider-red";
|
|
245
|
+
ARTIFACT: "artifact-red";
|
|
246
|
+
}>;
|
|
247
|
+
/**
|
|
248
|
+
* THE OWNED RULE TABLE. Frozen: the rulebook is the arbiter's, and a caller that
|
|
249
|
+
* could add a rule at runtime would be authoring the arbiter.
|
|
250
|
+
* @type {Readonly<Record<string, {id: string, ask: string, check: (fn: any, lines: Set<string>|null) => RuleRed[]}>>}
|
|
251
|
+
*/
|
|
252
|
+
export const JUDGE_RULES: Readonly<Record<string, {
|
|
253
|
+
id: string;
|
|
254
|
+
ask: string;
|
|
255
|
+
check: (fn: any, lines: Set<string> | null) => RuleRed[];
|
|
256
|
+
}>>;
|
|
257
|
+
/** the enumerated set a card may name — handed over verbatim in every refusal */
|
|
258
|
+
export const JUDGE_RULE_IDS: readonly string[];
|
|
259
|
+
/**
|
|
260
|
+
* THE SIZE — hamr, 2026-08-18, verbatim: *"go build 10, we could double later"*.
|
|
261
|
+
*
|
|
262
|
+
* Not a number measurement found, and not one the agent may revisit: a size
|
|
263
|
+
* change is a spec-level threshold change and stays operator territory (the
|
|
264
|
+
* standing no-agent-threshold-picking rule). The second half of his sentence is
|
|
265
|
+
* part of the ruling — doubling later is explicitly allowed — which is why it is
|
|
266
|
+
* ONE constant read by the schema, the validator and the refusal alike.
|
|
267
|
+
*/
|
|
268
|
+
export const CALIBRATION_SIZE: 10;
|
|
269
|
+
/** the two verdicts a case may expect — `decide()`'s own closed set */
|
|
270
|
+
export const CASE_VERDICTS: readonly string[];
|
|
271
|
+
export function defaultJudgeLoop({ provider, system }: {
|
|
272
|
+
provider: any;
|
|
273
|
+
system: string;
|
|
274
|
+
}): import("bare-agent").Loop;
|
|
275
|
+
export type RuleRed = {
|
|
276
|
+
fn: string;
|
|
277
|
+
why: string;
|
|
278
|
+
quote: string | null;
|
|
279
|
+
};
|
|
280
|
+
import { scrubRaw } from './text.js';
|
package/types/kinds.d.ts
CHANGED
|
@@ -1,3 +1,22 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Does this run end at a review door? The operator's flag WINS in both
|
|
3
|
+
* directions and is never inferred: `true` opens it for any class, `false` shuts
|
|
4
|
+
* it for any class, and `null`/absent is the class default above.
|
|
5
|
+
* @param {any} job the signed spec (its `verdictType` is the class)
|
|
6
|
+
* @param {boolean|null} [reviewDoor] the operator's explicit choice, or null
|
|
7
|
+
* @returns {boolean}
|
|
8
|
+
*/
|
|
9
|
+
export function doorOpens(job: any, reviewDoor?: boolean | null): boolean;
|
|
10
|
+
/**
|
|
11
|
+
* The stages an `accept` re-runs: the MECHANICAL ones, and never a judged floor
|
|
12
|
+
* or a person (`SEED_EXEMPT_KINDS`, ruling 8's set, for the same reason it was
|
|
13
|
+
* drawn — a machine may re-prove a machine's reading, and may not re-prove a
|
|
14
|
+
* judgment). hamr's N4 ruling: an accept re-runs the mechanical stages, because a
|
|
15
|
+
* door lives 60 days and nothing freezes a tree.
|
|
16
|
+
* @param {any[]|null|undefined} stages
|
|
17
|
+
* @returns {any[]}
|
|
18
|
+
*/
|
|
19
|
+
export function mechanicalStages(stages: any[] | null | undefined): any[];
|
|
1
20
|
/**
|
|
2
21
|
* D8's seed, READ rather than typed: HEAD at run start. A close stores the
|
|
3
22
|
* counting rule and never the number (D12), so which commit the run is measured
|
|
@@ -235,6 +254,60 @@ export function normalizeHumanRuling(ruling: any): {
|
|
|
235
254
|
} | null;
|
|
236
255
|
why: string | null;
|
|
237
256
|
};
|
|
257
|
+
/**
|
|
258
|
+
* WHICH ANSWER IS THIS LEG ACTING ON — the fresh one, or the one the checkpoint
|
|
259
|
+
* held? F102's cure needs one seam that can say, because two callers ask the same
|
|
260
|
+
* question (`runJob` declares the fold on `job-start` before `runPlan` reads the
|
|
261
|
+
* ruling at all), and two spellings of "is this a fresh engagement" is how the
|
|
262
|
+
* record and the clock come to disagree about the same leg.
|
|
263
|
+
*
|
|
264
|
+
* THE AMBIGUITY IS REFUSED, NEVER MERGED. A checkpoint that holds a decision and a
|
|
265
|
+
* command line that carries another are two answers to one question. Merging them
|
|
266
|
+
* picks a winner nobody chose; overriding silently discards words a person typed
|
|
267
|
+
* (the F98/F28 class at a process boundary). So it comes back as a refusal that
|
|
268
|
+
* NAMES the held decision, and the operator resolves it — by answering the held
|
|
269
|
+
* one, or by starting a fresh run against the tree as it stands.
|
|
270
|
+
*
|
|
271
|
+
* `fresh` is F103's half: a `rerun` is a FRESH ENGAGEMENT (design record §3.4 —
|
|
272
|
+
* *"redo/rerun comes with new authoring for money+time and keeps accounting of
|
|
273
|
+
* this far and this session separate counters"*), and nothing else is. An `accept`
|
|
274
|
+
* commissions no work and a `pause` runs nothing, so neither opens one.
|
|
275
|
+
*
|
|
276
|
+
* A REVIEW DOOR's rerun (module 8) is the SAME fact arriving one level out, and it
|
|
277
|
+
* is folded in here rather than beside the clock, because "is this a fresh
|
|
278
|
+
* engagement" gets ONE spelling — a second one is how a record and a clock come to
|
|
279
|
+
* disagree about the same leg, which is the very thing this resolver exists to
|
|
280
|
+
* prevent. It is deliberately NOT read as a ruling: a `humanRuling` answers a
|
|
281
|
+
* close's human STAGE and is refused for a close that has none, so the door's words
|
|
282
|
+
* stay in their own parameter and only this reading is shared.
|
|
283
|
+
*
|
|
284
|
+
* PRECEDENCE, stated because both CAN be handed to one leg: a door rerun makes the
|
|
285
|
+
* engagement fresh whatever the ruling says, and the two are OR'd, never merged.
|
|
286
|
+
* Freshness is the FAIL-SAFE direction — a fresh clock costs a leg nothing, while
|
|
287
|
+
* inheriting a dead leg's wall is F103's measured, structurally-impossible
|
|
288
|
+
* engagement. The door's own validation (empty words are refused) stays where it
|
|
289
|
+
* is, at the seam that accepts it.
|
|
290
|
+
* @param {any} fresh the answer arriving on this invocation (`--decide`), or null
|
|
291
|
+
* @param {any} held the answer the CHECKPOINT carries (`readResume`'s
|
|
292
|
+
* `restart.pendingDecision`), or null. `receivedAt` is when the PERSON said it —
|
|
293
|
+
* carried through so the resumed leg records the receipt, not its own re-reading.
|
|
294
|
+
* @param {any} [doorRerun] MODULE 8 — the review door's rerun, when this leg IS the
|
|
295
|
+
* fresh engagement a signer commissioned at a previous run's door. Presence is the
|
|
296
|
+
* whole of what is read here; the words are the accepting seam's business.
|
|
297
|
+
* @returns {{ok: boolean, why: string|null, ruling: {decision: string, text: string|null}|null,
|
|
298
|
+
* source: 'operator'|'checkpoint'|null, receivedAt: string|null, fresh: boolean}}
|
|
299
|
+
*/
|
|
300
|
+
export function resolveHumanRuling(fresh: any, held: any, doorRerun?: any): {
|
|
301
|
+
ok: boolean;
|
|
302
|
+
why: string | null;
|
|
303
|
+
ruling: {
|
|
304
|
+
decision: string;
|
|
305
|
+
text: string | null;
|
|
306
|
+
} | null;
|
|
307
|
+
source: "operator" | "checkpoint" | null;
|
|
308
|
+
receivedAt: string | null;
|
|
309
|
+
fresh: boolean;
|
|
310
|
+
};
|
|
238
311
|
/**
|
|
239
312
|
* Run ONE declared stage against a workdir at a seed.
|
|
240
313
|
*
|
|
@@ -350,17 +423,45 @@ export const STOP_FAULTS: Readonly<{
|
|
|
350
423
|
}>;
|
|
351
424
|
export const AGGREGATES: readonly string[];
|
|
352
425
|
export const SIGNS: readonly number[];
|
|
353
|
-
/** every kind this executor implements.
|
|
354
|
-
*
|
|
355
|
-
*
|
|
356
|
-
*
|
|
426
|
+
/** every kind this executor implements. It is asserted EQUAL to the catalogue's
|
|
427
|
+
* own live half by the suite, both directions: a kind the catalogue offers and
|
|
428
|
+
* this executor cannot run is a close that stops at runtime, and a kind this
|
|
429
|
+
* executor runs that the catalogue never names is a capability nothing can
|
|
430
|
+
* reach. `judged-floor` joined at softgreen module 2 — the runner and the
|
|
431
|
+
* catalogue unlock in ONE commit for exactly that reason. */
|
|
357
432
|
export const LIVE_KINDS: readonly string[];
|
|
433
|
+
/**
|
|
434
|
+
* The judged stage's ladder: ONE attempt plus ONE retry, and no more.
|
|
435
|
+
*
|
|
436
|
+
* The POC measured a malformed emission roughly 1 in 6, which is exactly the
|
|
437
|
+
* shape a single retry buys down and a longer ladder buys nothing more of — each
|
|
438
|
+
* further attempt is another paid call against the same unchanged artifact and
|
|
439
|
+
* the same unchanged prompt. It is a CEILING the arbiter owns, never a knob a
|
|
440
|
+
* declaration can widen: a stage that could re-ask a model forever is a stage
|
|
441
|
+
* that can spend a budget without ever rendering a verdict.
|
|
442
|
+
*
|
|
443
|
+
* A `pricing-red` is never retried at all (see `runJudgedFloor`): a retry of an
|
|
444
|
+
* unpriced call only buys more spend nobody can see (F6).
|
|
445
|
+
*/
|
|
446
|
+
export const JUDGE_ATTEMPTS: 2;
|
|
447
|
+
/**
|
|
448
|
+
* The ceiling on how many artifacts ONE judged stage may buy a judgement of —
|
|
449
|
+
* the sibling of `MAX_TERMS` and `MAX_STAGES`, and here for the same reason: a
|
|
450
|
+
* runaway list is a bill, not a red. Enforced by the catalogue's validator
|
|
451
|
+
* before any token, so a declaration that names more is refused at the gate
|
|
452
|
+
* rather than discovered at the invoice.
|
|
453
|
+
*/
|
|
454
|
+
export const MAX_JUDGED_PATHS: 8;
|
|
358
455
|
/** RULING 8 — the stages the seed-verdict read (D12) must NOT run. A judged
|
|
359
456
|
* number at seed is unstable and a person at seed is an interview, not a
|
|
360
457
|
* measurement; their bar comes from calibration (soft-green) or from the person
|
|
361
458
|
* themselves (hitl), never from the seed. The skip is RECORDED by name rather
|
|
362
459
|
* than taken in silence (F59: absent is not empty), so `seedRead` still returns
|
|
363
460
|
* one row per declared stage. */
|
|
461
|
+
/** the one kind a MODEL renders. Named once for the same reason `HUMAN_KIND` is:
|
|
462
|
+
* the authoring pipeline, the card, the catalogue and the close each key a
|
|
463
|
+
* decision on it, and five literals is how they come to disagree. */
|
|
464
|
+
export const JUDGED_FLOOR_KIND: "judged-floor";
|
|
364
465
|
export const SEED_EXEMPT_KINDS: readonly string[];
|
|
365
466
|
/** the one kind a PERSON renders — named once, because the runner keys three
|
|
366
467
|
* decisions on it (is there a door to answer, which stage paused, whose ruling
|
|
@@ -377,6 +478,29 @@ export const HUMAN_KIND: "human-confirms";
|
|
|
377
478
|
* here forces a forever-decision at the one moment the person has least reason
|
|
378
479
|
* to make one. */
|
|
379
480
|
export const HUMAN_DECISIONS: readonly string[];
|
|
481
|
+
/** the door's record type on the spine. It is a DISPOSITION, never a verdict:
|
|
482
|
+
* hamr's law, verbatim — *"it's important not to change the loop self verdict"*.
|
|
483
|
+
* The close mints the verdict before this is written, and nothing downstream of
|
|
484
|
+
* this record may change what the ledger recorded. */
|
|
485
|
+
export const REVIEW_DOOR: "review-door";
|
|
486
|
+
/** the terminals a door opens on: the verdict-bearing ones, and only those. A red,
|
|
487
|
+
* a casualty and a governance halt are not offered to a person as merchandise —
|
|
488
|
+
* they are already decision-ready in their own right, with their own levers. */
|
|
489
|
+
export const DOOR_OPEN_OUTCOMES: readonly string[];
|
|
490
|
+
/** the classes whose door opens WITHOUT being asked for. Soft-green is here
|
|
491
|
+
* because the judge is young: its green is quarantined at mint (module 6) and a
|
|
492
|
+
* person's `accept` is the only thing that releases the credit, so a run that
|
|
493
|
+
* never offered the door would hold its own credit forever.
|
|
494
|
+
*
|
|
495
|
+
* `green` is deliberately ABSENT, and that is the one operator-facing default in
|
|
496
|
+
* this module: a green-class run behaves exactly as it always has unless the
|
|
497
|
+
* runner asks for the door (`reviewDoor: true`, `--review-door`). PRD v1.71 §3
|
|
498
|
+
* says *"the door at the end of EVERY run"* and also that a green door is
|
|
499
|
+
* NON-BLOCKING, and the two readings differ only in whether an unasked-for door
|
|
500
|
+
* appears on an existing job's spine. Flipping it is one line — adding `'green'`
|
|
501
|
+
* here — and it is hamr's to flip, not this module's to assume. A constant, never
|
|
502
|
+
* a threshold; widening it is arbiter territory. */
|
|
503
|
+
export const REVIEW_DOOR_CLASSES: readonly string[];
|
|
380
504
|
/**
|
|
381
505
|
* The arbiter's book NAMES, one home. F98's mechanism was exactly this list
|
|
382
506
|
* living as independent literals: the fence deny arrays (src/planrun.js,
|
|
@@ -405,6 +529,16 @@ export type Red = {
|
|
|
405
529
|
* territory: a declaration parameterises kinds, never the contracts.
|
|
406
530
|
* `humanRuling` is the SIGNER's answer at a hitl pause, carried in from the
|
|
407
531
|
* runner — never authored, never defaulted, and absent on every ordinary run.
|
|
532
|
+
* `judgeLoop` is the PAID seam the judged stage runs its locate call through, and
|
|
533
|
+
* it is operator territory for the same reason the worker's provider is: this
|
|
534
|
+
* module owns no provider and never picks one, and the judge tier is PINNED
|
|
535
|
+
* (`JUDGE_MODEL`) rather than chosen. Absent is not a fall-back to some other
|
|
536
|
+
* model — the stage stops as a wiring gap, the same answer `runPlan` gives a
|
|
537
|
+
* native job with no native factory.
|
|
538
|
+
* `onJudgeCost` is the METER, and it is the only reason a judged stage can be
|
|
539
|
+
* funded honestly: this executor cannot reach the run's ledger, so every locate
|
|
540
|
+
* call's cost is reported OUT through it, once per call, on every route including
|
|
541
|
+
* the red ones — a paid call that leaves no meter record is F12 in a judge's coat.
|
|
408
542
|
* `onStage` is a REPORTING seam and nothing else: the seed read spawns a real
|
|
409
543
|
* toolchain per stage (a suite is the slow one) and a whole read is one opaque
|
|
410
544
|
* await, so the shell gets told as each stage lands. It decides nothing, it is
|
|
@@ -424,6 +558,19 @@ export type Ctx = {
|
|
|
424
558
|
decision: string;
|
|
425
559
|
text?: string | null;
|
|
426
560
|
} | null;
|
|
561
|
+
judgeLoop?: (o: {
|
|
562
|
+
system: string;
|
|
563
|
+
}) => any;
|
|
564
|
+
onJudgeCost?: (c: {
|
|
565
|
+
stage: string | null;
|
|
566
|
+
kind: string | null;
|
|
567
|
+
path: string;
|
|
568
|
+
attempt: number;
|
|
569
|
+
label: string;
|
|
570
|
+
model: string;
|
|
571
|
+
costUsd: number | null;
|
|
572
|
+
unpricedRounds: number;
|
|
573
|
+
}) => void;
|
|
427
574
|
onStage?: (row: {
|
|
428
575
|
stage: string | null;
|
|
429
576
|
kind: string | null;
|
package/types/ledger.d.ts
CHANGED
|
@@ -48,8 +48,66 @@ export function updateLedger({ ledgerFile, spineFiles }: {
|
|
|
48
48
|
appended: object[];
|
|
49
49
|
fold: Record<string, FoldEntry>;
|
|
50
50
|
};
|
|
51
|
+
/**
|
|
52
|
+
* Read one spend record's rate PROVENANCE. Four-way, and the two absent-ish cases are
|
|
53
|
+
* deliberately distinct facts:
|
|
54
|
+
* - `'vouched'` — a `VOUCHED_RATE_SOURCES` value: somebody stands behind the rate.
|
|
55
|
+
* - `'guessed'` — a rate was used and nobody vouched for it (`'tier'`, `'default'`, or
|
|
56
|
+
* any future value we do not recognise).
|
|
57
|
+
* - `'unpriced'` — `rateSource: null`: nothing was priced, so there was no rate to have
|
|
58
|
+
* guessed. Upstream said so; we are not inferring it.
|
|
59
|
+
* - `'unknown'` — the record carries no `rateSource` at all. Every round archived before
|
|
60
|
+
* this signal existed lands here and is NEVER backfilled: an absent label
|
|
61
|
+
* is reported as unknown, never reconstructed, and never rounded up to
|
|
62
|
+
* vouched (F6's unknown-is-never-zero, applied to provenance).
|
|
63
|
+
* @param {any} record a `worker-round` / `judge-round` / `worker-turn` spine record
|
|
64
|
+
* @returns {'vouched'|'guessed'|'unpriced'|'unknown'}
|
|
65
|
+
*/
|
|
66
|
+
export function rateProvenance(record: any): "vouched" | "guessed" | "unpriced" | "unknown";
|
|
67
|
+
/** @typedef {{rounds: number, usd: number, unpricedRounds: number}} ProvenanceBucket */
|
|
68
|
+
/**
|
|
69
|
+
* How much of a run's spend was priced by a guess. Pure fold over one spine's events,
|
|
70
|
+
* REPORTING ONLY — it mints nothing, halts nothing and refuses nothing.
|
|
71
|
+
*
|
|
72
|
+
* `usd` sums only FINITE costs; a round whose cost is not a finite number is counted in
|
|
73
|
+
* `unpricedRounds` instead of being summed as $0 (F6 — unpriced is never free), so a
|
|
74
|
+
* bucket's `usd` is a FLOOR whenever its `unpricedRounds` is non-zero.
|
|
75
|
+
* @param {any[]} events one spine's parsed events
|
|
76
|
+
* @returns {{vouched: ProvenanceBucket, guessed: ProvenanceBucket, unpriced: ProvenanceBucket, unknown: ProvenanceBucket}}
|
|
77
|
+
*/
|
|
78
|
+
export function spendProvenance(events: any[]): {
|
|
79
|
+
vouched: ProvenanceBucket;
|
|
80
|
+
guessed: ProvenanceBucket;
|
|
81
|
+
unpriced: ProvenanceBucket;
|
|
82
|
+
unknown: ProvenanceBucket;
|
|
83
|
+
};
|
|
51
84
|
/** severity order, worst-first (the fold's display order; frequency ranks within) */
|
|
52
85
|
export const LEDGER_CLASSES: readonly string[];
|
|
86
|
+
/**
|
|
87
|
+
* The two rate sources somebody VOUCHED for: `'provider'` (the provider reported its own
|
|
88
|
+
* authoritative cost, e.g. the claude CLI's `total_cost_usd`) and `'caller'` (we passed
|
|
89
|
+
* the rate in ourselves). Everything else bare-agent can report is a built-in guess.
|
|
90
|
+
*
|
|
91
|
+
* An ALLOW-LIST of vouched values, never a deny-list of guessed ones — this is the
|
|
92
|
+
* load-bearing line of the whole signal, for two reasons.
|
|
93
|
+
*
|
|
94
|
+
* First: BOTH of bareloop's production models resolve to `'tier'`, not to `'default'`
|
|
95
|
+
* (`claude-sonnet-5` and `claude-haiku-4-5-…` each match a recognized Claude tier by
|
|
96
|
+
* model-id substring, so bare-agent prices them off a hardcoded tier rate that could
|
|
97
|
+
* have drifted from Anthropic's). A "is this a guess?" test spelled
|
|
98
|
+
* `rateSource === 'default'` would therefore read EVERY normal bareloop round as
|
|
99
|
+
* confidently priced and see none of the population it exists to measure — this repo's
|
|
100
|
+
* blind-instrument class, seven recorded instances, and not becoming the eighth.
|
|
101
|
+
*
|
|
102
|
+
* Second: written this way, any value bare-agent adds later that we do not recognise
|
|
103
|
+
* reads as a GUESS rather than as vouched. The fail-safe direction, by construction.
|
|
104
|
+
*/
|
|
105
|
+
export const VOUCHED_RATE_SOURCES: readonly string[];
|
|
106
|
+
export type ProvenanceBucket = {
|
|
107
|
+
rounds: number;
|
|
108
|
+
usd: number;
|
|
109
|
+
unpricedRounds: number;
|
|
110
|
+
};
|
|
53
111
|
export type Sample = {
|
|
54
112
|
spine: string;
|
|
55
113
|
seq: number;
|
package/types/plan.d.ts
CHANGED
|
@@ -72,7 +72,7 @@ export function closeStagesOf(job: any): any[] | null;
|
|
|
72
72
|
* job spec. Never throws on JSON text or plain parsed data; every failure is
|
|
73
73
|
* a named red. Returns the parsed plan on ok (single parse), null on any red.
|
|
74
74
|
* @param {object|string} input parsed plan, or raw JSON text (parse failures are a red)
|
|
75
|
-
* @param {{ job?: any, maxStepRounds?: number, scopes?: string[], seedRed?: string[], priorChecks?: string[] }} [opts] `job`: the
|
|
75
|
+
* @param {{ job?: any, maxStepRounds?: number, scopes?: string[], seedRed?: string[], priorChecks?: string[], readShim?: boolean|'cap'|'diff' }} [opts] `job`: the
|
|
76
76
|
* validateJob-GREEN four-field spec (the ceiling, the fence, and the checks
|
|
77
77
|
* menu all come from it — a missing or non-plan-shape job fails CLOSED);
|
|
78
78
|
* `maxStepRounds`: the shell's per-step rounds ceiling (interpret's
|
|
@@ -94,14 +94,23 @@ export function closeStagesOf(job: any): any[] | null;
|
|
|
94
94
|
* only). Rule B — a redraft may not drop one (`check-shed`): exits without
|
|
95
95
|
* the check verify FORM alone, so a shed lets the run "green" unearned
|
|
96
96
|
* (u-msdsmkid mechanism b; the 3 archived sheds were all on step-red runs).
|
|
97
|
+
* `readShim` (G1): the read shim's ARM for this run — `false` (default), `'cap'`,
|
|
98
|
+
* `'diff'` or `true`. The arms that CAP (`'cap'`, `true`) add one rule: a step
|
|
99
|
+
* granting `read` must also grant `recall` and `get` (`read-blind`), because a
|
|
100
|
+
* capped read is only navigable with them. The arms that do not cap (`false`,
|
|
101
|
+
* `'diff'`) render byte-identically to the pre-shim validator, so the frozen A0
|
|
102
|
+
* baseline stays a baseline and A2 isolates the diff instead of importing an
|
|
103
|
+
* admission rule with it. An unrecognised value THROWS (`readShimArm`) — a param
|
|
104
|
+
* guard on the caller's own argument, never a plan red.
|
|
97
105
|
* @returns {{ ok: boolean, reds: Red[], plan: object|null }}
|
|
98
106
|
*/
|
|
99
|
-
export function validatePlan(input: object | string, { job, maxStepRounds, scopes, seedRed, priorChecks }?: {
|
|
107
|
+
export function validatePlan(input: object | string, { job, maxStepRounds, scopes, seedRed, priorChecks, readShim }?: {
|
|
100
108
|
job?: any;
|
|
101
109
|
maxStepRounds?: number;
|
|
102
110
|
scopes?: string[];
|
|
103
111
|
seedRed?: string[];
|
|
104
112
|
priorChecks?: string[];
|
|
113
|
+
readShim?: boolean | "cap" | "diff";
|
|
105
114
|
}): {
|
|
106
115
|
ok: boolean;
|
|
107
116
|
reds: Red[];
|
|
@@ -122,6 +131,14 @@ export const MAX_PLAN_STEPS: 8;
|
|
|
122
131
|
* count. Shallow entries survive the cap (see `legalScopes`) because they are
|
|
123
132
|
* the useful ones — a deep leaf is reachable via its parent. */
|
|
124
133
|
export const MAX_SCOPE_MENU: 24;
|
|
134
|
+
/** G1's pair (read shim only): the two verbs that can reach INTO a file the cap
|
|
135
|
+
* cut short — `recall` names where a symbol lives, `get` reads that symbol in
|
|
136
|
+
* full. Named here because the rule that needs them lives here; the cap that
|
|
137
|
+
* makes them mandatory lives in src/readshim.js.
|
|
138
|
+
* EXPORTED because `runPlan` refuses the same pair at $0 (see the G1 ceiling
|
|
139
|
+
* guard there): the rule and its pre-flight must read ONE spelling of which
|
|
140
|
+
* verbs count, never two that can drift apart. */
|
|
141
|
+
export const RETRIEVAL_PAIR: readonly string[];
|
|
125
142
|
/** P (design record 2026-07-28): the signed per-step model-tier menu. Closed by
|
|
126
143
|
* construction — the agent picks a tier, never names an arbitrary model string
|
|
127
144
|
* (the tier→model mapping and any effort params are the RUNNER's, per provider).
|