bareloop 0.37.0 → 0.39.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 +53 -0
- package/bareloop.context.md +48 -26
- package/package.json +3 -3
- package/src/authorflow.js +3 -2
- package/src/authoring.js +62 -3
- package/src/authorjob.js +37 -24
- package/src/authorreadout.js +449 -2
- package/src/authorrun.js +21 -4
- package/src/calibrate.js +84 -10
- package/src/cardauthor.js +4 -1
- package/src/declaredclose.js +10 -4
- package/src/job.js +1 -1
- package/src/judged.js +322 -28
- package/src/kinds.js +13 -1
- package/src/legs.js +15 -0
- package/src/panel/authorroutes.js +9 -6
- package/src/panel/authorsession.js +66 -49
- package/src/panel/index.html +321 -355
- package/src/panel/server.js +33 -13
- package/src/panel/status.js +5 -4
- package/src/planrun.js +4 -3
- package/src/providers.js +27 -0
- package/src/ralph.js +8 -2
- package/src/replay.js +23 -3
- package/src/trend.js +1 -0
- package/types/authoring.d.ts +24 -1
- package/types/authorjob.d.ts +17 -10
- package/types/authorreadout.d.ts +106 -0
- package/types/calibrate.d.ts +4 -1
- package/types/declaredclose.d.ts +2 -1
- package/types/judged.d.ts +21 -2
- package/types/legs.d.ts +12 -0
- package/types/panel/authorsession.d.ts +15 -1
- package/types/panel/server.d.ts +5 -4
- package/types/panel/status.d.ts +1 -1
- package/types/providers.d.ts +7 -0
- package/types/ralph.d.ts +6 -1
- package/types/trend.d.ts +1 -0
package/src/panel/server.js
CHANGED
|
@@ -74,7 +74,7 @@ export const RUNID_RE = /^[A-Za-z0-9._~-]+$/;
|
|
|
74
74
|
export const DEFAULT_PORT = 4700;
|
|
75
75
|
|
|
76
76
|
/**
|
|
77
|
-
* `[✓]`/`[✗]`/`[▶]` — the ONLY vocabulary a result is ever rendered in
|
|
77
|
+
* `[✓]`/`[✗]`/`[■]`/`[▶]` — the ONLY vocabulary a result is ever rendered in
|
|
78
78
|
* (auto-memory `ui-verdict-words.md`: never the words green/red/soft-green
|
|
79
79
|
* anywhere in the page). `null` (no `job-end` reached — a killed-mid-run or
|
|
80
80
|
* still-running spine) reads `▶` — the same "in progress / unresolved" glyph
|
|
@@ -82,10 +82,10 @@ export const DEFAULT_PORT = 4700;
|
|
|
82
82
|
* running attempt and an archived spine that never reached its own end are
|
|
83
83
|
* the same fact from this read-only side: no verdict has been recorded yet.
|
|
84
84
|
* @param {string|null} outcome
|
|
85
|
-
* @returns {'✓'|'✗'|'▶'}
|
|
85
|
+
* @returns {'✓'|'✗'|'■'|'▶'}
|
|
86
86
|
*/
|
|
87
87
|
export function glyphForOutcome(outcome) {
|
|
88
|
-
return /** @type {'✓'|'✗'|'▶'} */ (statusFor({ outcome }).sign);
|
|
88
|
+
return /** @type {'✓'|'✗'|'■'|'▶'} */ (statusFor({ outcome }).sign);
|
|
89
89
|
}
|
|
90
90
|
|
|
91
91
|
/**
|
|
@@ -417,7 +417,7 @@ const GOAL_NOT_MET = new Set(['plan-red', 'check-red', 'step-red', 'escalated'])
|
|
|
417
417
|
* @returns {{reason: string, next: string, line: string, actions: {id: string, label: string}[]}|null}
|
|
418
418
|
*/
|
|
419
419
|
export function endedFor(summary, death, o = {}) {
|
|
420
|
-
const base = endedBase(summary, death, o);
|
|
420
|
+
const base = withEditAction(endedBase(summary, death, o));
|
|
421
421
|
const wt = o.worktree;
|
|
422
422
|
if (!base || !wt) return base;
|
|
423
423
|
// P6 item 1 — the run worked on a worktree in the person's own repo. GREEN: the branch carries the work (the folder is
|
|
@@ -432,21 +432,31 @@ export function endedFor(summary, death, o = {}) {
|
|
|
432
432
|
return wt.exists ? { ...base, next: `${base.next} Your work is in ${wt.folder}.` } : base;
|
|
433
433
|
}
|
|
434
434
|
|
|
435
|
+
/**
|
|
436
|
+
* "Edit in chat" (hamr 2026-10-07): offered on EVERY non-green ending (failed, capped, stopped, died, every other red),
|
|
437
|
+
* after Resume when both show; a green ending keeps Reuse workflow only. ONE place, so no ending branch can forget it.
|
|
438
|
+
* @param {{reason: string, next: string, line: string, actions: {id: string, label: string}[]}|null} ended
|
|
439
|
+
*/
|
|
440
|
+
function withEditAction(ended) {
|
|
441
|
+
if (!ended || ended.actions.some((a) => a.id === 'reuse')) return ended;
|
|
442
|
+
return { ...ended, actions: [...ended.actions, { id: 'edit', label: 'Edit in chat' }] };
|
|
443
|
+
}
|
|
444
|
+
|
|
435
445
|
/** @param {any} summary @param {any} death @param {any} o */
|
|
436
446
|
function endedBase(summary, death, o) {
|
|
437
447
|
const resumeOk = !!(o.resume && o.resume.ok);
|
|
438
448
|
/** @type {{id: string, label: string}[]} */
|
|
439
449
|
const RESUME = [{ id: 'resume', label: 'Resume' }];
|
|
440
450
|
/** Reuse workflow (replaces P5 item 3's Start from this, hamr 2026-10-03): the same signed job on a new source — a NEW
|
|
441
|
-
* run, never a rerun. Offered on GREEN rows only; every red row says "Change the job:
|
|
451
|
+
* run, never a rerun. Offered on GREEN rows only; every red row says "Change the job: press Edit in chat" instead. */
|
|
442
452
|
const REUSE = [{ id: 'reuse', label: 'Reuse workflow' }];
|
|
443
|
-
const CHANGE = 'Change the job:
|
|
453
|
+
const CHANGE = 'Change the job: press Edit in chat.';
|
|
444
454
|
const detailOf = (/** @type {string|null} */ s) => {
|
|
445
455
|
if (typeof s !== 'string' || s.length === 0) return '';
|
|
446
456
|
return s.length > 120 ? `${s.slice(0, 120)}…` : s;
|
|
447
457
|
};
|
|
448
458
|
/** a resumable ending whose Resume the engine would refuse: say so, never offer the button */
|
|
449
|
-
const resumeOr = (/** @type {string} */ okNext) => (resumeOk ? okNext : `Resume is not available for this run${o.resume && o.resume.why ? ` (${o.resume.why})` : ''}
|
|
459
|
+
const resumeOr = (/** @type {string} */ okNext) => (resumeOk ? okNext : `Resume is not available for this run${o.resume && o.resume.why ? ` (${o.resume.why})` : ''}. ${CHANGE}`);
|
|
450
460
|
|
|
451
461
|
if (death.died) {
|
|
452
462
|
return {
|
|
@@ -497,13 +507,20 @@ function endedBase(summary, death, o) {
|
|
|
497
507
|
|
|
498
508
|
if (outcome === 'green' || outcome === 'already-green' || outcome === 'satisfied') {
|
|
499
509
|
if (o.destinationRefused) {
|
|
510
|
+
// Reuse workflow only on a run that earned its green (hamr 2026-10-08, as below); the rest take Edit in chat
|
|
511
|
+
// (withEditAction adds it) and are told to change the job, not to reuse it
|
|
512
|
+
const earned = outcome === 'green';
|
|
500
513
|
return {
|
|
501
514
|
reason: 'Goal met, but the output could not be delivered.',
|
|
502
|
-
next: 'Fix the destination, then Reuse workflow.',
|
|
515
|
+
next: earned ? 'Fix the destination, then Reuse workflow.' : CHANGE,
|
|
503
516
|
line: `${GOAL_MET_LINE} — not delivered`,
|
|
504
|
-
actions: REUSE,
|
|
517
|
+
actions: earned ? REUSE : [],
|
|
505
518
|
};
|
|
506
519
|
}
|
|
520
|
+
// hamr 2026-10-08: already-green (the check passed before any work) is not a reusable job — only a run that
|
|
521
|
+
// earned its green offers Reuse workflow; the rest take Edit in chat (withEditAction adds it). `satisfied` is a
|
|
522
|
+
// close VERDICT, never a run outcome (ralph and the job-end emitters write `green`), so it rides with already-green.
|
|
523
|
+
if (outcome !== 'green') return { reason: 'Goal met.', next: CHANGE, line: GOAL_MET_LINE, actions: [] };
|
|
507
524
|
return { reason: 'Goal met.', next: 'Nothing to do.', line: GOAL_MET_LINE, actions: REUSE };
|
|
508
525
|
}
|
|
509
526
|
if (outcome === 'cap-halt') {
|
|
@@ -545,7 +562,7 @@ function endedBase(summary, death, o) {
|
|
|
545
562
|
if (outcome === 'step-stalled') {
|
|
546
563
|
return {
|
|
547
564
|
reason: 'A step stopped making progress.',
|
|
548
|
-
next: resumeOr('Resume, or change the job:
|
|
565
|
+
next: resumeOr('Resume, or change the job: press Edit in chat.'),
|
|
549
566
|
line: resumeOk ? 'step stalled — resume' : 'step stalled',
|
|
550
567
|
actions: resumeOk ? RESUME : [],
|
|
551
568
|
};
|
|
@@ -2408,7 +2425,7 @@ export function reuseLine(record) {
|
|
|
2408
2425
|
* to copy: change the job: Clear the card). `null` when the runid is not listed.
|
|
2409
2426
|
* @param {string} runid
|
|
2410
2427
|
* @param {{ home?: string, model?: string }} [opts] `model` = the Model box's current Name (the estimate counts only runs on that worker); absent = the card's own Model
|
|
2411
|
-
* @returns {{ok: true, origin: {runid: string, job: string}, card: Record<string, any>, from: 'signed job',
|
|
2428
|
+
* @returns {{ok: true, origin: {runid: string, job: string}, outcome: string|null, card: Record<string, any>, from: 'signed job',
|
|
2412
2429
|
* locked: readonly string[], open: readonly string[], note: string|null,
|
|
2413
2430
|
* specHash: string, workflowKey: string, spec: any, specPath: string,
|
|
2414
2431
|
* trackRecord: ReturnType<typeof trackRecordFor>, line: string}|{ok: false, error: string}|null}
|
|
@@ -2447,6 +2464,8 @@ export function getStartFrom(runid, opts = {}) {
|
|
|
2447
2464
|
return {
|
|
2448
2465
|
ok: true,
|
|
2449
2466
|
origin: { runid, job: row.job },
|
|
2467
|
+
// the run's terminal outcome — the Reuse start (POST /api/author/start) refuses anything but a real green
|
|
2468
|
+
outcome: legsOf(records).at(-1)?.outcome ?? null,
|
|
2450
2469
|
card,
|
|
2451
2470
|
from: 'signed job',
|
|
2452
2471
|
locked: REUSE_LOCKED_FIELDS,
|
|
@@ -2480,8 +2499,9 @@ export function listReuseJobs(opts = {}) {
|
|
|
2480
2499
|
let records;
|
|
2481
2500
|
try { records = parseJsonl(row.spine).records; } catch { continue; }
|
|
2482
2501
|
const outcome = legsOf(records).at(-1)?.outcome ?? null;
|
|
2483
|
-
//
|
|
2484
|
-
|
|
2502
|
+
// only a REAL green (hamr 2026-10-08): `already-green` means the check passed before any work, so there is nothing to reuse
|
|
2503
|
+
// (src/reuse.js 'green-predates-run'). Narrower than the Run tab's Reuse button on purpose.
|
|
2504
|
+
if (outcome !== 'green') continue;
|
|
2485
2505
|
const signed = signedSpecForRun(row, records);
|
|
2486
2506
|
if (!signed) continue;
|
|
2487
2507
|
const key = workflowKey(signed.spec);
|
package/src/panel/status.js
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// hamr's ruling 2026-10-04 (option A): every surface (run cards, expanded run rows, the right-side header, imported
|
|
3
3
|
// rows) reads `status` from the server; the page never maps an outcome to a word itself.
|
|
4
4
|
//
|
|
5
|
-
// [▶] running · [·] waiting · [✓] passed · [✗] failed · [✗] capped · [
|
|
5
|
+
// [▶] running · [·] waiting · [✓] passed · [✗] failed · [✗] capped · [■] stopped · [?] died
|
|
6
6
|
//
|
|
7
7
|
// Outcome -> word (every outcome the panel knows; anything not listed is `failed`):
|
|
8
8
|
// green, already-green, satisfied -> passed
|
|
@@ -15,6 +15,8 @@
|
|
|
15
15
|
// and every other outcome (refusals, instrument stops) -> failed
|
|
16
16
|
// `waiting` is the step-level sign for a step that has not started; no run outcome maps to it.
|
|
17
17
|
|
|
18
|
+
import { capKindOf } from '../legs.js';
|
|
19
|
+
|
|
18
20
|
/** the sentence a passed run carries as its reason (the Ended block's `line`) */
|
|
19
21
|
export const GOAL_MET_LINE = 'goal met';
|
|
20
22
|
|
|
@@ -27,7 +29,7 @@ export const STATUS = Object.freeze({
|
|
|
27
29
|
passed: Object.freeze({ sign: '✓', word: 'passed' }),
|
|
28
30
|
failed: Object.freeze({ sign: '✗', word: 'failed' }),
|
|
29
31
|
capped: Object.freeze({ sign: '✗', word: 'capped' }),
|
|
30
|
-
stopped: Object.freeze({ sign: '
|
|
32
|
+
stopped: Object.freeze({ sign: '■', word: 'stopped' }),
|
|
31
33
|
died: Object.freeze({ sign: '?', word: 'died' }),
|
|
32
34
|
});
|
|
33
35
|
|
|
@@ -44,9 +46,8 @@ export function statusFor(o) {
|
|
|
44
46
|
if (o.died) key = 'died';
|
|
45
47
|
else if (out === null || out === undefined) key = 'running';
|
|
46
48
|
else if (out === 'green' || out === 'already-green' || out === 'satisfied') key = 'passed';
|
|
47
|
-
else if (
|
|
49
|
+
else if (capKindOf(o) !== null) key = 'capped';
|
|
48
50
|
else if (out === 'stopped') key = 'stopped';
|
|
49
|
-
else if (out === 'escalated' && ((o.category === 'cap-halt' && o.moneyHalt) || o.category === 'wall-halt')) key = 'capped';
|
|
50
51
|
return { key, ...STATUS[key] };
|
|
51
52
|
}
|
|
52
53
|
|
package/src/planrun.js
CHANGED
|
@@ -36,6 +36,7 @@ import { createLadder, STRIKE_LIMIT } from './ladder.js';
|
|
|
36
36
|
import { createTrend, FIX_STRIKE_LIMIT } from './trend.js';
|
|
37
37
|
import { TOOL_MENU, STORE_VERBS, checkMenu } from './job.js';
|
|
38
38
|
import { TOOL_BY_VERB, CTX_TOOLS, createCtxTools, toolAction, PERSONA_TOOLS, strategyFor } from './tools.js';
|
|
39
|
+
import { anySuppressionWarning } from './authoring.js';
|
|
39
40
|
import { createReadShim, readShimArm, readShimStrategy } from './readshim.js';
|
|
40
41
|
import { globToPrefix, redactSecrets, SECRET_PATTERNS } from './validate.js';
|
|
41
42
|
import { validateBridge, loadGate, newestEligibleVersion, reuseEligibility, quarantinesCredit, QUARANTINED_CODE } from './bridges.js';
|
|
@@ -2035,7 +2036,7 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
2035
2036
|
}
|
|
2036
2037
|
const listed = await seedListing(workdir, s.seedRef);
|
|
2037
2038
|
const v = listed.stop === null
|
|
2038
|
-
? validateCloseDecl(job.closeDecl, { at: 'closeDecl', listing: listed.files, verdictType: job.verdictType })
|
|
2039
|
+
? validateCloseDecl(job.closeDecl, { at: 'closeDecl', listing: listed.files, verdictType: job.verdictType, writeScope: job.writeScope })
|
|
2039
2040
|
: { ok: false, grounded: false, reds: [{ code: 'listing-unreadable', path: 'closeDecl', detail: listed.stop }] };
|
|
2040
2041
|
emit('close-decl', {
|
|
2041
2042
|
genre: job.closeDecl.genre,
|
|
@@ -2546,7 +2547,7 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
2546
2547
|
const readLines = grantedNames.has(TOOL_BY_VERB.read)
|
|
2547
2548
|
? (native && !shimArm.cap ? NATIVE_READ_STRATEGY : '') + readShimStrategy(shimArm)
|
|
2548
2549
|
: '';
|
|
2549
|
-
const system = PERSONA_TOOLS + strategyFor(granted) + readLines;
|
|
2550
|
+
const system = PERSONA_TOOLS + strategyFor(granted) + readLines + anySuppressionWarning(closeStagesOf(job));
|
|
2550
2551
|
/** @param {any} u @returns {{inputTokens: number, outputTokens: number, cacheReadTokens: number, cacheCreationTokens: number}} */
|
|
2551
2552
|
const usageOf = (u) => ({ inputTokens: u?.inputTokens ?? 0, outputTokens: u?.outputTokens ?? 0, cacheReadTokens: u?.cacheReadTokens ?? 0, cacheCreationTokens: u?.cacheCreationTokens ?? 0 });
|
|
2552
2553
|
|
|
@@ -4108,7 +4109,7 @@ export async function runPlan(job, { workdir, provider, nativeProvider, provider
|
|
|
4108
4109
|
// is not passed — it survives only inside the governor, as the bound for the
|
|
4109
4110
|
// case the trend instrument is blind.
|
|
4110
4111
|
middle, judge: async () => judgeClose(), ladder: fixGovernor, emit: emitL, redact: scrub,
|
|
4111
|
-
closeTimeoutMs, cwd: workdir, workerWrites: w.workerWrites, closeDir,
|
|
4112
|
+
closeTimeoutMs, cwd: workdir, workerWrites: w.workerWrites, closeDir, phase: 'fix',
|
|
4112
4113
|
});
|
|
4113
4114
|
} catch (e) {
|
|
4114
4115
|
planExecuted();
|
package/src/providers.js
CHANGED
|
@@ -115,6 +115,33 @@ const OPENAI_MODEL_OPTIONS = Object.freeze({
|
|
|
115
115
|
'deepseek-flash': Object.freeze({ legacyMaxTokens: true }),
|
|
116
116
|
});
|
|
117
117
|
|
|
118
|
+
/**
|
|
119
|
+
* Per-MODEL JUDGE-ONLY request options (F192, hamr ruling A 2026-10-09). deepseek-flash
|
|
120
|
+
* thinks by default and bills the reasoning as output: run mv117wde's rubric judge locate
|
|
121
|
+
* truncated at JUDGE_MAX_TOKENS=4000 four times, and a probe measured 1835 of 2409
|
|
122
|
+
* completion tokens as reasoning. The judge extracts quotes and decides nothing, so it
|
|
123
|
+
* runs with thinking off. These are PER-CALL options (bare-agent >=0.49 forwards
|
|
124
|
+
* `thinking` verbatim to `body.thinking`), never constructor options: the judge may be
|
|
125
|
+
* the very same provider instance as the worker (`buildRunnerProviders` reuses it), and
|
|
126
|
+
* the worker is deliberately left as it was. A model with no entry gets nothing; the
|
|
127
|
+
* field is never sent to a model not listed here (OpenAI/Anthropic would reject or
|
|
128
|
+
* misread it). Read through `judgeCallOptions` only — one owner.
|
|
129
|
+
*/
|
|
130
|
+
const OPENAI_JUDGE_CALL_OPTIONS = Object.freeze({
|
|
131
|
+
'deepseek-flash': Object.freeze({ thinking: Object.freeze({ type: 'disabled' }) }),
|
|
132
|
+
});
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* The per-call options the rubric JUDGE's model calls carry for this provider
|
|
136
|
+
* instance, from the allow-list above. Empty for any provider/model not listed.
|
|
137
|
+
* @param {any} provider a constructed bare-agent provider
|
|
138
|
+
* @returns {Record<string, any>}
|
|
139
|
+
*/
|
|
140
|
+
export function judgeCallOptions(provider) {
|
|
141
|
+
if (!(provider instanceof OpenAIProvider)) return {};
|
|
142
|
+
return OPENAI_JUDGE_CALL_OPTIONS[/** @type {keyof typeof OPENAI_JUDGE_CALL_OPTIONS} */ (provider.model)] ?? {};
|
|
143
|
+
}
|
|
144
|
+
|
|
118
145
|
/**
|
|
119
146
|
* @typedef {object} ProviderTableEntry
|
|
120
147
|
* @property {new (options: any) => any} ctor the bare-agent provider class
|
package/src/ralph.js
CHANGED
|
@@ -562,9 +562,13 @@ export const CLOSE_FAULTS = Object.freeze({
|
|
|
562
562
|
* (PRD item 27/M3 Part B), forwarded to the raw `close` path's `runClose`
|
|
563
563
|
* call unchanged; a caller supplying `judge` instead already closed over
|
|
564
564
|
* its own `closeDir` and this param does nothing for it.
|
|
565
|
+
* @param {string} [opts.phase] the phase this loop runs in, when it is not the step loop
|
|
566
|
+
* (the fix loop passes `'fix'`): the person's-stop text then names it ("during the fix
|
|
567
|
+
* phase") instead of saying a resume re-enters "this step", which only the step loop has.
|
|
568
|
+
* Text only — it changes no category, outcome or option.
|
|
565
569
|
* @returns {Promise<'green'|'escalated'>}
|
|
566
570
|
*/
|
|
567
|
-
export async function ralph({ middle, close, judge, capRuns, ladder, emit, redact, closeTimeoutMs, cwd, expect, judged, gapKeep, workerWrites, closeDir }) {
|
|
571
|
+
export async function ralph({ middle, close, judge, capRuns, ladder, emit, redact, closeTimeoutMs, cwd, expect, judged, gapKeep, workerWrites, closeDir, phase }) {
|
|
568
572
|
// PARAM GUARD (the BA-4 class — a caller's malformed argument, which is the one
|
|
569
573
|
// thing this shell throws for; a WORKER's bad reach comes back as a refusal
|
|
570
574
|
// RESULT instead, and that rule is about feedback, not about the API contract).
|
|
@@ -654,7 +658,9 @@ export async function ralph({ middle, close, judge, capRuns, ladder, emit, redac
|
|
|
654
658
|
'revise the goal/spec so the work fits the time (same re-approval)',
|
|
655
659
|
'abandon the task']],
|
|
656
660
|
// P5 item 5 — the PERSON asked to stop at a round boundary: nothing failed, nothing is discarded.
|
|
657
|
-
'stopped': [
|
|
661
|
+
'stopped': [phase
|
|
662
|
+
? `You stopped the run during the ${phase} phase. Nothing is discarded: the work on disk stands, and a resume continues the run.`
|
|
663
|
+
: 'You stopped the run. Nothing is discarded: the work on disk stands, and a resume re-enters this step.',
|
|
658
664
|
['resume (the same run continues)', 'abandon the task']],
|
|
659
665
|
// F66 — the STALL WATCHDOG gave up: no round completed for the stall window,
|
|
660
666
|
// three times over, and reissuing the call did not recover it. It borrows both
|
package/src/replay.js
CHANGED
|
@@ -104,7 +104,7 @@
|
|
|
104
104
|
import { runBehaviour, formatBehaviour } from './behaviour.js';
|
|
105
105
|
import { shortSha } from './codeversion.js';
|
|
106
106
|
import { SPEND_RECORD_TYPES, chainSpend } from './ledger.js';
|
|
107
|
-
import { legsOf } from './legs.js';
|
|
107
|
+
import { legsOf, capKindOf } from './legs.js';
|
|
108
108
|
|
|
109
109
|
/**
|
|
110
110
|
* @param {unknown} e
|
|
@@ -234,6 +234,16 @@ const ALL_REASON_MAX = 60;
|
|
|
234
234
|
* record fired) — printed literally, never blank, never a fabricated 0. */
|
|
235
235
|
const NONE_RECORDED = 'none recorded';
|
|
236
236
|
|
|
237
|
+
/**
|
|
238
|
+
* Did this leg end on a cap? Reads the leg's own last escalation and `money-halt` record.
|
|
239
|
+
* @param {import('./legs.js').Leg} leg
|
|
240
|
+
* @returns {'money'|'time'|null}
|
|
241
|
+
*/
|
|
242
|
+
function legCapKind(leg) {
|
|
243
|
+
const esc = leg.outcome === 'escalated' ? leg.records.findLast((r) => r && r.type === 'escalation') : null;
|
|
244
|
+
return capKindOf({ outcome: leg.outcome, category: esc?.category ?? null, moneyHalt: leg.records.some((r) => r && r.type === 'money-halt') });
|
|
245
|
+
}
|
|
246
|
+
|
|
237
247
|
/**
|
|
238
248
|
* `stopReason` for a non-green outcome: `category — decision — detail`, every
|
|
239
249
|
* part present that the escalation actually carries (see the field-site
|
|
@@ -737,11 +747,21 @@ export function replayRun(spineEvents, auditEvents = [], { runId = null, auditAv
|
|
|
737
747
|
// why this leg stopped the step (code-owned words); null for a step that ended itself or is still running
|
|
738
748
|
/** @type {string|null} */
|
|
739
749
|
let stopReason = null;
|
|
750
|
+
// a single-leg run has no leg boundary to cut a step, but a cap still ends it: its open step reads capped too
|
|
751
|
+
// (hamr 2026-10-08), from the same rule (`capKindOf`) the run's own status word uses
|
|
752
|
+
if (!end && myLeg === null) {
|
|
753
|
+
const only = legs[0];
|
|
754
|
+
const capKind = only ? legCapKind(only) : null;
|
|
755
|
+
if (capKind !== null) stopReason = capKind === 'money' ? 'money cap' : 'time cap';
|
|
756
|
+
}
|
|
740
757
|
if (!end && myLeg !== null) {
|
|
741
758
|
const o = myLeg.outcome;
|
|
742
759
|
const isLastLeg = myLegIdx === legs.length - 1;
|
|
743
760
|
if (cutByLeg || (isLastLeg && o !== null && o !== 'green' && o !== 'already-green')) {
|
|
744
|
-
|
|
761
|
+
// hamr 2026-10-08: a leg that ended `escalated` on a money-halt / wall-halt reads as the cap, exactly as the
|
|
762
|
+
// run's own status word does (src/panel/status.js statusFor) — derived HERE so every surface follows
|
|
763
|
+
const capKind = legCapKind(myLeg);
|
|
764
|
+
stopReason = capKind === 'money' ? 'money cap' : capKind === 'time' ? 'time cap' : o === 'provider-red' ? 'provider failed'
|
|
745
765
|
: o === 'step-stalled' ? 'step stalled' : o === 'stopped' ? 'you stopped it' : o === null ? 'died' : o;
|
|
746
766
|
}
|
|
747
767
|
}
|
|
@@ -832,7 +852,7 @@ export function replayRun(spineEvents, auditEvents = [], { runId = null, auditAv
|
|
|
832
852
|
return {
|
|
833
853
|
id,
|
|
834
854
|
occurrence: idOccurrence.get(id),
|
|
835
|
-
...(myLeg === null ? {} : { stopReason, continued, tryNumber }),
|
|
855
|
+
...(myLeg === null && stopReason === null ? {} : { stopReason, continued, tryNumber }),
|
|
836
856
|
outcome: end ? (end.outcome ?? null) : null,
|
|
837
857
|
...buildOccurrenceMetrics(startSeq, startTs, endSeq, endTs, roundsInWindow),
|
|
838
858
|
checks: { passed, failed },
|
package/src/trend.js
CHANGED
|
@@ -272,6 +272,7 @@ export function createTrend({ stageOrder = [], limit = FIX_STRIKE_LIMIT, blindCa
|
|
|
272
272
|
* @param {{gap?: string, stage?: string|null, value?: number|null}} o either a
|
|
273
273
|
* raw gap (parsed here) or an already-read `{stage, value}` — never both
|
|
274
274
|
* spellings of the same reading in one call.
|
|
275
|
+
* (A judged stage's value is its distinct red COUNT, supplied by the caller.)
|
|
275
276
|
* @returns {{iteration: number, stage: string, value: number|null, improved: boolean,
|
|
276
277
|
* comparable: boolean, noProgress: number, limit: number, stageIndex: number}}
|
|
277
278
|
*/
|
package/types/authoring.d.ts
CHANGED
|
@@ -80,6 +80,14 @@ export function envCapableKind(kind: string, catalogue?: Record<string, KindSpec
|
|
|
80
80
|
* @param {string} cmd @returns {string|null}
|
|
81
81
|
*/
|
|
82
82
|
export function deniedCommandReason(cmd: string): string | null;
|
|
83
|
+
/**
|
|
84
|
+
* The warning line for a job whose ACTUAL stages carry the js `any` pattern, else
|
|
85
|
+
* ''. Derived from the stages' patterns (never goal prose), so a job without the
|
|
86
|
+
* guard is told nothing about it.
|
|
87
|
+
* @param {any[]|null|undefined} stages
|
|
88
|
+
* @returns {string}
|
|
89
|
+
*/
|
|
90
|
+
export function anySuppressionWarning(stages: any[] | null | undefined): string;
|
|
83
91
|
/**
|
|
84
92
|
* The picked class's mandatory guards, FULLY ENUMERATED (D13 forward-compat
|
|
85
93
|
* point 3: an omittable-with-a-default field is the omitted-`tools` shape, where
|
|
@@ -219,6 +227,16 @@ export function ungroundedGenreEnv(lang: string, recorded: Record<string, string
|
|
|
219
227
|
element: string;
|
|
220
228
|
resolved: string;
|
|
221
229
|
}[];
|
|
230
|
+
/**
|
|
231
|
+
* Which of a judged stage's paths lie OUTSIDE the job's write fence? One owner of the rule "a check must be
|
|
232
|
+
* free to edit every file it can report on", called from every door that validates a declaration. It uses
|
|
233
|
+
* the shipped fence spelling (`globToPrefix`, the same containment reading plan.js's `insideFence` has) and
|
|
234
|
+
* returns `[]` when there is no fence to compare against or the paths are not a string list (shape is the
|
|
235
|
+
* schema walk's business).
|
|
236
|
+
* @param {unknown} paths @param {string[]|null|undefined} writeScope
|
|
237
|
+
* @returns {string[]}
|
|
238
|
+
*/
|
|
239
|
+
export function judgedOutsideFence(paths: unknown, writeScope: string[] | null | undefined): string[];
|
|
222
240
|
/**
|
|
223
241
|
* Validate an authored close DECLARATION. Never throws on model output — every
|
|
224
242
|
* failure is a named red, in the shipped `{ok, reds, <parsed>}` shape, and the
|
|
@@ -256,7 +274,9 @@ export function ungroundedGenreEnv(lang: string, recorded: Record<string, string
|
|
|
256
274
|
* @param {{catalogue?: Record<string, KindSpec>, listing?: string[]|null,
|
|
257
275
|
* guards?: {name: string, kind: string, params: Record<string, any>, fill: string[]}[]|null,
|
|
258
276
|
* envOwned?: string[]|null, envInjected?: Record<string, string>|null, deferListing?: boolean,
|
|
259
|
-
* verdictType?: string|null}} [opts]
|
|
277
|
+
* verdictType?: string|null, writeScope?: string[]|null}} [opts]
|
|
278
|
+
* `writeScope` — the job's signed write fence. Every path a judged stage judges must sit inside it
|
|
279
|
+
* ({@link judgedOutsideFence}); absent, the rule is not run (the plain-folder deferral has no fence).
|
|
260
280
|
* `listing` — repo-relative paths at the seed (`git ls-tree -r --name-only`).
|
|
261
281
|
* `guards` — the picked class's injected guards (`classGuards`).
|
|
262
282
|
* `verdictType` — the class the USER picked (`VERDICT_CLASSES`).
|
|
@@ -283,6 +303,7 @@ export function validateDeclaration(declaration: any, opts?: {
|
|
|
283
303
|
envInjected?: Record<string, string> | null;
|
|
284
304
|
deferListing?: boolean;
|
|
285
305
|
verdictType?: string | null;
|
|
306
|
+
writeScope?: string[] | null;
|
|
286
307
|
}): {
|
|
287
308
|
ok: boolean;
|
|
288
309
|
reds: Red[];
|
|
@@ -455,6 +476,8 @@ export const DENIED_COMMANDS: readonly string[];
|
|
|
455
476
|
*/
|
|
456
477
|
export const TYPES_GENRE_TEMPLATE: "1. The STRICT form of the language's type checker is used only when a confirmed check \u2014 or,\n absent a confirm turn, the goal and interview answers \u2014 asks for strict typing; otherwise the\n checker runs at whatever strictness the repo's own scripts already use.\n2. Tools are invoked so their binaries actually resolve \u2014 through the project's own package\n runner or language module runner, never a bare binary name.\n3. The WORK stages \u2014 typecheck (error count IN the target scope, baseline 0, lower-is-better),\n typecheck-outside (error count OUTSIDE the target scope, baseline measured at seed, a\n ceiling \u2014 required whenever a typecheck stage is scoped to a subset of the tree), tests-kept\n (a floor on tests that actually EXECUTED, baseline at seed, higher-is-better; a skipped or\n deselected test did not run and must not count), and suite-green (the suite exits clean AND\n reports zero failing tests \u2014 two assertions) \u2014 are composed ONLY for the checks a confirmed\n plan asked for (or, absent a confirm turn, the goal and interview answers). The GUARDS \u2014\n changed-from-seed and no-suppressions \u2014 are composed always, first-red-wins:\n changed-from-seed before every WORK stage, no-suppressions last.\n4. One population per stage \u2014 two structurally different counts never share a stage.\n5. The checker must judge the PATIENT's own tree: if imports could resolve to an installed or\n editable copy elsewhere, the environment is set so they resolve inside the patient.\n6. A number the tool did not report is unknown, never zero.";
|
|
457
478
|
export const GENRE_LANGUAGES: readonly string[];
|
|
479
|
+
/** The worker-facing line that rides with the js `any` guard (run mv13ery3). */
|
|
480
|
+
export const ANY_SUPPRESSION_WARNING: string;
|
|
458
481
|
/**
|
|
459
482
|
* THE GENRE, as data. The suppression batteries are the hand-written closes'
|
|
460
483
|
* own `SUPPRESSIONS` tables (`scripts/u-pulselog-close.mjs:35`,
|
package/types/authorjob.d.ts
CHANGED
|
@@ -185,6 +185,14 @@ export function assembleSpec(specDraft: any, { closeDecl, verdictType }: {
|
|
|
185
185
|
closeDecl: any;
|
|
186
186
|
verdictType: string;
|
|
187
187
|
}): any;
|
|
188
|
+
/**
|
|
189
|
+
* THE one spelling of "a passed calibration gate stands in for the seed red": the
|
|
190
|
+
* close carries a judged stage AND the gate ran and passed. A failed or missing
|
|
191
|
+
* calibration never satisfies it, and a close with no judged stage never reaches it.
|
|
192
|
+
* @param {any[]} judged `judgedStages(closeDecl)` @param {any} calibration `gates.calibration`
|
|
193
|
+
* @returns {boolean}
|
|
194
|
+
*/
|
|
195
|
+
export function seedProofIsCalibration(judged: any[], calibration: any): boolean;
|
|
188
196
|
/**
|
|
189
197
|
* D9, in code: *nothing JUDGES the close; three mechanical gates plus a
|
|
190
198
|
* signature.* No LLM validates another LLM's close, here or anywhere.
|
|
@@ -206,16 +214,15 @@ export function assembleSpec(specDraft: any, { closeDecl, verdictType }: {
|
|
|
206
214
|
* has already had its chance, and nobody pays to discover a broken
|
|
207
215
|
* declaration.
|
|
208
216
|
*
|
|
209
|
-
* D9.3 AND THE JUDGED
|
|
210
|
-
*
|
|
211
|
-
*
|
|
212
|
-
*
|
|
213
|
-
*
|
|
214
|
-
*
|
|
215
|
-
*
|
|
216
|
-
*
|
|
217
|
-
*
|
|
218
|
-
* its calibration says.
|
|
217
|
+
* D9.3 AND THE JUDGED CLOSE (2026-08-18 second addendum, ruling 3; hamr: *"fix it now,
|
|
218
|
+
* we are delivering softgreen, isn't that the whole point?"*; widened 2026-10-09 to mixed
|
|
219
|
+
* closes, hamr: *"a rubric job should check if judge can judge (calibration) and judge
|
|
220
|
+
* rules can apply properly to pass ... we can't close everything upfront"*). Gate 3
|
|
221
|
+
* protects against a close that cannot fail. A judged stage SKIPS the seed read by ruling
|
|
222
|
+
* 8, so it can never show red there. For ANY close carrying a judged stage (pure or
|
|
223
|
+
* mixed with mechanical stages), a PASSED calibration gate plays the seed-red role
|
|
224
|
+
* (`seedProofIsCalibration`). A close with NO judged stage is UNCHANGED: some work stage
|
|
225
|
+
* must be red at the seed. A failed or missing calibration never satisfies it.
|
|
219
226
|
*
|
|
220
227
|
* Gates 2 and 3 share ONE execution, and that is stated rather than hidden: at
|
|
221
228
|
* job creation the tree IS the seed, so "does every stage run against the real
|
package/types/authorreadout.d.ts
CHANGED
|
@@ -87,6 +87,20 @@ export function answeredQuestionLines(confirmed: {
|
|
|
87
87
|
* @returns {string[]}
|
|
88
88
|
*/
|
|
89
89
|
export function calibrationLines(calibration: any): string[];
|
|
90
|
+
/**
|
|
91
|
+
* The calibration gate's real result as one line a person reads next to the step (hamr, 2026-10-09: the panel
|
|
92
|
+
* showed `calibrating ✓` over a gate that graded 8 of 10 and let 2 attack styles leak). PASS IS THE GATE'S OWN:
|
|
93
|
+
* `ok` is `calibration.ok` as `runCalibration` decided it (every case right as a SET of reds, every style
|
|
94
|
+
* resisted), never recomputed here, so there is no second spelling of the floor. The counts are read from the
|
|
95
|
+
* record (`graded[].ok` is `compareExpectation`, which compares reds as sets); the denominators are the live
|
|
96
|
+
* constants (CALIBRATION_SIZE, the battery length), so a gate that stopped part-way reads "x of 10", not "x of x".
|
|
97
|
+
* @param {any} calibration `signing.gates.calibration`
|
|
98
|
+
* @returns {{ok: boolean, note: string}|null} null when the gate was not reached
|
|
99
|
+
*/
|
|
100
|
+
export function calibrationSummary(calibration: any): {
|
|
101
|
+
ok: boolean;
|
|
102
|
+
note: string;
|
|
103
|
+
} | null;
|
|
90
104
|
/**
|
|
91
105
|
* THE AUTHORING CEILING, parsed from `--budget`. Housed here for the same reason
|
|
92
106
|
* the readout above is: the runner is a script, and a rule no test can reach is a
|
|
@@ -169,4 +183,96 @@ export function crashRecord(err: unknown): {
|
|
|
169
183
|
code: string | null;
|
|
170
184
|
raw: ReturnType<typeof scrubRaw>;
|
|
171
185
|
};
|
|
186
|
+
/**
|
|
187
|
+
* Every plain kind the table can emit — the list the test enumerates against.
|
|
188
|
+
* @returns {string[]}
|
|
189
|
+
*/
|
|
190
|
+
export function plainKinds(): string[];
|
|
191
|
+
/**
|
|
192
|
+
* Map the technical reds of a refused proposal to plain sentences, one per red.
|
|
193
|
+
* An unknown code is NEVER dropped: it becomes a generic sentence that still
|
|
194
|
+
* quotes the code and path, under the kind `unmapped`.
|
|
195
|
+
* @param {PlainRed[]} reds
|
|
196
|
+
* @param {{cases?: any}} [o] the proposed cases, to name a case by its own id
|
|
197
|
+
* @returns {PlainReason[]}
|
|
198
|
+
*/
|
|
199
|
+
export function plainReasons(reds: PlainRed[], { cases }?: {
|
|
200
|
+
cases?: any;
|
|
201
|
+
}): PlainReason[];
|
|
202
|
+
/**
|
|
203
|
+
* THE MESSAGE a person reads when the rubric proposal was refused. Both doors
|
|
204
|
+
* (the panel's failed step and the CLI) print exactly this.
|
|
205
|
+
* @param {{stop: string, reds: PlainRed[], cases?: any, spend?: {knownUsd?: number|null, spendComplete?: boolean|null}|null,
|
|
206
|
+
* source?: 'proposal'|'signer'}} o
|
|
207
|
+
* @returns {string}
|
|
208
|
+
*/
|
|
209
|
+
export function proposalStopText({ stop, reds, cases, spend, source }: {
|
|
210
|
+
stop: string;
|
|
211
|
+
reds: PlainRed[];
|
|
212
|
+
cases?: any;
|
|
213
|
+
spend?: {
|
|
214
|
+
knownUsd?: number | null;
|
|
215
|
+
spendComplete?: boolean | null;
|
|
216
|
+
} | null;
|
|
217
|
+
source?: "proposal" | "signer";
|
|
218
|
+
}): string;
|
|
219
|
+
/**
|
|
220
|
+
* Plain sentences for the reds of a refused signing, one or more per red. Card and practice-case reds reuse the
|
|
221
|
+
* proposal table; an unknown code is a generic sentence that quotes the code.
|
|
222
|
+
* @param {PlainRed[]} reds @param {{cases?: any}} [o]
|
|
223
|
+
* @returns {{kind: string, sentence: string, todo: string, red: PlainRed}[]}
|
|
224
|
+
*/
|
|
225
|
+
export function signingReasons(reds: PlainRed[], { cases }?: {
|
|
226
|
+
cases?: any;
|
|
227
|
+
}): {
|
|
228
|
+
kind: string;
|
|
229
|
+
sentence: string;
|
|
230
|
+
todo: string;
|
|
231
|
+
red: PlainRed;
|
|
232
|
+
}[];
|
|
233
|
+
/** every plain kind `signingReasons` can emit besides the proposal table's — what the test enumerates
|
|
234
|
+
* @returns {string[]} */
|
|
235
|
+
export function signingKinds(): string[];
|
|
236
|
+
/**
|
|
237
|
+
* THE MESSAGE a person reads when the signing gates refuse. The panel's failed step and the CLI print exactly this.
|
|
238
|
+
* @param {{reds: PlainRed[], refusal?: {detail?: string}|null, cases?: any,
|
|
239
|
+
* spend?: {knownUsd?: number|null, spendComplete?: boolean|null}|null}} o
|
|
240
|
+
* @returns {string}
|
|
241
|
+
*/
|
|
242
|
+
export function signingStopText({ reds, refusal, cases, spend }: {
|
|
243
|
+
reds: PlainRed[];
|
|
244
|
+
refusal?: {
|
|
245
|
+
detail?: string;
|
|
246
|
+
} | null;
|
|
247
|
+
cases?: any;
|
|
248
|
+
spend?: {
|
|
249
|
+
knownUsd?: number | null;
|
|
250
|
+
spendComplete?: boolean | null;
|
|
251
|
+
} | null;
|
|
252
|
+
}): string;
|
|
253
|
+
/**
|
|
254
|
+
* The reds as the drafting log keeps them: every structured field, the plain
|
|
255
|
+
* sentence beside it, every string secret-scrubbed and bounded, the list capped.
|
|
256
|
+
* @param {PlainRed[]} reds @param {{cases?: any, signing?: boolean}} [o] `signing`: the reds are the signing gates', worded by signingReasons
|
|
257
|
+
* @returns {Record<string, any>[]}
|
|
258
|
+
*/
|
|
259
|
+
export function redsRecord(reds: PlainRed[], { cases, signing }?: {
|
|
260
|
+
cases?: any;
|
|
261
|
+
signing?: boolean;
|
|
262
|
+
}): Record<string, any>[];
|
|
263
|
+
/** the stops this module explains in plain words */
|
|
264
|
+
export const PLAIN_PROPOSAL_STOPS: readonly string[];
|
|
265
|
+
/** codes the declaration gates (validateJob, validateCloseDecl) raise: one plain sentence covers the family */
|
|
266
|
+
export const DECLARATION_CODES: readonly string[];
|
|
267
|
+
export type PlainRed = {
|
|
268
|
+
code: string;
|
|
269
|
+
path: string;
|
|
270
|
+
detail: string;
|
|
271
|
+
[k: string]: any;
|
|
272
|
+
};
|
|
273
|
+
export type PlainReason = {
|
|
274
|
+
kind: string;
|
|
275
|
+
sentence: string;
|
|
276
|
+
red: PlainRed;
|
|
277
|
+
};
|
|
172
278
|
import { scrubRaw } from './text.js';
|
package/types/calibrate.d.ts
CHANGED
|
@@ -90,7 +90,7 @@ export function factsResist(facts: any, gold: any, artifactText: string): {
|
|
|
90
90
|
* onCost?: (c: any) => void, attempts?: number, battery?: any[], batteryCard?: any,
|
|
91
91
|
* capStop?: () => 'cap-halt'|'pricing-red'|null}} o
|
|
92
92
|
* @returns {Promise<{ok: boolean, stop: string|null, casualty: any,
|
|
93
|
-
* graded: {id: string, ok: boolean, got: any, want: any, detail: string|null}[],
|
|
93
|
+
* graded: {id: string, ok: boolean, got: any, want: any, detail: string|null, diag: any}[],
|
|
94
94
|
* injection: {styles: any[], allResisted: boolean, leaks: string[]},
|
|
95
95
|
* failures: string[], reds: Red[], judgeModel: string,
|
|
96
96
|
* cardHash: string|null, casesHash: string|null, setHash: string|null,
|
|
@@ -116,6 +116,7 @@ export function runCalibration({ cases, card, judgeLoop, judgeModel, onCost, att
|
|
|
116
116
|
got: any;
|
|
117
117
|
want: any;
|
|
118
118
|
detail: string | null;
|
|
119
|
+
diag: any;
|
|
119
120
|
}[];
|
|
120
121
|
injection: {
|
|
121
122
|
styles: any[];
|
|
@@ -190,6 +191,8 @@ ${string}`;
|
|
|
190
191
|
}>[];
|
|
191
192
|
}>;
|
|
192
193
|
}>[];
|
|
194
|
+
/** the per-string cap on anything the diagnostic keeps (bytes, scrubbed) */
|
|
195
|
+
export const DIAG_STRING_CAP: 400;
|
|
193
196
|
export type Red = {
|
|
194
197
|
code: string;
|
|
195
198
|
path: string;
|
package/types/declaredclose.d.ts
CHANGED
|
@@ -53,7 +53,7 @@ export function declaredStages(closeDecl: any): any[] | null;
|
|
|
53
53
|
*
|
|
54
54
|
* @param {any} closeDecl
|
|
55
55
|
* @param {{at?: string, listing?: string[]|null, deferListing?: boolean,
|
|
56
|
-
* catalogue?: Record<string, any>, verdictType?: string|null}} [opts]
|
|
56
|
+
* catalogue?: Record<string, any>, verdictType?: string|null, writeScope?: string[]|null}} [opts]
|
|
57
57
|
* @returns {{ok: boolean, reds: Red[], closeDecl: any, grounded: boolean,
|
|
58
58
|
* scoped: {scoped: boolean, via: string|null},
|
|
59
59
|
* ceiling: {class: string, kind: string, stage: string|null, locked: boolean}|null}}
|
|
@@ -64,6 +64,7 @@ export function validateCloseDecl(closeDecl: any, opts?: {
|
|
|
64
64
|
deferListing?: boolean;
|
|
65
65
|
catalogue?: Record<string, any>;
|
|
66
66
|
verdictType?: string | null;
|
|
67
|
+
writeScope?: string[] | null;
|
|
67
68
|
}): {
|
|
68
69
|
ok: boolean;
|
|
69
70
|
reds: Red[];
|
package/types/judged.d.ts
CHANGED
|
@@ -97,6 +97,15 @@ export function resolveJobJudge(source: {
|
|
|
97
97
|
provider: string;
|
|
98
98
|
model: string;
|
|
99
99
|
};
|
|
100
|
+
/**
|
|
101
|
+
* THE ONE ACCEPTANCE for "this quote points at the JSDoc block of this declaration":
|
|
102
|
+
* every non-empty line of `docQuote` is a line inside the block directly above the
|
|
103
|
+
* declaration. `has-doc`'s check and the calibration gate's injection facts check
|
|
104
|
+
* both call this, so the two can never disagree about what a good pointer is (F192).
|
|
105
|
+
* @param {string} text the artifact @param {string|null} declQuote @param {string} docQuote
|
|
106
|
+
* @returns {boolean}
|
|
107
|
+
*/
|
|
108
|
+
export function docQuoteInBlock(text: string, declQuote: string | null, docQuote: string): boolean;
|
|
100
109
|
/**
|
|
101
110
|
* The rubric card: Q6's answer compiled into enumerated items, signed by the
|
|
102
111
|
* person and enumerated in the spec hash. `rule` selects from `JUDGE_RULES`;
|
|
@@ -359,15 +368,25 @@ export const LOCATE_AXES: Readonly<{
|
|
|
359
368
|
PROVIDER: "provider-red";
|
|
360
369
|
ARTIFACT: "artifact-red";
|
|
361
370
|
}>;
|
|
371
|
+
/**
|
|
372
|
+
* Function words ONLY: articles, prepositions, conjunctions. No judgement words
|
|
373
|
+
* ("returns", "gets", "function"): a description that adds one of those has added
|
|
374
|
+
* something, and deciding whether it is MEANINGFUL is exactly what this rule
|
|
375
|
+
* refuses to do.
|
|
376
|
+
*/
|
|
377
|
+
export const SAYS_WHAT_STOPWORDS: readonly string[];
|
|
362
378
|
/**
|
|
363
379
|
* THE OWNED RULE TABLE. Frozen: the rulebook is the arbiter's, and a caller that
|
|
364
380
|
* could add a rule at runtime would be authoring the arbiter.
|
|
365
|
-
*
|
|
381
|
+
* `means` is the rule's plain-words reach: what it CAN check, and what it cannot. The case-proposal
|
|
382
|
+
* prompt renders it, so a proposed case never expects a red the rule cannot raise.
|
|
383
|
+
* @type {Readonly<Record<string, {id: string, means: string, ask: string, check: (fn: any, text: string|null) => RuleRed[]}>>}
|
|
366
384
|
*/
|
|
367
385
|
export const JUDGE_RULES: Readonly<Record<string, {
|
|
368
386
|
id: string;
|
|
387
|
+
means: string;
|
|
369
388
|
ask: string;
|
|
370
|
-
check: (fn: any,
|
|
389
|
+
check: (fn: any, text: string | null) => RuleRed[];
|
|
371
390
|
}>>;
|
|
372
391
|
/** the enumerated set a card may name — handed over verbatim in every refusal */
|
|
373
392
|
export const JUDGE_RULE_IDS: readonly string[];
|