bareloop 0.10.0 → 0.11.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.
@@ -97,7 +97,8 @@ export function guardNames(closeDecl: any, verdictType: string): string[];
97
97
  * @param {any[]} stages declared stage descriptors, already validated
98
98
  * @param {(s: string) => string} [redact] the scrub, applied at THIS boundary
99
99
  * @param {{timeoutMs?: number, cwd?: string, seedRef?: string,
100
- * seedTrees?: any, gapCap?: number, maxBuffer?: number, baselineMode?: 'auto'|'worktree'}} [opts]
100
+ * seedTrees?: any, gapCap?: number, maxBuffer?: number, baselineMode?: 'auto'|'worktree',
101
+ * humanRuling?: {decision: string, text?: string|null}|null}} [opts]
101
102
  * @returns {Promise<any>}
102
103
  */
103
104
  export function runDeclaredStages(stages: any[], redact?: (s: string) => string, opts?: {
@@ -108,6 +109,10 @@ export function runDeclaredStages(stages: any[], redact?: (s: string) => string,
108
109
  gapCap?: number;
109
110
  maxBuffer?: number;
110
111
  baselineMode?: "auto" | "worktree";
112
+ humanRuling?: {
113
+ decision: string;
114
+ text?: string | null;
115
+ } | null;
111
116
  }): Promise<any>;
112
117
  /**
113
118
  * The trend reader's input for ONE close verdict, whichever executor produced
@@ -160,11 +165,64 @@ export const DECLARED_GAP_PREFIX: "close: ";
160
165
  * it; because every declared line carries the prefix, it designates the stage's
161
166
  * whole judged output rather than a hand-picked slice of it. */
162
167
  export const DECLARED_GAP_KEEP: string;
163
- /** The verdict class a DECLARED close may claim. Hard, and by CONSTRUCTION
164
- * rather than by assertion: every live kind is a mechanical command whose
165
- * measurement is the truth, and the two judgment kinds are locked out at the
166
- * catalogue (M2) and absent from the executor (M1). Named as data so the job
167
- * validator reads one hierarchy table rather than hard-coding a class. */
168
+ /**
169
+ * The close verdict word for a run WAITING on a person (N4 §1.3/§1.4). A
170
+ * literal, exported, because three readers key on it — the runner's pause
171
+ * branch, the ledger's excluded set, and the trend reader's blind rule — and
172
+ * three hand-spelled copies of one terminal is how they come to disagree.
173
+ *
174
+ * It is not in `CLOSE_FAULTS` and must never be added: a fault says the close
175
+ * could not render a verdict, and this says the close is not FINISHED rendering
176
+ * one. The first escalates; the second is a checkpoint the operator returns to.
177
+ */
178
+ export const HUMAN_PAUSE: "human-pause";
179
+ /**
180
+ * The RUN-level terminal names the hitl class mints one layer up from
181
+ * `HUMAN_PAUSE` (N4 §1.3–§1.5): the checkpoint, and the honest refusal of the
182
+ * operator's rerun input. Spelled once here, beside the close-verdict word they
183
+ * translate, for the same reason `HUMAN_PAUSE` is — the emitters (`planrun.js`),
184
+ * the ledger's excluded set, the resume reader (`reuse.js`), and the runner
185
+ * script all key on them, and hand-spelled copies of one terminal is how they
186
+ * come to disagree.
187
+ *
188
+ * There were three (2026-08-17): `hitl-cancel` was the signer's own terminal and
189
+ * is DELETED with the door it belonged to. A person who does not want to carry on
190
+ * now PAUSES — the same checkpoint, resumable, retired by the TTL if nobody comes
191
+ * back — so nothing in this tree mints that name any more. The ledger keeps the
192
+ * bare string in its excluded set so a spine written before this change still
193
+ * reads as governance rather than as a capability gap; that is a READER's
194
+ * backward compatibility, and deliberately not a constant a writer could reach.
195
+ */
196
+ export const HITL_PAUSE: "hitl-pause";
197
+ export const HITL_DECISION_RED: "hitl-decision-red";
198
+ /**
199
+ * The terminals above that leave the run WAITING ON A PERSON — as a list, minted
200
+ * beside the names it holds so a reader keys on membership rather than on a
201
+ * spelling, and a rename here travels to every reader for free.
202
+ *
203
+ * It is deliberately NOT `reuse.js`'s `CHECKPOINT_OUTCOMES`, which is the wider
204
+ * RESUME list: every stop that left work on disk and an allowance unspent, money
205
+ * and time included. Those are governance stops the reuse loop is meant to try
206
+ * again under its remaining tries; this is the half no further attempt can
207
+ * change, because the allowance still unspent is a human answer. `CHECKPOINT_OUTCOMES`
208
+ * is composed FROM this list so the two can never disagree about which is which.
209
+ *
210
+ * `HITL_DECISION_RED` is not here: it is a verdict already rendered on the operator's
211
+ * own input, not a question still open.
212
+ */
213
+ export const HUMAN_CHECKPOINTS: readonly string[];
214
+ /** The verdict classes a DECLARED close may claim. Named as data so the job
215
+ * validator reads one hierarchy table rather than hard-coding a class.
216
+ *
217
+ * `hard` is by CONSTRUCTION: every mechanical kind is a command whose measurement
218
+ * is the truth. `hitl` joined it at N4 slice 1, and only because `human-confirms`
219
+ * is genuinely LIVE — the widening and the kind go in one commit, or this table
220
+ * admits a class the executor cannot render. The `soft` class is still absent:
221
+ * `judged-floor` is locked, so no declaration can reach above hitl.
222
+ *
223
+ * The direction that matters is unchanged. This says which class a declaration
224
+ * MAY claim; `closeCeiling` says which class its stages actually NEED. A pick
225
+ * below the ceiling is still the silent upgrade `class-ceiling` refuses. */
168
226
  export const DECLARED_CLOSE_CLASSES: readonly string[];
169
227
  export type Red = {
170
228
  code: string;
package/types/index.d.ts CHANGED
@@ -8,13 +8,13 @@ export { validatePlan, stageClose, EXIT_TYPES, MAX_EXITS_PER_STEP, MAX_PLAN_STEP
8
8
  export { snapshotScope, evalExits } from "./exits.js";
9
9
  export { BRIDGE_SCHEMA, validateBridge, deriveStatus, listingRow, loadGate, mintBridge, appendGreen, appendRed, loadBridge, loadRegistry, saveBridge, makeRegistry, registryExists } from "./bridges.js";
10
10
  export { renderListing, selectionPrompt } from "./selection.js";
11
- export { validateEnvelope, resolveTrySpec, resolveReuse, reuseSpecHash, selectBridge, runReuse, REUSE_GRADED_RED, readResume, resumeTreeGate } from "./reuse.js";
11
+ export { validateEnvelope, resolveTrySpec, resolveReuse, reuseSpecHash, selectBridge, runReuse, REUSE_GRADED_RED, readResume, resumeTreeGate, CHECKPOINT_OUTCOMES, PAUSE_TTL_MS, checkpointAgeGate } from "./reuse.js";
12
12
  export { classifyIncidents, foldLedger, ledgerDeltas, updateLedger, LEDGER_CLASSES } from "./ledger.js";
13
13
  export { workBranchName, prepareWorkBranch, WORK_BRANCH_PREFIX, WORK_BRANCH_RE } from "./workbranch.js";
14
- export { runDeclaredClose, runStage, seedRead, seedAtHead, seedListing, changedSet, makeSeedTrees, LIVE_KINDS, STOP_FAULTS, JUDGED_MARKER, EXIT_GREEN, EXIT_RED, EXIT_STOP } from "./kinds.js";
15
- export { validateDeclaration, normalizeDeclaration, KIND_CATALOGUE, CATALOGUE_KINDS, CATALOGUE_LIVE_KINDS, LOCKED_KINDS, TYPES_GENRE, TYPES_GENRE_TEMPLATE, GENRE_LANGUAGES, genreEnv, genreOwnedEnvNames, genreInstruments, DIRECTIONS, BASELINES, MAX_STAGES, VERDICT_CLASSES, LOCKED_CLASSES, LIVE_CLASSES, CLASS_BATTERIES, classGuards, closeCeiling } from "./authoring.js";
14
+ export { runDeclaredClose, runStage, seedRead, seedAtHead, seedListing, changedSet, makeSeedTrees, LIVE_KINDS, STOP_FAULTS, JUDGED_MARKER, EXIT_GREEN, EXIT_RED, EXIT_STOP, HUMAN_DECISIONS, SEED_EXEMPT_KINDS, normalizeHumanRuling } from "./kinds.js";
15
+ export { validateDeclaration, normalizeDeclaration, KIND_CATALOGUE, CATALOGUE_KINDS, CATALOGUE_LIVE_KINDS, LOCKED_KINDS, TYPES_GENRE, TYPES_GENRE_TEMPLATE, GENRE_LANGUAGES, genreEnv, genreOwnedEnvNames, genreInstruments, DIRECTIONS, BASELINES, MAX_STAGES, VERDICT_CLASSES, LOCKED_CLASSES, LIVE_CLASSES, CLASS_BATTERIES, classGuards, closeCeiling, NEVER_OFFERED_KINDS } from "./authoring.js";
16
16
  export { runAuthorScout, buildSeedListing, seedFileList, classifySurvey, AUTHOR_SCOUT_VERBS, SCOUT_ATTEMPTS, SURVEY_CAUSES, SCOUT_RETRY_CAUSES } from "./authorscout.js";
17
17
  export { scrubRaw, RAW_PERSIST_MAX, RAW_TRIM_MARKER } from "./text.js";
18
18
  export { authorClose, authorPrompt, declarationSchema, makeLoopGenerate, MAX_REVISIONS, QUESTION_SETS, GREEN_QUESTIONS, CLASS_STATEMENTS, questionsFor, requiredAnswersFor } from "./authorflow.js";
19
- export { validateCloseDecl, runDeclaredStages, declaredStages, isDeclaredClose, guardNames, closeGrade, DECLARED_GENRES, DECLARED_GAP_PREFIX, DECLARED_GAP_KEEP, DECLARED_CLOSE_CLASSES, CLOSE_DECL_FIELDS } from "./declaredclose.js";
19
+ export { validateCloseDecl, runDeclaredStages, declaredStages, isDeclaredClose, guardNames, closeGrade, DECLARED_GENRES, DECLARED_GAP_PREFIX, DECLARED_GAP_KEEP, DECLARED_CLOSE_CLASSES, CLOSE_DECL_FIELDS, HUMAN_PAUSE, HITL_PAUSE, HITL_DECISION_RED, HUMAN_CHECKPOINTS } from "./declaredclose.js";
20
20
  export { runInterview, authorCloseForJob, assembleSpec, prepareSigning, refusalEvents, refuseLockedKind, GENRE, REFUSAL_LIB, REFUSAL_CATEGORY, AUTHORED_SPEC_FIELDS } from "./authorjob.js";
package/types/job.d.ts CHANGED
@@ -158,6 +158,11 @@ export const LOCKED_TOOLS: readonly string[];
158
158
  * pattern: disclosure ≠ admission), so the ledger counts the demand and the
159
159
  * non-code rung later fills a declared slot instead of reshaping the schema. */
160
160
  export const VERDICT_TYPES: readonly string[];
161
+ /** N4 slice 1: `hitl` is ADMITTED — the class whose *done* needs a person runs
162
+ * through the same signed spec, the same agent-authored plan and the same
163
+ * arbiter as a green job (the nine rulings, 2026-08-07; the three-button surface,
164
+ * 2026-08-12). `soft-green` stays declared-but-locked until its judged floor is
165
+ * built and proven (slice 2): a wobbly ruler must not mint anything. */
161
166
  export const LOCKED_VERDICTS: readonly string[];
162
167
  export const CADENCE_UNITS: readonly string[];
163
168
  /** SP-2: API-first; local deferred (PRD §5/§8). `clipipe-subscription` (F42,
package/types/kinds.d.ts CHANGED
@@ -211,6 +211,30 @@ export function parseValue(output: string, terms: any[], { scope, workdir }?: {
211
211
  * @returns {SeedTrees}
212
212
  */
213
213
  export function makeSeedTrees(): SeedTrees;
214
+ /**
215
+ * THE SIGNER'S ANSWER, normalised at the seam that accepts it — the library half
216
+ * of the three-button gate (2026-08-12 §1).
217
+ *
218
+ * `null` is not a refusal: no ruling at all IS the pause path, which is how every
219
+ * ordinary run and every first leg of a hitl run arrives here. What is refused is
220
+ * an answer that is not one of the three doors, and — the case the POC's negative
221
+ * control measured — a `rerun` with no text. Ruling 3 makes the human's words the
222
+ * gap the worker converts from; an empty one launches a fix worker that re-runs
223
+ * as though nothing had been said, which is the failure this refusal exists for.
224
+ *
225
+ * It never throws and never repairs: a decision that cannot be read is handed
226
+ * back as a refusal with its reason, and the caller decides what to do about it.
227
+ * @param {any} ruling
228
+ * @returns {{ok: boolean, ruling: {decision: string, text: string|null}|null, why: string|null}}
229
+ */
230
+ export function normalizeHumanRuling(ruling: any): {
231
+ ok: boolean;
232
+ ruling: {
233
+ decision: string;
234
+ text: string | null;
235
+ } | null;
236
+ why: string | null;
237
+ };
214
238
  /**
215
239
  * Run ONE declared stage against a workdir at a seed.
216
240
  *
@@ -254,13 +278,14 @@ export function seedRead(declaration: any, ctx: Ctx): Promise<StageResult[]>;
254
278
  * room would break a documented adopter contract for nothing; the new name says
255
279
  * exactly what differs. `src/declaredclose.js` is what bridges the two shapes.
256
280
  * @param {any} declaration @param {Ctx} ctx
257
- * @returns {Promise<{verdict: 'green'|'red'|'instrument-stop', exitCode: number, judged: boolean, firstRed: string|null, stages: StageResult[]}>}
281
+ * @returns {Promise<{verdict: 'green'|'red'|'instrument-stop'|'pause', exitCode: number|null, judged: boolean, firstRed: string|null, pausedAt: string|null, stages: StageResult[]}>}
258
282
  */
259
283
  export function runDeclaredClose(declaration: any, ctx: Ctx): Promise<{
260
- verdict: "green" | "red" | "instrument-stop";
261
- exitCode: number;
284
+ verdict: "green" | "red" | "instrument-stop" | "pause";
285
+ exitCode: number | null;
262
286
  judged: boolean;
263
287
  firstRed: string | null;
288
+ pausedAt: string | null;
264
289
  stages: StageResult[];
265
290
  }>;
266
291
  /** the close's two judgment exits — a stage's verdict, in the shape the
@@ -330,6 +355,28 @@ export const SIGNS: readonly number[];
330
355
  * counted demand (the request-red path), and reaching one at runtime is a
331
356
  * stop — never a red, and never a silent pass. */
332
357
  export const LIVE_KINDS: readonly string[];
358
+ /** RULING 8 — the stages the seed-verdict read (D12) must NOT run. A judged
359
+ * number at seed is unstable and a person at seed is an interview, not a
360
+ * measurement; their bar comes from calibration (soft-green) or from the person
361
+ * themselves (hitl), never from the seed. The skip is RECORDED by name rather
362
+ * than taken in silence (F59: absent is not empty), so `seedRead` still returns
363
+ * one row per declared stage. */
364
+ export const SEED_EXEMPT_KINDS: readonly string[];
365
+ /** the one kind a PERSON renders — named once, because the runner keys three
366
+ * decisions on it (is there a door to answer, which stage paused, whose ruling
367
+ * this is) and three literals is how they come to disagree */
368
+ export const HUMAN_KIND: "human-confirms";
369
+ /** the three doors the signer is offered at a hitl pause (2026-08-12 §1, doors
370
+ * re-cut 2026-08-17). No fourth door, and no free-text-only variant: a red that
371
+ * is not one of these two is not a ruling the run can act on.
372
+ *
373
+ * `cancel` was the third door and is DELETED as a concept (hamr: *"what's the
374
+ * point of cancel anyways? pause can resume — that would be more honest"*). The
375
+ * case it served — a person who looks and does not want to carry on — is a pause
376
+ * nobody returns to, which the 60-day checkpoint TTL retires on its own. Nothing
377
+ * here forces a forever-decision at the one moment the person has least reason
378
+ * to make one. */
379
+ export const HUMAN_DECISIONS: readonly string[];
333
380
  /**
334
381
  * The arbiter's book NAMES, one home. F98's mechanism was exactly this list
335
382
  * living as independent literals: the fence deny arrays (src/planrun.js,
@@ -356,6 +403,13 @@ export type Red = {
356
403
  /**
357
404
  * The arbiter's half of a stage run. Every field here is operator/runner
358
405
  * territory: a declaration parameterises kinds, never the contracts.
406
+ * `humanRuling` is the SIGNER's answer at a hitl pause, carried in from the
407
+ * runner — never authored, never defaulted, and absent on every ordinary run.
408
+ * `onStage` is a REPORTING seam and nothing else: the seed read spawns a real
409
+ * toolchain per stage (a suite is the slow one) and a whole read is one opaque
410
+ * await, so the shell gets told as each stage lands. It decides nothing, it is
411
+ * never consulted, and no verdict depends on it — the house pattern is
412
+ * `runJob`'s `emit` (the library reports, the shell prints).
359
413
  */
360
414
  export type Ctx = {
361
415
  workdir: string;
@@ -366,9 +420,27 @@ export type Ctx = {
366
420
  gapCap?: number;
367
421
  maxBuffer?: number;
368
422
  seedTrees?: SeedTrees;
423
+ humanRuling?: {
424
+ decision: string;
425
+ text?: string | null;
426
+ } | null;
427
+ onStage?: (row: {
428
+ stage: string | null;
429
+ kind: string | null;
430
+ verdict: string;
431
+ durationMs: number;
432
+ }) => void;
369
433
  };
434
+ /**
435
+ * Six outcomes, and only two of them are verdicts. `pause` is N4's fifth: the
436
+ * close reached a stage a machine cannot render and is WAITING on a person —
437
+ * neither green nor red, and never graded (the F17 forbidden-zone rule applied
438
+ * to a non-verdict). `skipped` is the sixth and says a stage was deliberately
439
+ * not run at THIS reading (ruling 8's seed exemption), which is a different
440
+ * fact from `not-reached` (an earlier stage decided first).
441
+ */
370
442
  export type StageResult = {
371
- verdict: "green" | "red" | "instrument-stop" | "not-reached";
443
+ verdict: "green" | "red" | "instrument-stop" | "not-reached" | "pause" | "skipped";
372
444
  exitCode: number | null;
373
445
  value: number | null;
374
446
  baseline: number | null;
@@ -294,12 +294,24 @@ export function planPrompt(job: any, scoutBlob: string, reds: any[] | null, maxS
294
294
  * it the deterministic name would collide with that very branch and the collision walk
295
295
  * would mint a `-2` beside the progress the resume exists to keep. Absent is the cold
296
296
  * path. A recorded branch that no longer exists is a STOP, never a fresh start.
297
+ * @param {{decision: string, text?: string}|null} [opts.humanRuling] N4 — the SIGNER's
298
+ * answer at a hitl pause (2026-08-12 §1, re-cut 2026-08-17: accept | rerun <text> | pause), carried in
299
+ * by the runner on the leg that RESUMES a paused run. Absent on every ordinary run and
300
+ * on the leg that pauses; it is never authored, never defaulted, and never inferred.
301
+ *
302
+ * It is spent ONCE, on this leg's close readings up to the moment the fix loop opens:
303
+ * `accept` greens the human stage on fresh evidence (OPEN-3 — the mechanical stages
304
+ * re-run first, because the tree can change while a run is paused), `rerun` reds it
305
+ * with the human's own words as the gap, and `pause` keeps the checkpoint exactly as it
306
+ * is, before anything is read at all. After the fix loop opens the ruling is gone, so once the mechanical
307
+ * stages pass again the run PAUSES for a second review rather than converting the same
308
+ * sentence forever.
297
309
  * @returns {Promise<string>} 'green' | 'already-green' | 'escalated' | 'plan-red' |
298
310
  * 'check-red' | 'close-red' | 'close-unsupported' | 'recipe-stale' | 'pricing-red' |
299
311
  * 'branch-red' | 'cap-halt' | 'wall-halt' | 'provider-red' | 'interpreter-red' |
300
- * 'step-stalled' | `step-red:<id>`
312
+ * 'step-stalled' | 'hitl-pause' | 'hitl-decision-red' | `step-red:<id>`
301
313
  */
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 }: {
314
+ 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, humanRuling }: {
303
315
  workdir: string;
304
316
  provider: any;
305
317
  nativeProvider?: ((o: {
@@ -332,6 +344,10 @@ export function runPlan(job: any, { workdir, provider, nativeProvider, providerF
332
344
  spendComplete?: (() => boolean) | undefined;
333
345
  now?: (() => number) | undefined;
334
346
  resumeBranch?: string | null | undefined;
347
+ humanRuling?: {
348
+ decision: string;
349
+ text?: string;
350
+ } | null | undefined;
335
351
  }): Promise<string>;
336
352
  /**
337
353
  * The recorded bound-reason's ceiling in the WORKER-facing note. The reason is a
package/types/reuse.d.ts CHANGED
@@ -230,6 +230,43 @@ export function readResume(events: any[], { deathAt, direct, resumableOutcomes }
230
230
  direct?: boolean;
231
231
  resumableOutcomes?: string[];
232
232
  }): any;
233
+ /**
234
+ * The AGE gate for a paused checkpoint — the sibling of `resumeTreeGate`, and it
235
+ * asks the other question a 60-day-old checkpoint raises. The tree gate asks
236
+ * *is this still the tree the run left?*; this asks *is this still a decision
237
+ * anyone is waiting on?*
238
+ *
239
+ * THREE things it deliberately does NOT do:
240
+ *
241
+ * - it does not widen any other resume rule. The 2026-08-12 ruling is about a
242
+ * person deciding, so `applies: false` on every other terminal: a cap-halt
243
+ * checkpoint is a different stop under a different allowance, and quietly
244
+ * ageing it out would be a governance change nobody ruled.
245
+ * - it does not treat an unreadable stamp as young (F6). A checkpoint that
246
+ * cannot say WHEN it paused has an unknown age, and unknown is refused rather
247
+ * than rounded down to fresh — the direction that fails safe.
248
+ * - it does not skip silently. An expired checkpoint comes back as a refusal
249
+ * that NAMES the age and the TTL, because the operator's next move (start
250
+ * fresh) depends on knowing which of the two it hit.
251
+ *
252
+ * The clock is injected for the same reason `runPlan`'s is: a rule measured in
253
+ * months cannot otherwise be tested at all.
254
+ *
255
+ * @param {any[]} events the paused run's own spine, parsed, in file order
256
+ * @param {{now?: () => number, ttlMs?: number}} [opts]
257
+ * @returns {{ok: boolean, applies: boolean, ageMs: number|null, ttlMs: number, pausedAt: string|null, detail: string|null}}
258
+ */
259
+ export function checkpointAgeGate(events: any[], { now, ttlMs }?: {
260
+ now?: () => number;
261
+ ttlMs?: number;
262
+ }): {
263
+ ok: boolean;
264
+ applies: boolean;
265
+ ageMs: number | null;
266
+ ttlMs: number;
267
+ pausedAt: string | null;
268
+ detail: string | null;
269
+ };
233
270
  /**
234
271
  * The PATIENT gate for a resume, which is deliberately NOT the fresh-launch one.
235
272
  *
@@ -384,6 +421,43 @@ export function runReuse(opts: {
384
421
  * started.
385
422
  */
386
423
  export const REUSE_GRADED_RED: readonly string[];
424
+ /**
425
+ * WHICH TERMINALS ARE A CHECKPOINT — the list a runner hands `readResume` as
426
+ * `resumableOutcomes`, minted HERE rather than in whichever script happens to
427
+ * launch a run.
428
+ *
429
+ * `readResume` deliberately takes it as a parameter and defaults to empty, and
430
+ * that stays true: the reuse loop's own semantics depend on the empty default.
431
+ * What did not exist was a canonical ANSWER, so `scripts/run-u.mjs` kept its own
432
+ * copy — and the exported bundle (PRD v1.44 §2: a thin runner with bareloop as a
433
+ * dependency) would have had to keep a third. The same reasoning that put the
434
+ * pause TTL in the library (OPEN-2, hamr, 2026-08-13) puts this here.
435
+ *
436
+ * Every entry is a stop that left WORK ON DISK and an allowance unspent — money,
437
+ * time, a self-healed stall, and now a person who has not answered yet. A verdict
438
+ * already rendered is never on this list: `green`/`escalated` are graded rows, and
439
+ * re-buying one would pay twice for an answer already in hand. (The signer's own
440
+ * `hitl-cancel` terminal used to be the other example; the door was deleted on
441
+ * 2026-08-17 — a person who does not want to carry on pauses, and the checkpoint
442
+ * they keep is retired by the TTL rather than by a terminal.)
443
+ *
444
+ * The human half is SPLICED IN from `declaredclose.js` rather than spelled again,
445
+ * because the try loop below reads the two halves differently and a second copy is
446
+ * how they would come to disagree about which member is which: the first three are
447
+ * allowances the loop's remaining tries are meant to spend against, and the human
448
+ * ones are the stop no further try can change (see `hardStop`).
449
+ */
450
+ export const CHECKPOINT_OUTCOMES: readonly string[];
451
+ /**
452
+ * THE PAUSE TTL (2026-08-12 §2): a hitl checkpoint is kept for 60 days.
453
+ *
454
+ * OPEN-2, RULED (hamr, in-turn, 2026-08-13): **the TTL lives in the LIBRARY**, so
455
+ * the exported bundle (a headless runner with bareloop as a dependency, PRD v1.44
456
+ * §2) inherits it instead of every runner re-implementing it. A script-only TTL
457
+ * would vanish on export — the one place the rule is most needed and least
458
+ * likely to be re-typed.
459
+ */
460
+ export const PAUSE_TTL_MS: number;
387
461
  export type Red = {
388
462
  code: string;
389
463
  path: string;
package/types/run.d.ts CHANGED
@@ -97,12 +97,18 @@
97
97
  * a manufactured-fixation probe). `true` is the ON/experimental arm; the
98
98
  * default is the OFF arm. (Shell-owned seam, same doctrine as the provider
99
99
  * binding.)
100
+ * @param {{decision: string, text?: string}|null} [opts.humanRuling] N4 — the SIGNER's
101
+ * answer at a hitl pause (accept | rerun <text> | pause), carried by the leg that
102
+ * RESUMES a paused run and forwarded verbatim to the plan flow. Absent on every
103
+ * ordinary run; never authored and never defaulted (a defaulted answer to "is this
104
+ * done?" is the rubber-stamp the class exists to prevent, 2026-08-12 §4).
100
105
  * @returns {Promise<string>} outcome: 'green' | 'already-green' | 'escalated' |
101
106
  * 'unapproved-spec' | 'job-red' | 'smoke-red' | 'plan-red' | 'check-red' |
102
107
  * 'close-red' | 'close-unsupported' | 'recipe-stale' | 'branch-red' | 'pricing-red' | 'provider-red' |
103
- * 'interpreter-red' | 'cap-halt' | 'wall-halt' | 'step-stalled' | `step-red:<id>`
108
+ * 'interpreter-red' | 'cap-halt' | 'wall-halt' | 'step-stalled' |
109
+ * 'hitl-pause' | 'hitl-decision-red' | `step-red:<id>`
104
110
  */
105
- export function runJob(rawSpec: object | string, { approvals, workdir, provider, nativeProvider, providerFor, emit, capRuns, strikeLimit, shellCapUsd, closeTimeoutMs, layerRoot, bridge, priorSpentUsd, priorSpendComplete, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch }: {
111
+ export function runJob(rawSpec: object | string, { approvals, workdir, provider, nativeProvider, providerFor, emit, capRuns, strikeLimit, shellCapUsd, closeTimeoutMs, layerRoot, bridge, priorSpentUsd, priorSpendComplete, priorWallMs, resumeSeed, resumeGrades, resumeReplans, resumeBranch, humanRuling }: {
106
112
  approvals: unknown;
107
113
  workdir: string;
108
114
  provider: any;
@@ -133,6 +139,10 @@ export function runJob(rawSpec: object | string, { approvals, workdir, provider,
133
139
  } | null | undefined;
134
140
  resumeBranch?: string | null | undefined;
135
141
  layerRoot?: boolean | undefined;
142
+ humanRuling?: {
143
+ decision: string;
144
+ text?: string;
145
+ } | null | undefined;
136
146
  }): Promise<string>;
137
147
  export type Red = {
138
148
  code: string;