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/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
|
|
57
|
-
*
|
|
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 }?: {
|
package/types/declaredclose.d.ts
CHANGED
|
@@ -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'
|
|
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
|
-
/**
|
|
164
|
-
*
|
|
165
|
-
*
|
|
166
|
-
*
|
|
167
|
-
*
|
|
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 {
|
|
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 {
|
|
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.
|
|
157
|
-
* declared-but-locked
|
|
158
|
-
*
|
|
159
|
-
*
|
|
156
|
+
* the four-field plan shape. All three are ADMITTED as of softgreen module 3;
|
|
157
|
+
* the declared-but-locked mechanism below (`LOCKED_VERDICTS`) is what carried
|
|
158
|
+
* them here one rung at a time — declaring a locked one is a `request-red` (the
|
|
159
|
+
* tool-menu pattern: disclosure ≠ admission), so the ledger counted the demand
|
|
160
|
+
* instead of the schema being reshaped when the rung finally landed. */
|
|
160
161
|
export const VERDICT_TYPES: readonly string[];
|
|
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';
|