bareloop 0.10.0 → 0.12.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 +510 -0
- package/README.md +18 -7
- package/bareloop.context.md +456 -19
- package/package.json +3 -3
- package/src/authorflow.js +290 -49
- package/src/authoring.js +258 -46
- package/src/authorjob.js +390 -32
- package/src/authorscout.js +13 -1
- package/src/bridges.js +238 -11
- package/src/calibrate.js +597 -0
- package/src/cardauthor.js +479 -0
- package/src/clock.js +21 -3
- package/src/declaredclose.js +188 -13
- package/src/index.js +90 -7
- package/src/job.js +28 -9
- package/src/judged.js +765 -0
- package/src/kinds.js +578 -13
- package/src/ledger.js +24 -1
- package/src/planrun.js +517 -14
- package/src/ralph.js +9 -0
- package/src/reuse.js +514 -96
- package/src/reviewdoor.js +246 -0
- package/src/root.js +1 -1
- package/src/run.js +81 -8
- package/src/selection.js +23 -7
- package/types/authorflow.d.ts +233 -21
- package/types/authoring.d.ts +99 -39
- package/types/authorjob.d.ts +44 -6
- package/types/authorscout.d.ts +7 -1
- package/types/bridges.d.ts +95 -1
- package/types/calibrate.d.ts +191 -0
- package/types/cardauthor.d.ts +352 -0
- package/types/clock.d.ts +5 -2
- package/types/declaredclose.d.ts +93 -6
- package/types/index.d.ts +11 -7
- package/types/job.d.ts +22 -4
- package/types/judged.d.ts +280 -0
- package/types/kinds.d.ts +227 -8
- package/types/planrun.d.ts +73 -3
- package/types/reuse.d.ts +218 -3
- package/types/reviewdoor.d.ts +81 -0
- package/types/run.d.ts +60 -2
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
|
|
@@ -211,6 +230,84 @@ export function parseValue(output: string, terms: any[], { scope, workdir }?: {
|
|
|
211
230
|
* @returns {SeedTrees}
|
|
212
231
|
*/
|
|
213
232
|
export function makeSeedTrees(): SeedTrees;
|
|
233
|
+
/**
|
|
234
|
+
* THE SIGNER'S ANSWER, normalised at the seam that accepts it — the library half
|
|
235
|
+
* of the three-button gate (2026-08-12 §1).
|
|
236
|
+
*
|
|
237
|
+
* `null` is not a refusal: no ruling at all IS the pause path, which is how every
|
|
238
|
+
* ordinary run and every first leg of a hitl run arrives here. What is refused is
|
|
239
|
+
* an answer that is not one of the three doors, and — the case the POC's negative
|
|
240
|
+
* control measured — a `rerun` with no text. Ruling 3 makes the human's words the
|
|
241
|
+
* gap the worker converts from; an empty one launches a fix worker that re-runs
|
|
242
|
+
* as though nothing had been said, which is the failure this refusal exists for.
|
|
243
|
+
*
|
|
244
|
+
* It never throws and never repairs: a decision that cannot be read is handed
|
|
245
|
+
* back as a refusal with its reason, and the caller decides what to do about it.
|
|
246
|
+
* @param {any} ruling
|
|
247
|
+
* @returns {{ok: boolean, ruling: {decision: string, text: string|null}|null, why: string|null}}
|
|
248
|
+
*/
|
|
249
|
+
export function normalizeHumanRuling(ruling: any): {
|
|
250
|
+
ok: boolean;
|
|
251
|
+
ruling: {
|
|
252
|
+
decision: string;
|
|
253
|
+
text: string | null;
|
|
254
|
+
} | null;
|
|
255
|
+
why: string | null;
|
|
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
|
+
};
|
|
214
311
|
/**
|
|
215
312
|
* Run ONE declared stage against a workdir at a seed.
|
|
216
313
|
*
|
|
@@ -254,13 +351,14 @@ export function seedRead(declaration: any, ctx: Ctx): Promise<StageResult[]>;
|
|
|
254
351
|
* room would break a documented adopter contract for nothing; the new name says
|
|
255
352
|
* exactly what differs. `src/declaredclose.js` is what bridges the two shapes.
|
|
256
353
|
* @param {any} declaration @param {Ctx} ctx
|
|
257
|
-
* @returns {Promise<{verdict: 'green'|'red'|'instrument-stop', exitCode: number, judged: boolean, firstRed: string|null, stages: StageResult[]}>}
|
|
354
|
+
* @returns {Promise<{verdict: 'green'|'red'|'instrument-stop'|'pause', exitCode: number|null, judged: boolean, firstRed: string|null, pausedAt: string|null, stages: StageResult[]}>}
|
|
258
355
|
*/
|
|
259
356
|
export function runDeclaredClose(declaration: any, ctx: Ctx): Promise<{
|
|
260
|
-
verdict: "green" | "red" | "instrument-stop";
|
|
261
|
-
exitCode: number;
|
|
357
|
+
verdict: "green" | "red" | "instrument-stop" | "pause";
|
|
358
|
+
exitCode: number | null;
|
|
262
359
|
judged: boolean;
|
|
263
360
|
firstRed: string | null;
|
|
361
|
+
pausedAt: string | null;
|
|
264
362
|
stages: StageResult[];
|
|
265
363
|
}>;
|
|
266
364
|
/** the close's two judgment exits — a stage's verdict, in the shape the
|
|
@@ -325,11 +423,84 @@ export const STOP_FAULTS: Readonly<{
|
|
|
325
423
|
}>;
|
|
326
424
|
export const AGGREGATES: readonly string[];
|
|
327
425
|
export const SIGNS: readonly number[];
|
|
328
|
-
/** every kind this executor implements.
|
|
329
|
-
*
|
|
330
|
-
*
|
|
331
|
-
*
|
|
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. */
|
|
332
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;
|
|
455
|
+
/** RULING 8 — the stages the seed-verdict read (D12) must NOT run. A judged
|
|
456
|
+
* number at seed is unstable and a person at seed is an interview, not a
|
|
457
|
+
* measurement; their bar comes from calibration (soft-green) or from the person
|
|
458
|
+
* themselves (hitl), never from the seed. The skip is RECORDED by name rather
|
|
459
|
+
* than taken in silence (F59: absent is not empty), so `seedRead` still returns
|
|
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";
|
|
465
|
+
export const SEED_EXEMPT_KINDS: readonly string[];
|
|
466
|
+
/** the one kind a PERSON renders — named once, because the runner keys three
|
|
467
|
+
* decisions on it (is there a door to answer, which stage paused, whose ruling
|
|
468
|
+
* this is) and three literals is how they come to disagree */
|
|
469
|
+
export const HUMAN_KIND: "human-confirms";
|
|
470
|
+
/** the three doors the signer is offered at a hitl pause (2026-08-12 §1, doors
|
|
471
|
+
* re-cut 2026-08-17). No fourth door, and no free-text-only variant: a red that
|
|
472
|
+
* is not one of these two is not a ruling the run can act on.
|
|
473
|
+
*
|
|
474
|
+
* `cancel` was the third door and is DELETED as a concept (hamr: *"what's the
|
|
475
|
+
* point of cancel anyways? pause can resume — that would be more honest"*). The
|
|
476
|
+
* case it served — a person who looks and does not want to carry on — is a pause
|
|
477
|
+
* nobody returns to, which the 60-day checkpoint TTL retires on its own. Nothing
|
|
478
|
+
* here forces a forever-decision at the one moment the person has least reason
|
|
479
|
+
* to make one. */
|
|
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[];
|
|
333
504
|
/**
|
|
334
505
|
* The arbiter's book NAMES, one home. F98's mechanism was exactly this list
|
|
335
506
|
* living as independent literals: the fence deny arrays (src/planrun.js,
|
|
@@ -356,6 +527,23 @@ export type Red = {
|
|
|
356
527
|
/**
|
|
357
528
|
* The arbiter's half of a stage run. Every field here is operator/runner
|
|
358
529
|
* territory: a declaration parameterises kinds, never the contracts.
|
|
530
|
+
* `humanRuling` is the SIGNER's answer at a hitl pause, carried in from the
|
|
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.
|
|
542
|
+
* `onStage` is a REPORTING seam and nothing else: the seed read spawns a real
|
|
543
|
+
* toolchain per stage (a suite is the slow one) and a whole read is one opaque
|
|
544
|
+
* await, so the shell gets told as each stage lands. It decides nothing, it is
|
|
545
|
+
* never consulted, and no verdict depends on it — the house pattern is
|
|
546
|
+
* `runJob`'s `emit` (the library reports, the shell prints).
|
|
359
547
|
*/
|
|
360
548
|
export type Ctx = {
|
|
361
549
|
workdir: string;
|
|
@@ -366,9 +554,40 @@ export type Ctx = {
|
|
|
366
554
|
gapCap?: number;
|
|
367
555
|
maxBuffer?: number;
|
|
368
556
|
seedTrees?: SeedTrees;
|
|
557
|
+
humanRuling?: {
|
|
558
|
+
decision: string;
|
|
559
|
+
text?: string | null;
|
|
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;
|
|
574
|
+
onStage?: (row: {
|
|
575
|
+
stage: string | null;
|
|
576
|
+
kind: string | null;
|
|
577
|
+
verdict: string;
|
|
578
|
+
durationMs: number;
|
|
579
|
+
}) => void;
|
|
369
580
|
};
|
|
581
|
+
/**
|
|
582
|
+
* Six outcomes, and only two of them are verdicts. `pause` is N4's fifth: the
|
|
583
|
+
* close reached a stage a machine cannot render and is WAITING on a person —
|
|
584
|
+
* neither green nor red, and never graded (the F17 forbidden-zone rule applied
|
|
585
|
+
* to a non-verdict). `skipped` is the sixth and says a stage was deliberately
|
|
586
|
+
* not run at THIS reading (ruling 8's seed exemption), which is a different
|
|
587
|
+
* fact from `not-reached` (an earlier stage decided first).
|
|
588
|
+
*/
|
|
370
589
|
export type StageResult = {
|
|
371
|
-
verdict: "green" | "red" | "instrument-stop" | "not-reached";
|
|
590
|
+
verdict: "green" | "red" | "instrument-stop" | "not-reached" | "pause" | "skipped";
|
|
372
591
|
exitCode: number | null;
|
|
373
592
|
value: number | null;
|
|
374
593
|
baseline: number | null;
|
package/types/planrun.d.ts
CHANGED
|
@@ -140,6 +140,12 @@ export function boundedNote(bounded: {
|
|
|
140
140
|
* Rule A-v2's law (final write step only, framed wide). `priorChecks`: the predecessor
|
|
141
141
|
* plan's carried checks, replan phase only — Rule B's law (keep every one). Empty/omitted
|
|
142
142
|
* renders byte-identically to the pre-rules prompt.
|
|
143
|
+
* @param {string|null} [doorNote] MODULE 8 — the signer's own words, when a previous run
|
|
144
|
+
* of this spec ended at the review door and they took the RERUN door. Additive and
|
|
145
|
+
* omitted-by-default, so every existing caller renders byte-identically. It is a
|
|
146
|
+
* REQUIREMENT the plan must meet, never a replacement for the goal: the signed goal and
|
|
147
|
+
* the signed close are unchanged, and what the person added is what the machine could
|
|
148
|
+
* not see.
|
|
143
149
|
*/
|
|
144
150
|
export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxStepRounds: number, failure: string | null, scopes: string[] | undefined, materials?: {
|
|
145
151
|
balanceUsd?: number | null;
|
|
@@ -148,7 +154,7 @@ export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxS
|
|
|
148
154
|
}, startingDraft?: object | null, checkFacts?: {
|
|
149
155
|
seedRed?: string[];
|
|
150
156
|
priorChecks?: string[];
|
|
151
|
-
}): string;
|
|
157
|
+
}, doorNote?: string | null): string;
|
|
152
158
|
/**
|
|
153
159
|
* Execute a validateJob-GREEN plan-shape job (goal/verdictType/close).
|
|
154
160
|
* Called by runJob after the approval gate, validation, and the smoke — this
|
|
@@ -167,6 +173,13 @@ export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxS
|
|
|
167
173
|
* `false` → the drafter has no tools, so a native session would report NO cost — return a
|
|
168
174
|
* metered claude-json TEXT provider (`--output-format json`, `parse:'claude-json'`) instead,
|
|
169
175
|
* so its spend is never invisible. The Loop path (`anthropic-api`) never touches this.
|
|
176
|
+
* @param {any} [opts.judgeProvider] softgreen — the provider a JUDGED close stage runs its
|
|
177
|
+
* locate call through, pinned by the operator to `JUDGE_MODEL` (src/judged.js). Separate
|
|
178
|
+
* from `provider`/`providerFor` on purpose: the judge tier is not a step knob and never
|
|
179
|
+
* agent-selectable, and the worker's binding is not a legal stand-in for it. Absent is not
|
|
180
|
+
* a fall-back — a judged stage with no seam instrument-STOPS as a wiring gap, the same
|
|
181
|
+
* answer a native job with no `nativeProvider` gets. A close with no judged stage never
|
|
182
|
+
* reads it.
|
|
170
183
|
* @param {(type: string, data?: object) => object} opts.emit spine emitter (the caller's METERED emit)
|
|
171
184
|
* @param {() => number} opts.remainingUsd the one wallet: what is left of the signed budget right now
|
|
172
185
|
* @param {() => boolean} [opts.isUnpriced] has any round come back with a null cost? (F6) — the
|
|
@@ -294,12 +307,52 @@ export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxS
|
|
|
294
307
|
* it the deterministic name would collide with that very branch and the collision walk
|
|
295
308
|
* would mint a `-2` beside the progress the resume exists to keep. Absent is the cold
|
|
296
309
|
* path. A recorded branch that no longer exists is a STOP, never a fresh start.
|
|
310
|
+
* @param {{decision: string, text?: string}|null} [opts.humanRuling] N4 — the SIGNER's
|
|
311
|
+
* answer at a hitl pause (2026-08-12 §1, re-cut 2026-08-17: accept | rerun <text> | pause), carried in
|
|
312
|
+
* by the runner on the leg that RESUMES a paused run. Absent on every ordinary run and
|
|
313
|
+
* on the leg that pauses; it is never authored, never defaulted, and never inferred.
|
|
314
|
+
*
|
|
315
|
+
* It is spent ONCE, on this leg's close readings up to the moment the fix loop opens:
|
|
316
|
+
* `accept` greens the human stage on fresh evidence (OPEN-3 — the mechanical stages
|
|
317
|
+
* re-run first, because the tree can change while a run is paused), `rerun` reds it
|
|
318
|
+
* with the human's own words as the gap, and `pause` keeps the checkpoint exactly as it
|
|
319
|
+
* is, before anything is read at all. After the fix loop opens the ruling is gone, so once the mechanical
|
|
320
|
+
* stages pass again the run PAUSES for a second review rather than converting the same
|
|
321
|
+
* sentence forever.
|
|
322
|
+
* @param {{decision: string, text?: string|null, receivedAt?: string|null}|null} [opts.heldRuling]
|
|
323
|
+
* F102 — the decision the CHECKPOINT carries (`readResume`'s `restart.pendingDecision`):
|
|
324
|
+
* an answer a person already gave, on a leg that stopped before a single round was
|
|
325
|
+
* bought for it. It is applied exactly as a fresh one is, through the same seam, so
|
|
326
|
+
* the person is never asked the same question twice; the spine records that it came
|
|
327
|
+
* from a record rather than from a person, and `receivedAt` says when they said it.
|
|
328
|
+
*
|
|
329
|
+
* A leg handed BOTH is refused (`hitl-decision-red`, naming the held decision): two
|
|
330
|
+
* answers to one question is ambiguity, and a merge would pick a winner nobody chose.
|
|
331
|
+
* Absent is every ordinary run and every leg the operator answers directly.
|
|
332
|
+
* @param {number} [opts.priorSpentUsd=0] F103 — money the CHAIN spent before this leg,
|
|
333
|
+
* for the `engagement` record's money half ONLY. It bounds nothing here: the wallet
|
|
334
|
+
* is the ledger's one level up, and `remainingUsd` already nets this fold against the
|
|
335
|
+
* signed budget on every path (the signed budget is the chain's ceiling, and a rerun
|
|
336
|
+
* spends what remains of it — never a refill). Reporting, never governance.
|
|
337
|
+
* @param {boolean|null} [opts.reviewDoor] MODULE 8 — does this run END at the review
|
|
338
|
+
* door (PRD v1.71 §3)? `true`/`false` is the operator's explicit choice in either
|
|
339
|
+
* direction; absent is the CLASS default (`doorOpens`): always for soft-green,
|
|
340
|
+
* whose credit is held until a person accepts, and never for green-class unless
|
|
341
|
+
* asked. It changes what the run RECORDS and never what it returns — the door is
|
|
342
|
+
* a disposition, and the loop's verdict is not the door's to touch.
|
|
343
|
+
* @param {{text: string, fromRunid?: string|null, receivedAt?: string|null}|null} [opts.doorRerun]
|
|
344
|
+
* MODULE 8 — the signer took the RERUN door on a previous run: a FRESH ENGAGEMENT
|
|
345
|
+
* (§3.4) against the same signed spec, carrying their words. Two things follow, and
|
|
346
|
+
* only two: the already-green shortcut is refused (the tree passing the close is
|
|
347
|
+
* precisely the state the person rejected), and the words reach the PLANNER as new
|
|
348
|
+
* authoring. It is never a `humanRuling` — that answers a close's human STAGE, and
|
|
349
|
+
* a green-class job has none. Empty words are refused at the same seam every door is.
|
|
297
350
|
* @returns {Promise<string>} 'green' | 'already-green' | 'escalated' | 'plan-red' |
|
|
298
351
|
* 'check-red' | 'close-red' | 'close-unsupported' | 'recipe-stale' | 'pricing-red' |
|
|
299
352
|
* 'branch-red' | 'cap-halt' | 'wall-halt' | 'provider-red' | 'interpreter-red' |
|
|
300
|
-
* 'step-stalled' | `step-red:<id>`
|
|
353
|
+
* 'step-stalled' | 'hitl-pause' | 'hitl-decision-red' | `step-red:<id>`
|
|
301
354
|
*/
|
|
302
|
-
export function runPlan(job: any, { workdir, provider, nativeProvider, providerFor, emit, remainingUsd, isUnpriced, spendComplete, capRuns, strikeLimit, closeTimeoutMs, maxStepRounds, layerRoot, scoutRounds, bridge, now, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch }: {
|
|
355
|
+
export function runPlan(job: any, { workdir, provider, nativeProvider, providerFor, judgeProvider, emit, remainingUsd, isUnpriced, spendComplete, capRuns, strikeLimit, closeTimeoutMs, maxStepRounds, layerRoot, scoutRounds, bridge, now, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling, heldRuling, priorSpentUsd, reviewDoor, doorRerun }: {
|
|
303
356
|
workdir: string;
|
|
304
357
|
provider: any;
|
|
305
358
|
nativeProvider?: ((o: {
|
|
@@ -308,6 +361,7 @@ export function runPlan(job: any, { workdir, provider, nativeProvider, providerF
|
|
|
308
361
|
maxTurns: number;
|
|
309
362
|
hasTools: boolean;
|
|
310
363
|
}) => any) | undefined;
|
|
364
|
+
judgeProvider?: any;
|
|
311
365
|
emit: (type: string, data?: object) => object;
|
|
312
366
|
remainingUsd: () => number;
|
|
313
367
|
isUnpriced?: (() => boolean) | undefined;
|
|
@@ -332,6 +386,22 @@ export function runPlan(job: any, { workdir, provider, nativeProvider, providerF
|
|
|
332
386
|
spendComplete?: (() => boolean) | undefined;
|
|
333
387
|
now?: (() => number) | undefined;
|
|
334
388
|
resumeBranch?: string | null | undefined;
|
|
389
|
+
humanRuling?: {
|
|
390
|
+
decision: string;
|
|
391
|
+
text?: string;
|
|
392
|
+
} | null | undefined;
|
|
393
|
+
heldRuling?: {
|
|
394
|
+
decision: string;
|
|
395
|
+
text?: string | null;
|
|
396
|
+
receivedAt?: string | null;
|
|
397
|
+
} | null | undefined;
|
|
398
|
+
priorSpentUsd?: number | undefined;
|
|
399
|
+
reviewDoor?: boolean | null | undefined;
|
|
400
|
+
doorRerun?: {
|
|
401
|
+
text: string;
|
|
402
|
+
fromRunid?: string | null;
|
|
403
|
+
receivedAt?: string | null;
|
|
404
|
+
} | null | undefined;
|
|
335
405
|
}): Promise<string>;
|
|
336
406
|
/**
|
|
337
407
|
* The recorded bound-reason's ceiling in the WORKER-facing note. The reason is a
|
package/types/reuse.d.ts
CHANGED
|
@@ -136,7 +136,8 @@ export function reuseSpecHash(resolved: {
|
|
|
136
136
|
* @param {boolean} [opts.forceCold]
|
|
137
137
|
* @param {string[]} [opts.exclude] names already tried this run
|
|
138
138
|
* @returns {Promise<any>} `{ choice, reason, called, refused, forcedCold, red, costUsd,
|
|
139
|
-
* spendComplete, candidates, listing }`
|
|
139
|
+
* spendComplete, candidates, quarantineSkips, listing }` — `quarantineSkips` names every
|
|
140
|
+
* workflow held back for want of a signer's accept, with its reason (module 6)
|
|
140
141
|
*/
|
|
141
142
|
export function selectBridge({ registry, job, ask, provider, pinned, shortlist, forceCold, exclude }: {
|
|
142
143
|
registry: unknown;
|
|
@@ -164,8 +165,11 @@ export function selectBridge({ registry, job, ask, provider, pinned, shortlist,
|
|
|
164
165
|
* 4. **Which envelope this spine belongs to** — the `reuse-start` record's own
|
|
165
166
|
* approval hash, so a resume can prove it is continuing the SAME signed run.
|
|
166
167
|
*
|
|
167
|
-
* **F45 governs the arithmetic here.** A window's spend is summed from
|
|
168
|
-
* ONLY — the
|
|
168
|
+
* **F45 governs the arithmetic here.** A window's spend is summed from
|
|
169
|
+
* `ACCOUNTED_ROUND_TYPES` ONLY — the ledger's OWN list (src/run.js), imported rather
|
|
170
|
+
* than re-spelled, so a record `runJob` charges and this reader has never heard of
|
|
171
|
+
* cannot silently widen a resumed run's ceiling by exactly that amount. It was one
|
|
172
|
+
* event (`worker-round`) until the close itself could spend (`judge-round`). The selection calls
|
|
169
173
|
* are real money and are counted, but as the RUN's cost and never as a try's: a
|
|
170
174
|
* picker's tokens are not a worker's, and attributing them to a try would misstate
|
|
171
175
|
* both the fold and the row. `worker-turn` (native attribution, cost null by design)
|
|
@@ -230,6 +234,43 @@ export function readResume(events: any[], { deathAt, direct, resumableOutcomes }
|
|
|
230
234
|
direct?: boolean;
|
|
231
235
|
resumableOutcomes?: string[];
|
|
232
236
|
}): any;
|
|
237
|
+
/**
|
|
238
|
+
* The AGE gate for a paused checkpoint — the sibling of `resumeTreeGate`, and it
|
|
239
|
+
* asks the other question a 60-day-old checkpoint raises. The tree gate asks
|
|
240
|
+
* *is this still the tree the run left?*; this asks *is this still a decision
|
|
241
|
+
* anyone is waiting on?*
|
|
242
|
+
*
|
|
243
|
+
* THREE things it deliberately does NOT do:
|
|
244
|
+
*
|
|
245
|
+
* - it does not widen any other resume rule. The 2026-08-12 ruling is about a
|
|
246
|
+
* person deciding, so `applies: false` on every other terminal: a cap-halt
|
|
247
|
+
* checkpoint is a different stop under a different allowance, and quietly
|
|
248
|
+
* ageing it out would be a governance change nobody ruled.
|
|
249
|
+
* - it does not treat an unreadable stamp as young (F6). A checkpoint that
|
|
250
|
+
* cannot say WHEN it paused has an unknown age, and unknown is refused rather
|
|
251
|
+
* than rounded down to fresh — the direction that fails safe.
|
|
252
|
+
* - it does not skip silently. An expired checkpoint comes back as a refusal
|
|
253
|
+
* that NAMES the age and the TTL, because the operator's next move (start
|
|
254
|
+
* fresh) depends on knowing which of the two it hit.
|
|
255
|
+
*
|
|
256
|
+
* The clock is injected for the same reason `runPlan`'s is: a rule measured in
|
|
257
|
+
* months cannot otherwise be tested at all.
|
|
258
|
+
*
|
|
259
|
+
* @param {any[]} events the paused run's own spine, parsed, in file order
|
|
260
|
+
* @param {{now?: () => number, ttlMs?: number}} [opts]
|
|
261
|
+
* @returns {{ok: boolean, applies: boolean, ageMs: number|null, ttlMs: number, pausedAt: string|null, detail: string|null}}
|
|
262
|
+
*/
|
|
263
|
+
export function checkpointAgeGate(events: any[], { now, ttlMs }?: {
|
|
264
|
+
now?: () => number;
|
|
265
|
+
ttlMs?: number;
|
|
266
|
+
}): {
|
|
267
|
+
ok: boolean;
|
|
268
|
+
applies: boolean;
|
|
269
|
+
ageMs: number | null;
|
|
270
|
+
ttlMs: number;
|
|
271
|
+
pausedAt: string | null;
|
|
272
|
+
detail: string | null;
|
|
273
|
+
};
|
|
233
274
|
/**
|
|
234
275
|
* The PATIENT gate for a resume, which is deliberately NOT the fresh-launch one.
|
|
235
276
|
*
|
|
@@ -360,6 +401,143 @@ export function runReuse(opts: {
|
|
|
360
401
|
runJob?: typeof shippedRunJob | undefined;
|
|
361
402
|
now?: (() => number) | undefined;
|
|
362
403
|
}): Promise<any>;
|
|
404
|
+
/**
|
|
405
|
+
* WHAT A GREEN WRITES, in ONE place. Lifted verbatim out of `runReuse`'s own
|
|
406
|
+
* closure when the U runner needed it too (2B) — two spellings of "what a green
|
|
407
|
+
* writes" would be the two-transforms class applied to the ledger itself, which is
|
|
408
|
+
* the failure `greenParts` exists one level down to prevent.
|
|
409
|
+
*
|
|
410
|
+
* Two legs, and the split is which caller already holds an entry:
|
|
411
|
+
* - A BRIDGE IN HAND (the reuse leg) APPENDS. No shape check is needed: `runJob`'s
|
|
412
|
+
* load gate already proved at the door, before a token, that this entry is the
|
|
413
|
+
* same kind of recipe as this job (a mismatch returns `recipe-stale` and never
|
|
414
|
+
* gets here).
|
|
415
|
+
* - NO BRIDGE (the cold leg, and every U run) mints — unless a name already holds
|
|
416
|
+
* greens, in which case it APPENDS, because a cold run of a job that already has
|
|
417
|
+
* a bridge is another version of that workflow and overwriting it would destroy
|
|
418
|
+
* the green that minted it.
|
|
419
|
+
*
|
|
420
|
+
* But the name is the JOB's slug, and a job's CLOSE can be re-signed under that same
|
|
421
|
+
* slug. Appending across two close shapes does two wrong things at once: it counts a
|
|
422
|
+
* green another close rendered toward this entry's distinct-patient status (a false
|
|
423
|
+
* `proven`), and it hands the load gate — which matches on exactly these stage names
|
|
424
|
+
* — a plan that satisfied a different verification. So the shape is checked with the
|
|
425
|
+
* gate's own predicate, and a mismatch neither appends NOR discards the green: it
|
|
426
|
+
* forks to a deterministic derived name, with the fork ON the record.
|
|
427
|
+
*
|
|
428
|
+
* THE HOLD IS NOT DECIDED HERE. `quarantined` comes off `greenParts` reading the
|
|
429
|
+
* class the run was signed under (`record.verdictType`, module 6), so a caller that
|
|
430
|
+
* forgets to pass it mints an UNHELD green rather than a held one — which is why
|
|
431
|
+
* both callers pass the spec's own field and never a literal.
|
|
432
|
+
*
|
|
433
|
+
* KNOWN LIMIT, named rather than discovered later: `appendGreen` carries the entry's
|
|
434
|
+
* LABEL (`goal`, `closeStageNames`, `toolsUsed`) forward untouched, so a new version
|
|
435
|
+
* whose plan uses a verb the founding green did not is not reflected in `toolsUsed`.
|
|
436
|
+
* The load gate reads that field, so the gate's verb check goes slightly stale across
|
|
437
|
+
* versions. It is not a safety hole — the gate only ever gets more permissive there,
|
|
438
|
+
* and the TWEAKED plan still passes the full `validatePlan` against the job's signed
|
|
439
|
+
* menu at draft time, which is the check that actually decides. Fixing it properly
|
|
440
|
+
* means `appendGreen` taking a label update, which is module 1's contract and hamr's
|
|
441
|
+
* call, not something to change from the caller.
|
|
442
|
+
*
|
|
443
|
+
* Returns the WRITE RECORD both runners report — never throws, and a refusal is a
|
|
444
|
+
* record with `action: 'none'` and named reds rather than an absence a reader has to
|
|
445
|
+
* branch on.
|
|
446
|
+
* @param {{registryDir: string, bridge?: any|null, meta: any, record: any}} o
|
|
447
|
+
* @returns {{name: string|null, action: string, file: string|null, reds: any[], [k: string]: any}}
|
|
448
|
+
*/
|
|
449
|
+
export function writeGreenRow({ registryDir, bridge, meta, record }: {
|
|
450
|
+
registryDir: string;
|
|
451
|
+
bridge?: any | null;
|
|
452
|
+
meta: any;
|
|
453
|
+
record: any;
|
|
454
|
+
}): {
|
|
455
|
+
name: string | null;
|
|
456
|
+
action: string;
|
|
457
|
+
file: string | null;
|
|
458
|
+
reds: any[];
|
|
459
|
+
[k: string]: any;
|
|
460
|
+
};
|
|
461
|
+
/**
|
|
462
|
+
* THE U RUNNER'S TERMINAL WRITE (2B) — does the run that just ended earn a registry
|
|
463
|
+
* row, and if so, write it.
|
|
464
|
+
*
|
|
465
|
+
* `scripts/run-u.mjs` has always kept standalone bridge FILES and never a registry,
|
|
466
|
+
* so its review door could describe a held learning credit it had no row to release:
|
|
467
|
+
* `--decide accept` reached `recordDoor`, found no green row for the runid and died
|
|
468
|
+
* `no-row-for-run`. This is the row, and STORAGE IS ALL IT IS — selection, promotion
|
|
469
|
+
* and reuse execution stay parked (layer-3-reuse). Nothing here reads a row back.
|
|
470
|
+
*
|
|
471
|
+
* FOUR REFUSALS, each one a guard that already existed somewhere and is kept:
|
|
472
|
+
* - NO REGISTRY, no row. The registry path is operator-supplied and never
|
|
473
|
+
* conjured; without one the door says, in words, that nothing was released.
|
|
474
|
+
* - ALREADY-GREEN mints nothing. The close was satisfied before this run did
|
|
475
|
+
* anything, so nothing here earned a version — the reuse-credit leak block, from
|
|
476
|
+
* the writing side rather than the door's.
|
|
477
|
+
* - A GREEN WITH NO PLAN mints nothing. The artifact that inherits is the one that
|
|
478
|
+
* RAN, and there is none (R1).
|
|
479
|
+
* - A CLASS WHOSE CREDIT IS NOT HELD mints nothing HERE. That is `green` today, and
|
|
480
|
+
* it is main's behaviour kept rather than a new rule: this runner has never
|
|
481
|
+
* written a registry row for a green-class run, and widening what enters the
|
|
482
|
+
* registry is a reuse-rung question, not a side effect of fixing a door. The
|
|
483
|
+
* predicate is `quarantinesCredit` — the ONE spelling of "is this class held" —
|
|
484
|
+
* so the row this runner mints and the row a door releases can never disagree
|
|
485
|
+
* about which classes they are talking about.
|
|
486
|
+
*
|
|
487
|
+
* The row is born HELD for exactly the reason any judged green is: `greenParts` reads
|
|
488
|
+
* `verdictType` off the record. Nothing here sets the flag.
|
|
489
|
+
* @param {{registryDir: string|null|undefined, job: any, name: string|null,
|
|
490
|
+
* outcome: string, plan: any, record: any}} o
|
|
491
|
+
* @returns {{minted: boolean, reason: string|null, write: any|null}}
|
|
492
|
+
*/
|
|
493
|
+
export function writeRunGreenRow({ registryDir, job, name, outcome, plan, record }: {
|
|
494
|
+
registryDir: string | null | undefined;
|
|
495
|
+
job: any;
|
|
496
|
+
name: string | null;
|
|
497
|
+
outcome: string;
|
|
498
|
+
plan: any;
|
|
499
|
+
record: any;
|
|
500
|
+
}): {
|
|
501
|
+
minted: boolean;
|
|
502
|
+
reason: string | null;
|
|
503
|
+
write: any | null;
|
|
504
|
+
};
|
|
505
|
+
/**
|
|
506
|
+
* Apply a signer's door decision to the workflow a run wrote its row against:
|
|
507
|
+
* record the disposition, and — on `accept` over a HELD judged green — release the
|
|
508
|
+
* learning credit that green has been carrying unspent (PRD v1.71 §3).
|
|
509
|
+
*
|
|
510
|
+
* This is the ONE seam between a person's answer and the box. It is deliberately
|
|
511
|
+
* separate from `runReuse`: the door opens AFTER the run has ended (a green run is
|
|
512
|
+
* over, non-blocking, and the person may answer minutes or days later), so the
|
|
513
|
+
* decision cannot ride the run's own return path. The runner calls this with the
|
|
514
|
+
* runid it printed at the door.
|
|
515
|
+
*
|
|
516
|
+
* Never throws and never conjures: a missing registry, a missing entry, a
|
|
517
|
+
* malformed one and a runid with no green row all come back as named reds. The
|
|
518
|
+
* loop's verdict is untouched in every branch — this writes a disposition, never a
|
|
519
|
+
* result.
|
|
520
|
+
*
|
|
521
|
+
* @param {object} opts
|
|
522
|
+
* @param {string} opts.registryDir the operator-supplied registry directory
|
|
523
|
+
* @param {string} opts.name the workflow the run wrote to
|
|
524
|
+
* @param {string} opts.runid the RUN's id, exactly as the bridge row records it
|
|
525
|
+
* @param {string} opts.decision one of the three doors
|
|
526
|
+
* @param {string|null} [opts.at] when the person answered
|
|
527
|
+
* @returns {{ok: boolean, reds: any[], released: boolean, file: string|null}}
|
|
528
|
+
*/
|
|
529
|
+
export function applyDoorDecision({ registryDir, name, runid, decision, at }: {
|
|
530
|
+
registryDir: string;
|
|
531
|
+
name: string;
|
|
532
|
+
runid: string;
|
|
533
|
+
decision: string;
|
|
534
|
+
at?: string | null | undefined;
|
|
535
|
+
}): {
|
|
536
|
+
ok: boolean;
|
|
537
|
+
reds: any[];
|
|
538
|
+
released: boolean;
|
|
539
|
+
file: string | null;
|
|
540
|
+
};
|
|
363
541
|
/** @typedef {{code: string, path: string, detail?: string}} Red */
|
|
364
542
|
/**
|
|
365
543
|
* The GRADED-RED set: the runJob outcomes in which the operator's close actually
|
|
@@ -384,6 +562,43 @@ export function runReuse(opts: {
|
|
|
384
562
|
* started.
|
|
385
563
|
*/
|
|
386
564
|
export const REUSE_GRADED_RED: readonly string[];
|
|
565
|
+
/**
|
|
566
|
+
* WHICH TERMINALS ARE A CHECKPOINT — the list a runner hands `readResume` as
|
|
567
|
+
* `resumableOutcomes`, minted HERE rather than in whichever script happens to
|
|
568
|
+
* launch a run.
|
|
569
|
+
*
|
|
570
|
+
* `readResume` deliberately takes it as a parameter and defaults to empty, and
|
|
571
|
+
* that stays true: the reuse loop's own semantics depend on the empty default.
|
|
572
|
+
* What did not exist was a canonical ANSWER, so `scripts/run-u.mjs` kept its own
|
|
573
|
+
* copy — and the exported bundle (PRD v1.44 §2: a thin runner with bareloop as a
|
|
574
|
+
* dependency) would have had to keep a third. The same reasoning that put the
|
|
575
|
+
* pause TTL in the library (OPEN-2, hamr, 2026-08-13) puts this here.
|
|
576
|
+
*
|
|
577
|
+
* Every entry is a stop that left WORK ON DISK and an allowance unspent — money,
|
|
578
|
+
* time, a self-healed stall, and now a person who has not answered yet. A verdict
|
|
579
|
+
* already rendered is never on this list: `green`/`escalated` are graded rows, and
|
|
580
|
+
* re-buying one would pay twice for an answer already in hand. (The signer's own
|
|
581
|
+
* `hitl-cancel` terminal used to be the other example; the door was deleted on
|
|
582
|
+
* 2026-08-17 — a person who does not want to carry on pauses, and the checkpoint
|
|
583
|
+
* they keep is retired by the TTL rather than by a terminal.)
|
|
584
|
+
*
|
|
585
|
+
* The human half is SPLICED IN from `declaredclose.js` rather than spelled again,
|
|
586
|
+
* because the try loop below reads the two halves differently and a second copy is
|
|
587
|
+
* how they would come to disagree about which member is which: the first three are
|
|
588
|
+
* allowances the loop's remaining tries are meant to spend against, and the human
|
|
589
|
+
* ones are the stop no further try can change (see `hardStop`).
|
|
590
|
+
*/
|
|
591
|
+
export const CHECKPOINT_OUTCOMES: readonly string[];
|
|
592
|
+
/**
|
|
593
|
+
* THE PAUSE TTL (2026-08-12 §2): a hitl checkpoint is kept for 60 days.
|
|
594
|
+
*
|
|
595
|
+
* OPEN-2, RULED (hamr, in-turn, 2026-08-13): **the TTL lives in the LIBRARY**, so
|
|
596
|
+
* the exported bundle (a headless runner with bareloop as a dependency, PRD v1.44
|
|
597
|
+
* §2) inherits it instead of every runner re-implementing it. A script-only TTL
|
|
598
|
+
* would vanish on export — the one place the rule is most needed and least
|
|
599
|
+
* likely to be re-typed.
|
|
600
|
+
*/
|
|
601
|
+
export const PAUSE_TTL_MS: number;
|
|
387
602
|
export type Red = {
|
|
388
603
|
code: string;
|
|
389
604
|
path: string;
|