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/types/clock.d.ts CHANGED
@@ -53,8 +53,11 @@ export function isWallTimeout(err: any, clock: Clock): boolean;
53
53
  * run. `closeStages`: how many stages the job's close runs (W5) — each one gets
54
54
  * the FULL `closeTimeoutMs`, so this is the multiplier on the overshoot, not a
55
55
  * decoration. Defaults to 1, the single-predicate close. `now`: injected for
56
- * tests — the default is the real clock. `priorElapsedMs`: time this attempt's
57
- * PREDECESSOR already consumed (see below).
56
+ * tests — the default is the real clock, and it stays injectable precisely so
57
+ * every test here can drive time deterministically without a sleep. That default
58
+ * is MONOTONIC (fact 4 above), not `Date.now`: a wall-clock jump — a suspend, an
59
+ * NTP step, a hand-set date — must not be able to reach a signed deadline.
60
+ * `priorElapsedMs`: time this attempt's PREDECESSOR already consumed (see below).
58
61
  * @returns {Clock}
59
62
  */
60
63
  export function createClock({ maxWallMs, closeStages, now, priorElapsedMs }?: {
@@ -6,6 +6,16 @@ export function isDeclaredClose(job: any): boolean;
6
6
  *
7
7
  * `offer` and `needs` pass through untouched when a declaration carries them
8
8
  * (v1's schema does not let one), so `checkMenu`'s derivation is unchanged.
9
+ *
10
+ * THE JUDGE TIER RIDES ALONG, and it rides as a STAMP for the same reason `offer`
11
+ * does. The tier that graded this close's calibration set is a CLOSE-level fact
12
+ * (`calibration.judgeModel`), and the stage that would buy a judge call is a STAGE;
13
+ * `runStage` is handed stages and never the declaration they came out of. Rather than
14
+ * plumb a second close-level channel down through every executor seam, the one place
15
+ * that already turns a declaration into stages carries it across — and it carries it
16
+ * from the SIGNED bytes, so a stage can never name a tier the signature does not.
17
+ * `runJudgedFloor` refuses a mismatch; the absence of a stamp is the SPEC gate's
18
+ * business (`validateCloseDecl` reds a set with no model), never a runtime fall-back.
9
19
  * @param {any} closeDecl
10
20
  * @returns {any[]|null} null when this is not a declaration
11
21
  */
@@ -97,7 +107,10 @@ export function guardNames(closeDecl: any, verdictType: string): string[];
97
107
  * @param {any[]} stages declared stage descriptors, already validated
98
108
  * @param {(s: string) => string} [redact] the scrub, applied at THIS boundary
99
109
  * @param {{timeoutMs?: number, cwd?: string, seedRef?: string,
100
- * seedTrees?: any, gapCap?: number, maxBuffer?: number, baselineMode?: 'auto'|'worktree'}} [opts]
110
+ * seedTrees?: any, gapCap?: number, maxBuffer?: number, baselineMode?: 'auto'|'worktree',
111
+ * humanRuling?: {decision: string, text?: string|null}|null,
112
+ * judgeLoop?: ((o: {system: string}) => any)|null,
113
+ * onJudgeCost?: ((c: any) => void)|null}} [opts]
101
114
  * @returns {Promise<any>}
102
115
  */
103
116
  export function runDeclaredStages(stages: any[], redact?: (s: string) => string, opts?: {
@@ -108,6 +121,14 @@ export function runDeclaredStages(stages: any[], redact?: (s: string) => string,
108
121
  gapCap?: number;
109
122
  maxBuffer?: number;
110
123
  baselineMode?: "auto" | "worktree";
124
+ humanRuling?: {
125
+ decision: string;
126
+ text?: string | null;
127
+ } | null;
128
+ judgeLoop?: ((o: {
129
+ system: string;
130
+ }) => any) | null;
131
+ onJudgeCost?: ((c: any) => void) | null;
111
132
  }): Promise<any>;
112
133
  /**
113
134
  * The trend reader's input for ONE close verdict, whichever executor produced
@@ -150,6 +171,12 @@ export const DECLARED_GENRES: readonly "TYPES"[];
150
171
  * channel of its own.
151
172
  */
152
173
  export const CLOSE_DECL_FIELDS: readonly string[];
174
+ /** what a stored calibration set carries, ENUMERATED: the frozen cases and the judge
175
+ * tier that graded them. Both are inside the spec, so `jobSpecHash` covers both — which
176
+ * is the whole mechanism behind "a judge-model bump forces a full recalibration"
177
+ * (src/judged.js, `JUDGE_MODEL`): the field moves, the hash moves, the signature dies,
178
+ * and the re-sign re-runs the gate. Anything else in there is a field nobody reads. */
179
+ export const CALIBRATION_FIELDS: readonly string[];
153
180
  /**
154
181
  * The prefix M1 puts on EVERY gap line of a declared stage (its `ctx.gapKeep`).
155
182
  * A literal, so the anchored pattern below is exact.
@@ -160,11 +187,71 @@ export const DECLARED_GAP_PREFIX: "close: ";
160
187
  * it; because every declared line carries the prefix, it designates the stage's
161
188
  * whole judged output rather than a hand-picked slice of it. */
162
189
  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. */
190
+ /**
191
+ * The close verdict word for a run WAITING on a person (N4 §1.3/§1.4). A
192
+ * literal, exported, because three readers key on it — the runner's pause
193
+ * branch, the ledger's excluded set, and the trend reader's blind rule — and
194
+ * three hand-spelled copies of one terminal is how they come to disagree.
195
+ *
196
+ * It is not in `CLOSE_FAULTS` and must never be added: a fault says the close
197
+ * could not render a verdict, and this says the close is not FINISHED rendering
198
+ * one. The first escalates; the second is a checkpoint the operator returns to.
199
+ */
200
+ export const HUMAN_PAUSE: "human-pause";
201
+ /**
202
+ * The RUN-level terminal names the hitl class mints one layer up from
203
+ * `HUMAN_PAUSE` (N4 §1.3–§1.5): the checkpoint, and the honest refusal of the
204
+ * operator's rerun input. Spelled once here, beside the close-verdict word they
205
+ * translate, for the same reason `HUMAN_PAUSE` is — the emitters (`planrun.js`),
206
+ * the ledger's excluded set, the resume reader (`reuse.js`), and the runner
207
+ * script all key on them, and hand-spelled copies of one terminal is how they
208
+ * come to disagree.
209
+ *
210
+ * There were three (2026-08-17): `hitl-cancel` was the signer's own terminal and
211
+ * is DELETED with the door it belonged to. A person who does not want to carry on
212
+ * now PAUSES — the same checkpoint, resumable, retired by the TTL if nobody comes
213
+ * back — so nothing in this tree mints that name any more. The ledger keeps the
214
+ * bare string in its excluded set so a spine written before this change still
215
+ * reads as governance rather than as a capability gap; that is a READER's
216
+ * backward compatibility, and deliberately not a constant a writer could reach.
217
+ */
218
+ export const HITL_PAUSE: "hitl-pause";
219
+ export const HITL_DECISION_RED: "hitl-decision-red";
220
+ /**
221
+ * The terminals above that leave the run WAITING ON A PERSON — as a list, minted
222
+ * beside the names it holds so a reader keys on membership rather than on a
223
+ * spelling, and a rename here travels to every reader for free.
224
+ *
225
+ * It is deliberately NOT `reuse.js`'s `CHECKPOINT_OUTCOMES`, which is the wider
226
+ * RESUME list: every stop that left work on disk and an allowance unspent, money
227
+ * and time included. Those are governance stops the reuse loop is meant to try
228
+ * again under its remaining tries; this is the half no further attempt can
229
+ * change, because the allowance still unspent is a human answer. `CHECKPOINT_OUTCOMES`
230
+ * is composed FROM this list so the two can never disagree about which is which.
231
+ *
232
+ * `HITL_DECISION_RED` is not here: it is a verdict already rendered on the operator's
233
+ * own input, not a question still open.
234
+ */
235
+ export const HUMAN_CHECKPOINTS: readonly string[];
236
+ /** The verdict classes a DECLARED close may claim. Named as data so the job
237
+ * validator reads one hierarchy table rather than hard-coding a class.
238
+ *
239
+ * `hard` is by CONSTRUCTION: every mechanical kind is a command whose measurement
240
+ * is the truth. `hitl` joined it at N4 slice 1, and `soft` at softgreen module 3
241
+ * — each only because its kind is genuinely LIVE (`human-confirms`, then
242
+ * `judged-floor`): the widening and the kind go in one commit, or this table
243
+ * admits a class the executor cannot render.
244
+ *
245
+ * `hitl` STAYS despite the class's retirement (PRD v1.71: the human stage moved
246
+ * out of the close and became the review door at the end of every run). It is
247
+ * kept because `jobs/litectx-maintainer.json` is a SIGNED spec carrying a hitl
248
+ * declaration, and a signed spec is untouchable — dropping the spelling here
249
+ * would invalidate a signature the operator already gave, which is a re-sign
250
+ * nobody asked for. Retiring it is a separate, deliberate deletion.
251
+ *
252
+ * The direction that matters is unchanged. This says which class a declaration
253
+ * MAY claim; `closeCeiling` says which class its stages actually NEED. A pick
254
+ * below the ceiling is still the silent upgrade `class-ceiling` refuses. */
168
255
  export const DECLARED_CLOSE_CLASSES: readonly string[];
169
256
  export type Red = {
170
257
  code: string;
package/types/index.d.ts CHANGED
@@ -1,20 +1,24 @@
1
1
  export { makeSpine } from "./spine.js";
2
2
  export { runPlan } from "./planrun.js";
3
- export { runJob } from "./run.js";
4
3
  export { ralph, runClose, runStages, CLOSE_FAULTS } from "./ralph.js";
5
4
  export { globToPrefix, scanSecrets } from "./validate.js";
6
5
  export { validateJob, jobSpecHash, checkApproval, checkMenu, CLOSE_TYPES, CLASS_BY_CLOSE, GOLD_COMPARE, CADENCE_UNITS, PROVIDERS, CONDITION_KEYS, TOOL_MENU, LOCKED_TOOLS, STORE_VERBS, VERDICT_TYPES, LOCKED_VERDICTS } from "./job.js";
7
6
  export { validatePlan, stageClose, EXIT_TYPES, MAX_EXITS_PER_STEP, MAX_PLAN_STEPS, WRITE_VERBS, closeStagesOf } from "./plan.js";
8
7
  export { snapshotScope, evalExits } from "./exits.js";
9
- export { BRIDGE_SCHEMA, validateBridge, deriveStatus, listingRow, loadGate, mintBridge, appendGreen, appendRed, loadBridge, loadRegistry, saveBridge, makeRegistry, registryExists } from "./bridges.js";
8
+ export { runJob, ACCOUNTED_ROUND_TYPES } from "./run.js";
9
+ export { BRIDGE_SCHEMA, validateBridge, deriveStatus, listingRow, loadGate, mintBridge, appendGreen, appendRed, loadBridge, loadRegistry, saveBridge, makeRegistry, registryExists, QUARANTINED_VERDICTS, QUARANTINED_CODE, quarantinesCredit, newestEligibleVersion, reuseEligibility, recordDoor } 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, applyDoorDecision, writeRunGreenRow } 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, JUDGED_FLOOR_KIND, normalizeHumanRuling, resolveHumanRuling, JUDGE_ATTEMPTS, MAX_JUDGED_PATHS, REVIEW_DOOR, DOOR_OPEN_OUTCOMES, REVIEW_DOOR_CLASSES, doorOpens, mechanicalStages } from "./kinds.js";
15
+ export { answerReviewDoor, doorRecordOf, doorAgeGate } from "./reviewdoor.js";
16
+ export { JUDGE_MODEL, JUDGE_MAX_TOKENS, JUDGE_RULES, JUDGE_RULE_IDS, LOCATE_AXES, LOCATE_LABEL, validateCard, validateFacts, locatePrompt, runLocate, decide, defaultJudgeLoop, CALIBRATION_SIZE, CASE_VERDICTS, validateCalibrationSet, validateJudgedArtifacts, expectedOf } from "./judged.js";
17
+ export { proposeJudgedArtifacts, signJudgedArtifacts, foldJudgedArtifacts, proposalSchema, proposalTool, cardCasesPrompt, PROPOSAL_TOOL_NAME, PROPOSAL_LABEL, MAX_PROPOSAL_RETRIES, COMPILE_SYSTEM } from "./cardauthor.js";
18
+ export { runCalibration, compareExpectation, factsResist, artifactHash, INJECTION_LOCATE_BATTERY, INJECTION_CARD, CALIBRATION_LABEL, CASUALTY_AXES } from "./calibrate.js";
19
+ 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, classMenu, closeCeiling, NEVER_OFFERED_KINDS } from "./authoring.js";
16
20
  export { runAuthorScout, buildSeedListing, seedFileList, classifySurvey, AUTHOR_SCOUT_VERBS, SCOUT_ATTEMPTS, SURVEY_CAUSES, SCOUT_RETRY_CAUSES } from "./authorscout.js";
17
21
  export { scrubRaw, RAW_PERSIST_MAX, RAW_TRIM_MARKER } from "./text.js";
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";
22
+ export { authorClose, authorPrompt, declarationSchema, makeLoopGenerate, MAX_REVISIONS, QUESTION_SETS, GREEN_QUESTIONS, SOFTGREEN_QUESTIONS, CLASS_STATEMENTS, questionsFor, requiredAnswersFor } from "./authorflow.js";
23
+ 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
24
  export { runInterview, authorCloseForJob, assembleSpec, prepareSigning, refusalEvents, refuseLockedKind, GENRE, REFUSAL_LIB, REFUSAL_CATEGORY, AUTHORED_SPEC_FIELDS } from "./authorjob.js";
package/types/job.d.ts CHANGED
@@ -153,11 +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. v1 ADMITS only `green`; soft-green/hitl are
157
- * declared-but-locked — declaring one is a `request-red` (the tool-menu
158
- * pattern: disclosure ≠ admission), so the ledger counts the demand and the
159
- * non-code rung later fills a declared slot instead of reshaping the schema. */
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[];
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[]} */
161
179
  export const LOCKED_VERDICTS: readonly string[];
162
180
  export const CADENCE_UNITS: readonly string[];
163
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';