@crewhaus/provability 0.0.0-stage → 0.8.1

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,459 @@
1
+ /**
2
+ * The provability contract — every type the gate, the tools, the graders, the
3
+ * CLI and every surface read. design/0.8.0-provability-release.md §2, §5.3.
4
+ *
5
+ * One bundle shape, every surface: the cli footer, the channel footer, the
6
+ * gateway envelope, the research report section, the `provability_graded`
7
+ * trace/event-log line (as a summary), the audit record and the JSON file
8
+ * under `.crewhaus/proofs/` all render THIS type. Add fields additively.
9
+ */
10
+ import type { IrProvability, IrProvabilityLevel } from "@crewhaus/ir";
11
+ /** The four rungs, lowest first. Order is load-bearing: see {@link levelRank}. */
12
+ export declare const PROVABILITY_LEVELS: readonly ["cited", "grounded", "checked", "proven"];
13
+ export type ProvabilityLevel = IrProvabilityLevel;
14
+ /** A rung, or `"none"` for evidence that meets no rung at all. */
15
+ export type Rung = ProvabilityLevel | "none";
16
+ /** The resolved gate configuration — exactly the lowered IR block. */
17
+ export type ProvabilityConfig = IrProvability;
18
+ /**
19
+ * What a definition line binds a claim to (design §4.1):
20
+ * - `source` — `[1]: https://…` / `./path` / `doi:…` / free text
21
+ * - `execution` — `[4]: tool:<toolUseId>`
22
+ * - `formal` — `[5]: proof:prf_<16 hex>`
23
+ * - `assumption` — `[A1]: assume: <text>`
24
+ */
25
+ export type BindingKind = "source" | "execution" | "formal" | "assumption";
26
+ /**
27
+ * Why a binding does not meet the floor. Stable strings: they appear in the
28
+ * retry nudge, the bundle, `provability audit` roll-ups and the graders.
29
+ */
30
+ export type UnmetReason =
31
+ /** The claim sentence carries no marker at all. */
32
+ "no_citation"
33
+ /** A marker in the claim has no `[n]: …` definition. */
34
+ | "no_definition"
35
+ /** The source is neither a URL, a `doi:`, nor a workspace path (and
36
+ * `sources.bibliographic` is off). */
37
+ | "not_locatable"
38
+ /** The URL's host fails `sources.allow` or matches `sources.deny`. */
39
+ | "host_denied"
40
+ /** The URL names loopback / RFC 1918 / link-local / metadata space. */
41
+ | "private_address"
42
+ /** A path source resolves outside the workspace. */
43
+ | "path_outside_workspace"
44
+ /** A path source does not exist. */
45
+ | "path_missing"
46
+ /** `sources.resolve: head` got no 2xx/3xx answer. */
47
+ | "unresolved"
48
+ /** The source's body was never fetched/read in this run's evidence. */
49
+ | "not_fetched"
50
+ /** The body was fetched but the claim's words are not in one window of it. */
51
+ | "not_found_in_source"
52
+ /** The words are there, with a negation the claim lacks. */
53
+ | "polarity_conflict"
54
+ /** `grounded.independent` asks for more distinct origins than the claim has. */
55
+ | "not_independent"
56
+ /** `tool:<id>` resolves to no recorded call. */
57
+ | "tool_missing"
58
+ /** The recorded call returned an error result. */
59
+ | "tool_errored"
60
+ /** The tool is not on `execution.allow`. */
61
+ | "tool_not_allowed"
62
+ /** The result text does not contain the claim's numerals / quoted values. */
63
+ | "value_absent"
64
+ /** Re-running the pure tool produced a different output digest. */
65
+ | "replay_mismatch"
66
+ /** `proof:<id>` names no stored artifact. */
67
+ | "proof_missing"
68
+ /** The stored artifact records `ok: false`. */
69
+ | "proof_rejected"
70
+ /** The artifact's recorded claim does not cover the answer's sentence. */
71
+ | "claim_mismatch"
72
+ /** `assumptions: forbid`, or past `max_assumptions`. */
73
+ | "assumption_forbidden"
74
+ /** The binding is valid but below the floor (e.g. a source at `checked`). */
75
+ | "below_floor";
76
+ /** One definition a claim's marker resolved to, and what it proved. */
77
+ export type ProofBinding = {
78
+ /** The marker as written: `"[1]"`, `"[^a]"`, `"[A1]"`. */
79
+ readonly ref: string;
80
+ readonly kind: BindingKind;
81
+ /** The highest rung this binding satisfies (`"none"` when it satisfies none). */
82
+ readonly rung: Rung;
83
+ /** True iff `rung` is at or above the configured floor — and, under
84
+ * `grounded.independent > 1`, its claim has enough distinct origins (a
85
+ * source binding that grounded alone carries `not_independent`). An
86
+ * assumption binding is never `ok`: it is not evidence. */
87
+ readonly ok: boolean;
88
+ /** Present when `ok` is false: the first reason it fell short. Absent on
89
+ * an ALLOWED assumption binding — it is not evidence and not a failure;
90
+ * its claim's `status` (`assumption`) says what it is. */
91
+ readonly reason?: UnmetReason;
92
+ /** Human-readable detail for the reason (no fetched content beyond `excerpt`). */
93
+ readonly detail?: string;
94
+ /** Normalized URL / workspace path / toolUseId / `prf_` id / assumption text. */
95
+ readonly origin?: string;
96
+ /** Digest of the body / result / artifact the verdict was made against (`sha256:<hex>`). */
97
+ readonly sha256?: string;
98
+ /** ≤240 chars, whitespace-collapsed: the span that grounded the claim. */
99
+ readonly excerpt?: string;
100
+ /** Execution bindings: whether a replay ran (and matched, when `ok`). */
101
+ readonly replayed?: boolean;
102
+ /** Grounded bindings: the opt-in waiver judge accepted a paraphrase miss. */
103
+ readonly judgeWaived?: boolean;
104
+ /**
105
+ * Grounded bindings: the claim's words in the body the run read under this
106
+ * origin are text an EARLIER call of the run sent to it — a `Write` or an
107
+ * `Edit` of the file, a PUT to the URL, or one of those to a place a copy
108
+ * or move then brought here — and this is that call. The binding still
109
+ * grounds (the file does say it now: the model may have just set the value
110
+ * it reports), but the run is its own source: design §10's residual "a
111
+ * model that fetches a page it wrote itself", surfaced on every surface and
112
+ * by `provability audit` rather than assumed away. Decided per origin: an
113
+ * `Edit` of one figure is the run's text as a `Write` of the sentence is.
114
+ * Also on an execution binding whose `FactCrossCheck` found the claim
115
+ * only in files the run wrote it into: such a check reaches `grounded` at
116
+ * most, and is counted with the sources (`ProofBundle.selfWritten`).
117
+ * (0.8.0 additive, completeness pass; convergence pass.)
118
+ */
119
+ readonly writtenBy?: {
120
+ readonly toolUseId: string;
121
+ readonly toolName: string;
122
+ };
123
+ /** Formal bindings: the checker id and the formal statement shown beside the prose. */
124
+ readonly checker?: string;
125
+ readonly statement?: string;
126
+ /**
127
+ * Formal bindings: the hypotheses the proof rests on (the artifact's
128
+ * `assumptions` — builtin `given` steps, SMT asserts and definitions),
129
+ * shown beside the statement. Design §6.4: hypotheses are assumptions in
130
+ * the bundle — counted with the declared ones (`ProofBundle.hypotheses`),
131
+ * refused under `assumptions: forbid` and capped by `max_assumptions`.
132
+ * Absent when the proof rests on none. (0.8.0 additive, fix pass.)
133
+ */
134
+ readonly assumptions?: readonly string[];
135
+ };
136
+ export type ClaimStatus = "met" | "unmet" | "assumption";
137
+ /** One extracted claim and every binding its markers resolved to. */
138
+ export type ProofClaim = {
139
+ readonly index: number;
140
+ /** The claim sentence as it appears in the answer (clipped to 400 chars). */
141
+ readonly text: string;
142
+ readonly line?: number;
143
+ readonly status: ClaimStatus;
144
+ /** The highest rung any binding of this claim reached. */
145
+ readonly best: Rung;
146
+ readonly bindings: ReadonlyArray<ProofBinding>;
147
+ /**
148
+ * Present when the claim is text a definition line shows after what it
149
+ * names (`[1]: https://… Payments clear within 5 days`): that definition's
150
+ * marker, `"[1]"`. Such text renders as prose, so it is read as claims
151
+ * citing that definition rather than shown to the reader unread. (0.8.0
152
+ * additive, W1-C fix pass.)
153
+ */
154
+ readonly definition?: string;
155
+ };
156
+ export type ProofVerdict = "pass" | "fail" | "unverified";
157
+ /** The per-answer artifact (design §5.3). */
158
+ export type ProofBundle = {
159
+ readonly version: 1;
160
+ /** The floor. */
161
+ readonly level: ProvabilityLevel;
162
+ /** `unverified` — the verifier threw; the gate failed open (§5.1). */
163
+ readonly verdict: ProofVerdict;
164
+ /** met / (claims − assumptions); 1 for the vacuous zero-claim case. */
165
+ readonly score: number;
166
+ readonly threshold: number;
167
+ /** The highest floor this answer would have passed. */
168
+ readonly achieved: Rung;
169
+ readonly claims: ReadonlyArray<ProofClaim>;
170
+ /** Count of extracted claims (assumption claims included). */
171
+ readonly claimCount: number;
172
+ readonly met: number;
173
+ readonly unmet: number;
174
+ /** Claims whose status is `assumption`: accepted, and out of the score. */
175
+ readonly assumptions: number;
176
+ /**
177
+ * Declared assumptions the config refused — every one under `assumptions:
178
+ * forbid`, the ones past `max_assumptions` otherwise. Their claims are
179
+ * `unmet` (binding reason `assumption_forbidden`), and any refusal fails
180
+ * the verdict whatever the score (design §2: the turn passes only when
181
+ * assumptions ≤ max_assumptions). The answer declared `assumptions +
182
+ * assumptionsRefused` assumptions in all. Absent when none was refused.
183
+ * (0.8.0 additive, W1-C fix pass.)
184
+ */
185
+ readonly assumptionsRefused?: number;
186
+ /**
187
+ * Distinct hypotheses the accepted proofs behind `proven` bindings rest on
188
+ * (`ProofBinding.assumptions`): assumptions too (design §6.4, §10), so
189
+ * every surface counts them with `assumptions` — outside the score, which
190
+ * counts claims. A claim refused for its proof's hypotheses (`forbid`, or
191
+ * past `max_assumptions`) is in `assumptionsRefused` instead. Absent when
192
+ * none. (0.8.0 additive, fix pass.)
193
+ */
194
+ readonly hypotheses?: number;
195
+ /**
196
+ * Distinct source origins behind the claims: at a `cited` floor every
197
+ * origin that passed the cited checks, at `grounded` and above every
198
+ * origin that grounded a claim — what "N sources" beside the floor means.
199
+ * A `FactCrossCheck` of a file the run wrote stands where that file would,
200
+ * so it counts here, by its call (`ProofBinding.writtenBy`).
201
+ */
202
+ readonly sources: number;
203
+ /**
204
+ * Of those, the origins whose grounding text the run wrote itself earlier
205
+ * in the session (`ProofBinding.writtenBy`): the footer shows them beside
206
+ * the sources, and `provability audit` lists them as a gaming signal.
207
+ * Absent when none. (0.8.0 additive, completeness pass.)
208
+ */
209
+ readonly selfWritten?: number;
210
+ /** Bindings the waiver judge flipped. */
211
+ readonly judgeWaived: number;
212
+ /** 0 = the original answer, n = the n-th gate-triggered retry. */
213
+ readonly retryIndex: number;
214
+ /** Proof checkers whose artifacts back `proven` bindings, with the axioms
215
+ * and (when any) the hypotheses each proof depends on (design §2, §6.4). */
216
+ readonly checkers?: ReadonlyArray<{
217
+ readonly id: string;
218
+ readonly artifact: string;
219
+ readonly axioms: readonly string[];
220
+ /** The hypotheses the proof rests on; absent when none. (0.8.0 additive, fix pass.) */
221
+ readonly assumptions?: readonly string[];
222
+ }>;
223
+ /** Present on a zero-claim answer: why the pass is vacuous. */
224
+ readonly vacuous?: string;
225
+ /** Present when `verdict === "unverified"`: what the verifier threw. */
226
+ readonly error?: string;
227
+ /** The waiver judge's model and spend, when it ran. */
228
+ readonly judgeModel?: string;
229
+ readonly spentUsdMicros?: number;
230
+ readonly createdAt: string;
231
+ /** `sha256:<hex>` over the canonical JSON of every other field (see `bundleHash`). */
232
+ readonly bundleHash: string;
233
+ };
234
+ /** The summary carried by the `provability_graded` trace/event-log line and audit record. */
235
+ export type ProvabilityGradedSummary = {
236
+ readonly level: ProvabilityLevel;
237
+ readonly verdict: ProofVerdict;
238
+ readonly score: number;
239
+ readonly threshold: number;
240
+ readonly achieved: Rung;
241
+ readonly claims: number;
242
+ readonly met: number;
243
+ readonly unmet: number;
244
+ readonly assumptions: number;
245
+ /** `ProofBundle.assumptionsRefused`, when any. (0.8.0 additive, W1-C fix pass.) */
246
+ readonly assumptionsRefused?: number;
247
+ /**
248
+ * `ProofBundle.hypotheses` — the hypotheses accepted proofs rest on,
249
+ * assumptions too (design §6.4, §10) — when any. (0.8.0 additive, fix pass.)
250
+ */
251
+ readonly hypotheses?: number;
252
+ /**
253
+ * `ProofBundle.selfWritten` — source origins whose grounding text the run
254
+ * wrote itself — when any. (0.8.0 additive, completeness pass.)
255
+ */
256
+ readonly selfWritten?: number;
257
+ readonly retryIndex: number;
258
+ readonly bundleHash: string;
259
+ readonly judgeModel?: string;
260
+ readonly judgeCostUsdMicros?: number;
261
+ };
262
+ /**
263
+ * One recorded tool call: the input the model sent and the result it got,
264
+ * verbatim, as the session event log (or the in-memory transcript) holds it.
265
+ */
266
+ export type EvidenceRecord = {
267
+ readonly toolUseId: string;
268
+ readonly toolName: string;
269
+ readonly input: unknown;
270
+ /** The result text, verbatim, as the log holds it. */
271
+ readonly content: string;
272
+ /**
273
+ * The whole output when `content` is the runtime's spill preview of it
274
+ * (`@crewhaus/tool-result-store`: the head, then the path the output was
275
+ * saved at), read back from that file (evidence.ts `spilledOutput`). The
276
+ * verifiers read a record's text as `body ?? content`; a replay is
277
+ * compared with `content`, the form the log holds. (0.8.0 additive, fix
278
+ * pass.)
279
+ */
280
+ readonly body?: string;
281
+ readonly isError: boolean;
282
+ /** The session whose log carried the call (child sessions included). */
283
+ readonly sessionId?: string;
284
+ /** Normalized origins (URLs / workspace paths) this call fetched or read
285
+ * as a WHOLE body: its input's `url`/`path` fields and the URL a fetch's
286
+ * header names. `bySource` answers these with the whole record. */
287
+ readonly origins: readonly string[];
288
+ /**
289
+ * The HTTP status a fetcher's own header reports (WebFetch's `Status:`,
290
+ * Fetch's status line). A body under a status outside 2xx is the server's
291
+ * error page, not the page: `grounded` does not read it. (0.8.0 additive,
292
+ * fix pass.)
293
+ */
294
+ readonly status?: number;
295
+ /** Turn number, when the log recorded one (used by `scope: turn`). */
296
+ readonly turnNumber?: number;
297
+ /**
298
+ * A result that carries several hits, each from its own origin (Retrieve
299
+ * hits, a JSON search result with `url`/`source` fields): each hit's own
300
+ * text under its own normalized origin. `bySource` answers a hit's origin
301
+ * with that hit's text alone, never the whole result — otherwise one hit
302
+ * could ground a claim cited to another. Absent when the result has none.
303
+ * (0.8.0 additive, W1-C.)
304
+ */
305
+ readonly hits?: ReadonlyArray<{
306
+ readonly origin: string;
307
+ readonly content: string;
308
+ }>;
309
+ };
310
+ /** The session-scoped view of every body the run read and every result it got. */
311
+ export interface EvidenceIndex {
312
+ /** Records whose fetched/read origin normalizes to `origin`. For a hit's
313
+ * origin (`EvidenceRecord.hits`) the record returned is a view of the call
314
+ * whose `content` is that hit's text and whose `origins` is `[origin]`. */
315
+ bySource(origin: string): ReadonlyArray<EvidenceRecord>;
316
+ /**
317
+ * The record for one toolUseId, when the run made that call: the graded
318
+ * session's own call first, then a sub-agent's. An id only several
319
+ * sub-agent sessions share (a provider that numbers ids per conversation)
320
+ * is ambiguous and answers `undefined` (see `callsById`).
321
+ */
322
+ byToolUseId(toolUseId: string): EvidenceRecord | undefined;
323
+ /**
324
+ * Every call recorded under one toolUseId across the sessions the index
325
+ * read, the graded session's first — so a verifier can tell "no such
326
+ * call" from "several, and no way to tell which". Optional: an index
327
+ * without it reads as one call per id. (0.8.0 additive, fix pass.)
328
+ */
329
+ callsById?(toolUseId: string): ReadonlyArray<EvidenceRecord>;
330
+ /** Every record, in the order the calls were made. */
331
+ all(): ReadonlyArray<EvidenceRecord>;
332
+ }
333
+ /** What `ProofCheck` persists and the `proven` verifier reads (design §6.4). */
334
+ export type ProofArtifact = {
335
+ readonly version: 1;
336
+ /** `prf_<16 hex>`. */
337
+ readonly id: string;
338
+ /** Checker id: `builtin` | `smt` | `lean4` | a plugin id. */
339
+ readonly checker: string;
340
+ /** The prose claim the proof is for (the sentence the answer will carry). */
341
+ readonly claim: string;
342
+ /** The formal statement, in the checker's language. */
343
+ readonly statement: string;
344
+ /** The proof / certificate / SMT problem, verbatim. */
345
+ readonly proof: string;
346
+ readonly ok: boolean;
347
+ /** Hypotheses the proof rests on (`given` steps, SMT asserts). */
348
+ readonly assumptions: readonly string[];
349
+ /** Axioms beyond the checker's base (lean `#print axioms`). */
350
+ readonly axioms: readonly string[];
351
+ /** Checker output, clipped. */
352
+ readonly output: string;
353
+ readonly counterexample?: string;
354
+ readonly reason?: string;
355
+ /** `sha256:<hex>` of `output`. */
356
+ readonly outputDigest: string;
357
+ readonly checkedAt: string;
358
+ readonly durationMs?: number;
359
+ };
360
+ export interface ProofArtifactStore {
361
+ get(id: string): Promise<ProofArtifact | undefined>;
362
+ put(artifact: ProofArtifact): Promise<void>;
363
+ list(): Promise<ReadonlyArray<ProofArtifact>>;
364
+ }
365
+ /** Result of re-executing a pure tool for `checked` replay. */
366
+ export type ReplayResult = {
367
+ readonly content: string;
368
+ readonly isError: boolean;
369
+ };
370
+ /**
371
+ * A HEAD the caller's resolver refused to send (`VerifyOptions.resolveHead`):
372
+ * `egress` — the run's egress fabric found content a boundary tagged (a
373
+ * secret a tool read) in the URL, so the request would have carried it out.
374
+ * The URL is not checked, and the binding says why. (0.8.0 additive, fix
375
+ * pass.)
376
+ */
377
+ export type HeadRefusal = {
378
+ readonly refused: "egress";
379
+ };
380
+ /** A stored proof checked again (`VerifyOptions.recheckProof`). */
381
+ export type RecheckedProof = {
382
+ readonly ok: boolean;
383
+ readonly assumptions?: readonly string[];
384
+ readonly axioms?: readonly string[];
385
+ };
386
+ /** Verdict of the opt-in paraphrase-waiver judge (design §6.2). */
387
+ export type JudgeWaiverVerdict = {
388
+ readonly supported: boolean;
389
+ readonly model?: string;
390
+ readonly costUsdMicros?: number;
391
+ };
392
+ export type VerifyOptions = {
393
+ readonly config: ProvabilityConfig;
394
+ readonly evidence: EvidenceIndex;
395
+ /** Workspace root for path sources (default: process.cwd()). */
396
+ readonly workspaceRoot?: string;
397
+ /**
398
+ * Directories that hold the harness's own state — the session logs, the
399
+ * proofs tree, spilled results, memory — wherever this run put them
400
+ * (absolute, or relative to `workspaceRoot`). A path source under one is
401
+ * never grounded: what is there is the model's own text (an
402
+ * `assistant_message` line is an answer verbatim). `.crewhaus/` and
403
+ * `.crewhaus-shared/` under the workspace are always refused; this names
404
+ * the rest (a relocated `CREWHAUS_SESSION_DIR`, a tenant's root). (0.8.0
405
+ * additive, fix pass.)
406
+ */
407
+ readonly stateRoots?: ReadonlyArray<string>;
408
+ /** Where relative path sources are measured from (default: workspaceRoot). */
409
+ readonly baseDir?: string;
410
+ /** Proof artifacts for `proof:` bindings. Absent ⇒ every formal binding is `proof_missing`. */
411
+ readonly artifacts?: ProofArtifactStore;
412
+ /**
413
+ * Re-run a stored artifact's checker on its statement and proof (design
414
+ * §6.4: a registered checker ACCEPTED it) — wired for a checker that is
415
+ * pure and cheap enough to run at gate time, the builtin certificate
416
+ * checker. `undefined` when it cannot re-check this one (another checker,
417
+ * none wired); a throw reads the same. A re-check that rejects is
418
+ * `proof_rejected`; one that accepts supplies the hypotheses and axioms
419
+ * the bundle lists, in place of the stored ones. (0.8.0 additive, fix
420
+ * pass.)
421
+ */
422
+ readonly recheckProof?: (artifact: ProofArtifact) => Promise<RecheckedProof | undefined> | RecheckedProof | undefined;
423
+ /**
424
+ * Accept a stored artifact WITHOUT the `ProofCheck` call that wrote it in
425
+ * `evidence`. Off by default and never on the gate: an artifact is a file
426
+ * in the workspace, so the model can write one (or flip a rejected one to
427
+ * `ok: true`) with its own file tools, and only the run's own record of
428
+ * the check shows a checker accepted it. For an offline check of a
429
+ * document whose run's log is not at hand. (0.8.0 additive, fix pass.)
430
+ */
431
+ readonly trustStoredProofs?: boolean;
432
+ /** Re-execute a tool with its logged input. Absent ⇒ no replay (bindings record `replayed: false`). */
433
+ readonly replay?: (toolName: string, input: unknown) => Promise<ReplayResult | undefined>;
434
+ /** Whether a tool is pure (readOnly && concurrencySafe) and so may be replayed. */
435
+ readonly isPureTool?: (toolName: string) => boolean;
436
+ /**
437
+ * `sources.resolve: head` — the HTTP status of one guarded HEAD, or
438
+ * undefined on failure. Up to a few run at once, under one deadline for
439
+ * the whole verification (`headDeadlineMs`); `signal` aborts when that
440
+ * deadline passes.
441
+ */
442
+ readonly resolveHead?: (url: string, signal?: AbortSignal) => Promise<number | HeadRefusal | undefined>;
443
+ /**
444
+ * How long one verification waits on its HEAD requests in all (default
445
+ * 15 s): a URL not answered by then is `unresolved`, not checked. (0.8.0
446
+ * additive, fix pass.)
447
+ */
448
+ readonly headDeadlineMs?: number;
449
+ /** The opt-in waiver judge; called only for paraphrase-coverage misses at `grounded`. */
450
+ readonly judgeWaiver?: (input: {
451
+ readonly claim: string;
452
+ readonly excerpt: string;
453
+ readonly source: string;
454
+ }) => Promise<JudgeWaiverVerdict>;
455
+ /** Restrict evidence to these turn numbers (`scope: turn`). */
456
+ readonly turnNumbers?: ReadonlySet<number>;
457
+ readonly retryIndex?: number;
458
+ readonly now?: () => Date;
459
+ };
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ /** The four rungs, lowest first. Order is load-bearing: see {@link levelRank}. */
2
+ export const PROVABILITY_LEVELS = ["cited", "grounded", "checked", "proven"];
@@ -0,0 +1,50 @@
1
+ /**
2
+ * The vacuous-pass backstop (design §2, §0 A and B).
3
+ *
4
+ * An answer with no claims passes every floor with nothing to prove — the
5
+ * right verdict for a greeting, a clarifying question or a code block, and
6
+ * the wrong one for an answer whose claim the extractor failed to see.
7
+ * Review rounds one and two each found new shapes that merged a claim into
8
+ * a question or a heading — "30 days [1] — anything else?", "1,284.60; want
9
+ * the PDF?", a no-break space, a `<br>`, U+2028, a table row, a heading over
10
+ * a question — and claim-ledger's `extractClaims` saw none of them, so the
11
+ * gate served scenario A or B with a ✓. Each was fixed shape by shape, and
12
+ * each fix left the next shape open. So the verdict does not rest on the
13
+ * extractor alone: an answer may be vacuous only when what a reader sees
14
+ * of it, code and definition lines aside, carries
15
+ *
16
+ * - no citation marker at all, and
17
+ * - no figure outside a question,
18
+ *
19
+ * read by a segmenter far blunter than the extractor, which cuts at every
20
+ * line break (U+0085, U+2028, U+2029, `\f`, `\v` and `<br>` included),
21
+ * every sentence terminator whether a space follows it or not, every
22
+ * semicolon, em or en dash and table pipe — never at a comma. A heading
23
+ * is a title to the figure rule but for what it states after a label
24
+ * (claim-ledger's `headingClaim` reads the headings that state one); a
25
+ * marker in it still counts. Otherwise
26
+ * each segment that carries a marker, and each that states a figure and is
27
+ * no question, is a claim, verified like any other: "30 days" cut off a
28
+ * trailing "anything else?" is checked against the page, and fails where
29
+ * the page says 14.
30
+ *
31
+ * A question stays a question. A piece of text that ends in `?` is a
32
+ * question from the first segment in it that opens as one ("Which of the 4
33
+ * plans do you mean — Basic or Pro?", "Should I book 2 rooms\nfor Monday?")
34
+ * or ends as one; what comes before that segment is said, not asked ("The
35
+ * refund window is 30 days — anything else?"). A clarifying question with a
36
+ * figure in it ("Do you want 2 or 3 copies?"), a greeting, and an answer
37
+ * that is only code stay vacuous.
38
+ */
39
+ import { type ExtractedClaim } from "@crewhaus/claim-ledger";
40
+ /**
41
+ * Claims the backstop reads in an answer the extractor found nothing to
42
+ * count in (see the module doc), numbered from `firstIndex`. Each is the
43
+ * segment as written (`raw`, markers in it), read as `plainClaim` reads a
44
+ * sentence, citing the markers it carries — a marker the document defines,
45
+ * a numeral, a footnote or an assumption id; a segment that is only
46
+ * markers gives them to the segment before it. A segment the extractor
47
+ * already returned (an assumption it read) is not read twice. Empty when
48
+ * the answer may be vacuous.
49
+ */
50
+ export declare function backstopClaims(text: string, definitions: ReadonlyMap<string, string>, extracted: ReadonlyArray<ExtractedClaim>, firstIndex: number): ExtractedClaim[];