bareloop 0.7.0 → 0.9.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.
@@ -0,0 +1,174 @@
1
+ /** @param {any} job @returns {boolean} */
2
+ export function isDeclaredClose(job: any): boolean;
3
+ /**
4
+ * The declaration's stages as the RUNNER's stage descriptors: the declared
5
+ * `{name, kind, params}` plus the arbiter's own `gapKeep`.
6
+ *
7
+ * `offer` and `needs` pass through untouched when a declaration carries them
8
+ * (v1's schema does not let one), so `checkMenu`'s derivation is unchanged.
9
+ * @param {any} closeDecl
10
+ * @returns {any[]|null} null when this is not a declaration
11
+ */
12
+ export function declaredStages(closeDecl: any): any[] | null;
13
+ /**
14
+ * Validate a `closeDecl` as a JOB SPEC field: the envelope (genre, language,
15
+ * notes) plus M2's full declaration validator underneath it.
16
+ *
17
+ * `listing` is what splits the two callers, and the split is deliberate:
18
+ * - the JOB VALIDATOR has no repository, so it passes nothing and gets
19
+ * `grounded: false` — every tree-independent rule runs, the path rule and
20
+ * the scoped-job derivation do not;
21
+ * - the RUNNER has the real seed, so it passes the real listing and gets
22
+ * `grounded: true`. It runs this BEFORE any stage and before any token, so
23
+ * the deferred half is proved rather than skipped.
24
+ * A caller that passes neither a listing nor `deferListing` gets M2's
25
+ * `listing-absent` red, unchanged: a validator handed nothing to check against
26
+ * must refuse, never report a declaration it never examined.
27
+ *
28
+ * THREE checks now ride on the listing, not two: the path rule, the scoped-job
29
+ * derivation, and the GROUNDING of a recorded `genreEnv` (a by-value comparison
30
+ * between two copies inside one hand-editable spec proves they agree, never that
31
+ * the arbiter wrote them — see `ungroundedGenreEnv`). All three are deferred at
32
+ * the job-spec gate and all three are re-run GROUNDED by the runner before any
33
+ * stage executes, so the deferral moves the check rather than dropping it. The
34
+ * grounded gates are `prepareSigning`'s gate 1b — where the signature is minted,
35
+ * so a forged env can never be signed in the first place — and `runPlan`'s
36
+ * pre-flight, which catches a spec edited after signing.
37
+ *
38
+ * `verdictType` is the class the USER picked, and it is REQUIRED here because
39
+ * the guard battery keys off it (PRD v1.57 §2): with no class in hand there is
40
+ * no battery, and M2 would be handed nothing to check D5 against. It arrives
41
+ * from the spec's sibling `verdictType` field on every caller — the job
42
+ * validator, the signing gate and the runner all hold the whole spec.
43
+ *
44
+ * @param {any} closeDecl
45
+ * @param {{at?: string, listing?: string[]|null, deferListing?: boolean,
46
+ * catalogue?: Record<string, any>, verdictType?: string|null}} [opts]
47
+ * @returns {{ok: boolean, reds: Red[], closeDecl: any, grounded: boolean,
48
+ * scoped: {scoped: boolean, via: string|null},
49
+ * ceiling: {class: string, kind: string, stage: string|null, locked: boolean}|null}}
50
+ */
51
+ export function validateCloseDecl(closeDecl: any, opts?: {
52
+ at?: string;
53
+ listing?: string[] | null;
54
+ deferListing?: boolean;
55
+ catalogue?: Record<string, any>;
56
+ verdictType?: string | null;
57
+ }): {
58
+ ok: boolean;
59
+ reds: Red[];
60
+ closeDecl: any;
61
+ grounded: boolean;
62
+ scoped: {
63
+ scoped: boolean;
64
+ via: string | null;
65
+ };
66
+ ceiling: {
67
+ class: string;
68
+ kind: string;
69
+ stage: string | null;
70
+ locked: boolean;
71
+ } | null;
72
+ };
73
+ /**
74
+ * The genre's mandatory guard NAMES for a declaration — which stages are D5's
75
+ * guards and which are the job's own work. The seed-verdict read (D9.3) needs
76
+ * exactly this split: `changed-from-seed` is RED at its own seed by
77
+ * construction, so counting it as work would let a close with nothing to do
78
+ * pass the "something must be red at seed" gate.
79
+ * The battery keys off the CLASS the user picked (PRD v1.57 §2), so the class
80
+ * comes in beside the declaration. A class or a language with no battery hands
81
+ * back `[]` — this reads a split, it does not enforce one, and every caller
82
+ * reaches it only after `validateCloseDecl` has already refused both cases.
83
+ * @param {any} closeDecl @param {string} verdictType @returns {string[]}
84
+ */
85
+ export function guardNames(closeDecl: any, verdictType: string): string[];
86
+ /**
87
+ * Run a DECLARED close (or a check's chain out of one) and return `runClose`'s
88
+ * verdict shape, so every consumer downstream — the precheck, ralph's judge, the
89
+ * `CLOSE_FAULTS` routing, the trend reader, Layer R — is unchanged.
90
+ *
91
+ * The extras a declared close can honestly add ride alongside rather than
92
+ * replacing anything: `declared: true` marks the shape, `value`/`baseline` carry
93
+ * the deciding stage's own measurement, and `trendValue` is the ONE number the
94
+ * trend reader may compare (see the header for why a higher-is-better stage
95
+ * donates none).
96
+ *
97
+ * @param {any[]} stages declared stage descriptors, already validated
98
+ * @param {(s: string) => string} [redact] the scrub, applied at THIS boundary
99
+ * @param {{timeoutMs?: number, cwd?: string, seedRef?: string,
100
+ * seedTrees?: any, gapCap?: number, maxBuffer?: number, baselineMode?: 'auto'|'worktree'}} [opts]
101
+ * @returns {Promise<any>}
102
+ */
103
+ export function runDeclaredStages(stages: any[], redact?: (s: string) => string, opts?: {
104
+ timeoutMs?: number;
105
+ cwd?: string;
106
+ seedRef?: string;
107
+ seedTrees?: any;
108
+ gapCap?: number;
109
+ maxBuffer?: number;
110
+ baselineMode?: "auto" | "worktree";
111
+ }): Promise<any>;
112
+ /**
113
+ * The trend reader's input for ONE close verdict, whichever executor produced
114
+ * it. A command close hands over its gap and `readGrade` scrapes stage and count
115
+ * out of the text; a declared close already KNOWS both, so it says them instead
116
+ * of round-tripping them through prose it would then have to parse back.
117
+ *
118
+ * Byte-identical to the previous call for a command close: `{ gap }`, with
119
+ * `stage`/`value` left UNDEFINED so `trend.record` falls through to `readGrade`
120
+ * exactly as before. Passing `stage: null` instead would silently disable that
121
+ * fallback — undefined and null are different answers here.
122
+ * @param {any} v a `runClose`-shaped verdict
123
+ * @returns {{gap?: string, stage?: string|null, value?: number|null}}
124
+ */
125
+ export function closeGrade(v: any): {
126
+ gap?: string;
127
+ stage?: string | null;
128
+ value?: number | null;
129
+ };
130
+ /** @typedef {{code: string, path: string, detail: string, [k: string]: any}} Red */
131
+ /** The genres a `closeDecl` may name. v1 admits exactly one (D13) — a genre
132
+ * classifier is designed the day there is a second genre to classify. */
133
+ export const DECLARED_GENRES: readonly "TYPES"[];
134
+ /**
135
+ * The exact field set of a `closeDecl` — anything else is a smuggle channel, the
136
+ * same rule `STAGE_FIELDS` enforces on a command stage.
137
+ *
138
+ * `genreEnv` is the ARBITER's own record of what it injected (M3's
139
+ * `applyGenreEnv`), and it is here because the injection has to survive being
140
+ * re-read. The flow applies the genre's variable to every env-capable stage
141
+ * AFTER the model's form passed; from then on every re-validation — signing
142
+ * (`prepareSigning`), the job validator (`src/job.js`), the runner
143
+ * (`src/planrun.js`) — sees that key sitting in `params.env` and cannot, from
144
+ * the stage alone, tell our injection from a model-authored guess. Recording it
145
+ * once, in the SIGNED envelope, is what makes the comparison an EQUALITY rather
146
+ * than a presence flag (the same move `checkGuards` makes for guard params), and
147
+ * it is why the model never gets to author it: `authorCloseForJob` composes this
148
+ * field from the flow's own applied map, never from model output. Its keys are
149
+ * constrained to the names the genre OWNS, so it can never become an environment
150
+ * channel of its own.
151
+ */
152
+ export const CLOSE_DECL_FIELDS: readonly string[];
153
+ /**
154
+ * The prefix M1 puts on EVERY gap line of a declared stage (its `ctx.gapKeep`).
155
+ * A literal, so the anchored pattern below is exact.
156
+ */
157
+ export const DECLARED_GAP_PREFIX: "close: ";
158
+ /** The `gapKeep` PATTERN for a declared stage, DERIVED from the prefix so the
159
+ * two can never drift. Layer R's red-set and `boundGap`'s keep-block both read
160
+ * it; because every declared line carries the prefix, it designates the stage's
161
+ * whole judged output rather than a hand-picked slice of it. */
162
+ 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
+ export const DECLARED_CLOSE_CLASSES: readonly string[];
169
+ export type Red = {
170
+ code: string;
171
+ path: string;
172
+ detail: string;
173
+ [k: string]: any;
174
+ };
package/types/exits.d.ts CHANGED
@@ -36,6 +36,20 @@ export function evalExits(exits: Array<Record<string, any>>, { dir, snapshot, ru
36
36
  pass: boolean;
37
37
  results: ExitResult[];
38
38
  }>;
39
+ /** The check gap is ALREADY bounded upstream by runClose's boundGap (head +
40
+ * gapKeep block ≤ 8192 + tail ≈ 10KB, with the failing-test NAMES preserved).
41
+ * The evaluator must NOT re-truncate it: a 400-char clamp deletes exactly those
42
+ * `not ok`/`FAILED` names — the mechanical gap the F46 conversion mechanism
43
+ * feeds the worker (F28 reintroduced). This cap is a defensive backstop above
44
+ * boundGap's own envelope: a normal bounded gap passes through intact, and only
45
+ * a pathological seam that returned an unbounded gap would ever be trimmed.
46
+ *
47
+ * Exported for the ONE reason `boundGap`/`GAP_KEEP_TRIM_MARKER` are (src/ralph.js):
48
+ * the replan brief re-bounds this same artifact one seam later (`closeGapBlock`), and
49
+ * a second hand-spelled ceiling there would be a second answer to "how big may a
50
+ * check gap be" — the two would drift on exactly the property that matters, whether
51
+ * the `not ok`/`FAILED` names survive. */
52
+ export const CHECK_GAP_MAX: 12000;
39
53
  /**
40
54
  * `fault` present means the INSTRUMENT could not judge (unwired snapshot/seam,
41
55
  * a check in runClose's forbidden zone) — it carries the runClose verdict name
package/types/index.d.ts CHANGED
@@ -4,9 +4,17 @@ export { runJob } from "./run.js";
4
4
  export { ralph, runClose, runStages, CLOSE_FAULTS } from "./ralph.js";
5
5
  export { globToPrefix, scanSecrets } from "./validate.js";
6
6
  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
- export { validatePlan, stageClose, EXIT_TYPES, MAX_EXITS_PER_STEP, MAX_PLAN_STEPS, WRITE_VERBS } from "./plan.js";
7
+ export { validatePlan, stageClose, EXIT_TYPES, MAX_EXITS_PER_STEP, MAX_PLAN_STEPS, WRITE_VERBS, closeStagesOf } from "./plan.js";
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
11
  export { validateEnvelope, resolveTrySpec, resolveReuse, reuseSpecHash, selectBridge, runReuse, REUSE_GRADED_RED, readResume, resumeTreeGate } from "./reuse.js";
12
12
  export { classifyIncidents, foldLedger, ledgerDeltas, updateLedger, LEDGER_CLASSES } from "./ledger.js";
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, DIRECTIONS, BASELINES, MAX_STAGES, VERDICT_CLASSES, LOCKED_CLASSES, LIVE_CLASSES, CLASS_BATTERIES, classGuards, closeCeiling } from "./authoring.js";
16
+ export { runAuthorScout, buildSeedListing, seedFileList, classifySurvey, AUTHOR_SCOUT_VERBS, SCOUT_ATTEMPTS, SURVEY_CAUSES, SCOUT_RETRY_CAUSES } from "./authorscout.js";
17
+ 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";
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
@@ -1,4 +1,9 @@
1
- /** @typedef {{code: string, path: string, detail?: string, verb?: string}} Red — `verb` rides request-reds as structured data (the ledger keys on it, never on prose) */
1
+ /** @typedef {{code: string, path: string, detail?: string, verb?: string, lib?: string}} Red — `verb` and `lib` ride request-reds as structured data (the ledger keys on them, never on prose).
2
+ * `lib` is the TERRITORY the demand lands against, stamped HERE at the emit site
3
+ * (the typed-lib rule): one code, two catalogues — a locked TOOL verb is demand
4
+ * against the worker-surface package, a locked VERDICT type is demand against
5
+ * bareloop's OWN menu. Inferring it downstream is how a bareloop refusal gets
6
+ * filed as an upstream bug (the BA-2 misattribution class). */
2
7
  /**
3
8
  * Validate an operator-owned job spec (`schema: "job-v1"`). Never throws on
4
9
  * JSON text or plain parsed data — the ingest contract; a live object with a
@@ -88,6 +93,7 @@ export const CLASS_BY_CLOSE: Readonly<{
88
93
  gold: readonly string[];
89
94
  rubric: readonly string[];
90
95
  hitl: readonly string[];
96
+ declared: readonly string[];
91
97
  }>;
92
98
  export const GOLD_COMPARE: readonly string[];
93
99
  /** the tool grant menu (2b interview #1): read/grep/write, plus the two litectx
@@ -165,11 +171,17 @@ export const PROVIDERS: readonly string[];
165
171
  * not duplicated here). */
166
172
  export const CONDITION_KEYS: readonly string[];
167
173
  /**
168
- * — `verb` rides request-reds as structured data (the ledger keys on it, never on prose)
174
+ * — `verb` and `lib` ride request-reds as structured data (the ledger keys on them, never on prose).
175
+ * `lib` is the TERRITORY the demand lands against, stamped HERE at the emit site
176
+ * (the typed-lib rule): one code, two catalogues — a locked TOOL verb is demand
177
+ * against the worker-surface package, a locked VERDICT type is demand against
178
+ * bareloop's OWN menu. Inferring it downstream is how a bareloop refusal gets
179
+ * filed as an upstream bug (the BA-2 misattribution class).
169
180
  */
170
181
  export type Red = {
171
182
  code: string;
172
183
  path: string;
173
184
  detail?: string;
174
185
  verb?: string;
186
+ lib?: string;
175
187
  };
@@ -0,0 +1,343 @@
1
+ /**
2
+ * D8's seed, READ rather than typed: HEAD at run start. A close stores the
3
+ * counting rule and never the number (D12), so which commit the run is measured
4
+ * from is a fact about THIS run — recorded and shown, never carried in a signed
5
+ * spec where it would judge run 5 against run 1's tree.
6
+ *
7
+ * A fault is a named `stop`, never a fallback ref: measuring against the wrong
8
+ * baseline is the failure direction that reads as a clean tree.
9
+ * @param {string} workdir
10
+ * @returns {Promise<{stop: string, seedRef: null}|{stop: null, seedRef: string}>}
11
+ */
12
+ export function seedAtHead(workdir: string): Promise<{
13
+ stop: string;
14
+ seedRef: null;
15
+ } | {
16
+ stop: null;
17
+ seedRef: string;
18
+ }>;
19
+ /**
20
+ * The files that exist AT THE SEED COMMIT. Deliberately not `ls-files`, which
21
+ * reads the index and the working tree: this listing is what a declared path is
22
+ * judged against, and it must describe the tree the close measures from rather
23
+ * than whatever is lying around today.
24
+ *
25
+ * ONE spelling, here beside the other git reads (`src/authorscout.js`'s
26
+ * `seedFileList` delegates to it) — a second listing command is a second
27
+ * instrument, and the one that grades would be the one nobody validated.
28
+ * @param {string} workdir @param {string} seedRef
29
+ * @returns {Promise<{stop: string, files: null}|{stop: null, files: string[]}>}
30
+ */
31
+ export function seedListing(workdir: string, seedRef: string): Promise<{
32
+ stop: string;
33
+ files: null;
34
+ } | {
35
+ stop: null;
36
+ files: string[];
37
+ }>;
38
+ /**
39
+ * The two arbiter books, and the shape of each exclusion is the whole point.
40
+ *
41
+ * `.litectx/` is a PREFIX — everything beneath the store belongs to the
42
+ * arbiter. `gate-audit.jsonl` is an EXACT repo-relative path and never a
43
+ * basename or a pattern: the gate audit is written to
44
+ * `join(workdir, 'gate-audit.jsonl')` and nowhere else, so a worker-authored
45
+ * `src/gate-audit.jsonl` is NOT an arbiter book and must still count as work.
46
+ * Matching by basename would swallow it — and swallowing a worker's real
47
+ * write is the failure direction that reads as a clean tree.
48
+ * @param {string} rel a repo-relative POSIX path
49
+ * @returns {string|null} the book it belongs to, or null
50
+ */
51
+ export function isArbiterBook(rel: string): string | null;
52
+ /**
53
+ * Everything this run changed against the seed: the tracked diff PLUS
54
+ * untracked files (an untracked file has no diff — the case a naive
55
+ * `git diff` scan silently misses), minus the arbiter's own books.
56
+ * @param {string} workdir @param {string} seedRef
57
+ * @returns {Promise<{stop: string, fault: string}|{stop: null, paths: string[], tracked: string[], untracked: string[], excluded: {path: string, book: string}[]}>}
58
+ */
59
+ export function changedSet(workdir: string, seedRef: string): Promise<{
60
+ stop: string;
61
+ fault: string;
62
+ } | {
63
+ stop: null;
64
+ paths: string[];
65
+ tracked: string[];
66
+ untracked: string[];
67
+ excluded: {
68
+ path: string;
69
+ book: string;
70
+ }[];
71
+ }>;
72
+ /**
73
+ * The lines this run ADDED to one file. A tracked file's additions come from
74
+ * its own unified diff; an UNTRACKED file has no diff at all, so its ENTIRE
75
+ * body is added — the half of this primitive a diff-only scan misses.
76
+ * @param {string} workdir @param {string} seedRef @param {string} rel
77
+ * @param {{untracked?: boolean}} [o]
78
+ * @returns {Promise<{stop: string, fault: string}|{stop: null, lines: {n: number, text: string}[], note: string|null}>}
79
+ */
80
+ export function addedLines(workdir: string, seedRef: string, rel: string, { untracked }?: {
81
+ untracked?: boolean;
82
+ }): Promise<{
83
+ stop: string;
84
+ fault: string;
85
+ } | {
86
+ stop: null;
87
+ lines: {
88
+ n: number;
89
+ text: string;
90
+ }[];
91
+ note: string | null;
92
+ }>;
93
+ /**
94
+ * Count a pattern's capture groups without running it against real input:
95
+ * `src + '|'` always matches the empty string, so the result array's length
96
+ * names the group count. Also the ONE place a declared regex is compiled at
97
+ * validation time, so a broken pattern reds before any process is spawned.
98
+ * @param {any} src
99
+ * @returns {{count: number, red: string|null}}
100
+ */
101
+ export function regexGroups(src: any): {
102
+ count: number;
103
+ red: string | null;
104
+ };
105
+ /**
106
+ * THE normalisation of a `count-not-worse` parser (gate-1 amendment #5).
107
+ * The declaration validator and this executor both call it, so there is
108
+ * exactly one spelling of what a parser MEANS — two normalisers that
109
+ * disagree are two instruments, and the one that grades is the one nobody
110
+ * validated.
111
+ *
112
+ * value = Σ over terms of sign × (first | sum of that term's captures,
113
+ * inside that term's region)
114
+ *
115
+ * Backwards compatible by construction: the short form
116
+ * `{lineMatch, capture?}` resolves to exactly one term with
117
+ * `sign +1, aggregate "first", region "whole-output"`, which is the
118
+ * pre-amendment semantics unchanged.
119
+ *
120
+ * Never throws — reds come back in the shipped `{ok, reds, ...}` shape.
121
+ * @param {any} parser @param {string} [at] path prefix for reds
122
+ * @returns {{ok: boolean, reds: Red[], terms: any[]|null}}
123
+ */
124
+ export function normalizeParser(parser: any, at?: string): {
125
+ ok: boolean;
126
+ reds: Red[];
127
+ terms: any[] | null;
128
+ };
129
+ /**
130
+ * Read ONE number out of a command's output, as signed arithmetic over
131
+ * normalised terms.
132
+ *
133
+ * The zero-match split is contract (a), not a convenience:
134
+ * - `sum` over zero matches is a COUNTED zero. The region was searched and
135
+ * nothing was there; that is legitimately 0.
136
+ * - `first` over zero matches is UNKNOWN — the number was never reported —
137
+ * and unknown is an instrument stop, never a defaulted 0 (F6).
138
+ * - a region whose anchor matches nothing was never LOCATED: stop.
139
+ * The scope filter runs per OUTPUT LINE, before aggregation, and both its
140
+ * drop count and its unattributable count are ANNOUNCED: a line naming no
141
+ * path is KEPT (no filter can exclude what it cannot locate) and the notes
142
+ * say so, rather than the number quietly meaning something else.
143
+ *
144
+ * `preScopeMatches` is reported alongside the reading because a POST-scope
145
+ * count cannot answer "did the tool run at all". It tallies (term, line) pairs
146
+ * that matched BEFORE the filter judged them — kept, unattributable and dropped
147
+ * alike — and it is the only evidence of liveness a caller has once the scope
148
+ * has removed the whole population. It is never the number being read.
149
+ * @param {string} output @param {any[]} terms normalised terms
150
+ * Every stop here is `crashed` in the arbiter's vocabulary — the command RAN,
151
+ * came back, and the number could not be read out of it. Nothing in this
152
+ * function can observe a spawn failure, a timeout or a signal, so it never
153
+ * claims one.
154
+ * @param {{scope?: any, workdir?: string}} [o]
155
+ * @returns {{stop: string, fault: string, notes?: string[]}|{stop: null, value: number, breakdown: any[], notes: string[], preScopeMatches: number}}
156
+ */
157
+ export function parseValue(output: string, terms: any[], { scope, workdir }?: {
158
+ scope?: any;
159
+ workdir?: string;
160
+ }): {
161
+ stop: string;
162
+ fault: string;
163
+ notes?: string[];
164
+ } | {
165
+ stop: null;
166
+ value: number;
167
+ breakdown: any[];
168
+ notes: string[];
169
+ preScopeMatches: number;
170
+ };
171
+ /**
172
+ * A caller-scoped holder for detached seed worktrees. Scoped rather than
173
+ * module-global on purpose: a global cache in a published library leaks
174
+ * worktrees into whatever process imported it, and nothing would ever remove
175
+ * them. `seedRead`/`runClose` create one, share it across stages (so a
176
+ * multi-stage close pays for one checkout), and remove it in a `finally`.
177
+ * @returns {SeedTrees}
178
+ */
179
+ export function makeSeedTrees(): SeedTrees;
180
+ /**
181
+ * Run ONE declared stage against a workdir at a seed.
182
+ *
183
+ * It never throws. An unexpected throw inside a kind — a malformed parameter
184
+ * the validator did not catch, an unreachable path — is an INSTRUMENT STOP,
185
+ * not a red: that is the casualty-vs-evidence split at its narrowest, and
186
+ * the direction that matters, because a crash rendered as a red is fake
187
+ * evidence about the worker.
188
+ * @param {any} stage `{name, kind, params}`
189
+ * @param {Ctx} ctx
190
+ * @returns {Promise<StageResult>}
191
+ */
192
+ export function runStage(stage: any, ctx: Ctx): Promise<StageResult>;
193
+ /**
194
+ * D12's seed-verdict read: run EVERY declared stage at the seed, regardless
195
+ * of reds, and hand back one row per stage.
196
+ *
197
+ * Not first-red-wins, and the reason is structural rather than a preference:
198
+ * every shipped close opens with `changed-from-seed`, which is RED at its own
199
+ * seed by construction (nothing has changed yet), so a first-red-wins seed
200
+ * read measures exactly one stage and mints no baseline for any other. The
201
+ * baseline every `baseline: "seed"` stage is later graded against comes from
202
+ * here, so a stage that never ran is a stage with no bar.
203
+ * @param {any} declaration `{stages: [...]}`
204
+ * @param {Ctx} ctx
205
+ * @returns {Promise<StageResult[]>}
206
+ */
207
+ export function seedRead(declaration: any, ctx: Ctx): Promise<StageResult[]>;
208
+ /**
209
+ * The GRADING path: stages in declared order, FIRST RED WINS. The deciding
210
+ * stage names the verdict; everything after it is `not-reached` and is
211
+ * reported as such rather than as a green nobody ran.
212
+ *
213
+ * An instrument stop ends the close UNJUDGED — the run has no verdict to
214
+ * carry, which is the whole point of keeping a casualty out of the evidence.
215
+ *
216
+ * NAMED `runDeclaredClose`, not `runClose` (M4's export-surface decision): the
217
+ * arbiter's shell already ships a public `runClose(argv, redact, opts)` that
218
+ * adopters import, and the two take different things and mean different things
219
+ * — one runs a DECLARATION, the other an argv. Renaming the shipped one to make
220
+ * room would break a documented adopter contract for nothing; the new name says
221
+ * exactly what differs. `src/declaredclose.js` is what bridges the two shapes.
222
+ * @param {any} declaration @param {Ctx} ctx
223
+ * @returns {Promise<{verdict: 'green'|'red'|'instrument-stop', exitCode: number, judged: boolean, firstRed: string|null, stages: StageResult[]}>}
224
+ */
225
+ export function runDeclaredClose(declaration: any, ctx: Ctx): Promise<{
226
+ verdict: "green" | "red" | "instrument-stop";
227
+ exitCode: number;
228
+ judged: boolean;
229
+ firstRed: string | null;
230
+ stages: StageResult[];
231
+ }>;
232
+ /** the close's two judgment exits — a stage's verdict, in the shape the
233
+ * hand-written closes already spoke (exit code is truth) */
234
+ export const EXIT_GREEN: 0;
235
+ export const EXIT_RED: 1;
236
+ /** contract (a): the instrument-stop exit. `judged` is withheld with it. */
237
+ export const EXIT_STOP: 97;
238
+ /** The judgment-rendered marker a rendered close prints, for ralph's
239
+ * `runClose` judged floor (src/ralph.js). It rides EVERY real judgment, green or red,
240
+ * and NEVER a stop — this module reports the same fact as the boolean
241
+ * `judged`; the marker exists so a renderer and the floor agree on one
242
+ * spelling. */
243
+ export const JUDGED_MARKER: "judged=1";
244
+ /** contract (b): the per-stage gap ceiling. Trims are announced, never silent. */
245
+ export const GAP_LINE_CAP: 40;
246
+ /**
247
+ * The exact phrase this executor's trim announcement carries. Exported for the
248
+ * same reason ralph exports `GAP_KEEP_TRIM_MARKER`: Layer R's detector must be
249
+ * able to see that a gap window was TRIMMED, because failures beyond the window
250
+ * can move while the visible lines stay identical — and an undetected trim makes
251
+ * the detector claim "reds unchanged" off an instrument that was blind (F43,
252
+ * finding 5). A magic string on the reading side would drift the day this
253
+ * wording changes; the renderer below is its only other use.
254
+ */
255
+ export const GAP_TRIM_MARKER: "gap trimmed:";
256
+ /** used when neither the stage nor the ctx names a bound. A stage that could
257
+ * run forever is not a red — it is a stop, and something must decide when. */
258
+ export const DEFAULT_TIMEOUT_MS: 300000;
259
+ /** PER STREAM, like the arbiter's own close runner. An overrun truncates the
260
+ * output mid-summary, so any number read out of it is unknown, not zero. */
261
+ export const MAX_BUFFER: number;
262
+ /** an untracked file larger than this is not scanned line-by-line; the skip
263
+ * is ANNOUNCED in the gap rather than passing as a clean scan */
264
+ export const MAX_SCAN_BYTES: number;
265
+ /** ceiling on a parser's term list — a runaway parser is a red, not a bill */
266
+ export const MAX_TERMS: 8;
267
+ /**
268
+ * WHICH KIND of instrument stop, in the ARBITER's own four-row vocabulary
269
+ * (`CLOSE_FAULTS`, src/ralph.js). Stamped at the site that OBSERVES the fault —
270
+ * never sniffed out of the stop's prose downstream (the typed-attribution rule:
271
+ * a `lib`/fault field is stamped at the throw site, and prose is a fallback for
272
+ * pre-typed spines only).
273
+ *
274
+ * The rows are kept apart for the reason `CLOSE_FAULTS` keeps them apart: they
275
+ * are DIFFERENT HUMAN DECISIONS. "raise the close timeout", "fix the argv" and
276
+ * "re-run, it was OOM-killed" are three answers, and a declared close that
277
+ * pooled all three into `failed` would hand every one of them the same option
278
+ * list. `failed` is the default because "the close cannot run" is the safe
279
+ * reading of a fault nothing else claimed.
280
+ */
281
+ export const STOP_FAULTS: Readonly<{
282
+ /** the stage cannot RUN at all: spawn failure, a missing seed commit, git refusing */
283
+ FAILED: "failed";
284
+ /** it ran and never finished judging */
285
+ TIMED_OUT: "timed-out";
286
+ /** it died by signal — no opinion was ever rendered */
287
+ KILLED: "killed";
288
+ /** it ran, came back, and judged nothing: unreadable output, a blown buffer,
289
+ * a number that was never reported, a parser that does not normalise */
290
+ CRASHED: "crashed";
291
+ }>;
292
+ export const AGGREGATES: readonly string[];
293
+ export const SIGNS: readonly number[];
294
+ /** every kind this executor implements. `judged-floor` / `human-confirms`
295
+ * are catalogue entries and are deliberately absent here: declaring one is
296
+ * counted demand (the request-red path), and reaching one at runtime is a
297
+ * stop — never a red, and never a silent pass. */
298
+ export const LIVE_KINDS: readonly string[];
299
+ export type Red = {
300
+ code: string;
301
+ path: string;
302
+ detail: string;
303
+ };
304
+ /**
305
+ * The arbiter's half of a stage run. Every field here is operator/runner
306
+ * territory: a declaration parameterises kinds, never the contracts.
307
+ */
308
+ export type Ctx = {
309
+ workdir: string;
310
+ seedRef: string;
311
+ gapKeep: string;
312
+ timeoutMsDefault?: number;
313
+ baselineMode?: "auto" | "worktree";
314
+ gapCap?: number;
315
+ maxBuffer?: number;
316
+ seedTrees?: SeedTrees;
317
+ };
318
+ export type StageResult = {
319
+ verdict: "green" | "red" | "instrument-stop" | "not-reached";
320
+ exitCode: number | null;
321
+ value: number | null;
322
+ baseline: number | null;
323
+ baselineSource: string | null;
324
+ gapLines: string[];
325
+ judged: boolean;
326
+ stage: string | null;
327
+ kind: string | null;
328
+ detail: Record<string, any>;
329
+ };
330
+ export type SeedTrees = {
331
+ ensure: (workdir: string, seedRef: string) => Promise<{
332
+ stop: string;
333
+ fault: string;
334
+ } | {
335
+ stop: null;
336
+ dir: string;
337
+ }>;
338
+ cleanup: () => Promise<{
339
+ ok: boolean;
340
+ leaked: string[];
341
+ detail: string | null;
342
+ }>;
343
+ };