clearotron 0.4.0-beta.2 → 0.4.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/THIRD-PARTY-NOTICES.md +5 -5
- package/build-info.json +2 -2
- package/driver/CHANGELOG.md +90 -0
- package/driver/ask-ledger.mjs +2 -2
- package/driver/coverage-form.mjs +6 -0
- package/driver/coverage-ledger.mjs +14 -2
- package/driver/driver.config.mjs +24 -7
- package/driver/engine/mcp/band-server.mjs +3 -3
- package/driver/engine/mcp/stdio-server.mjs +12 -1
- package/driver/gateway.mjs +27 -3
- package/driver/package.json +1 -1
- package/driver/pipeline-knockout.mjs +41 -10
- package/driver/pipeline.mjs +104 -12
- package/driver/progress.mjs +6 -1
- package/driver/provider-usage.mjs +16 -0
- package/driver/publish/index.mjs +11 -5
- package/driver/publish/knockout.mjs +16 -3
- package/driver/publish/render-knockout.mjs +38 -11
- package/driver/record-carry.mjs +17 -1
- package/driver/reference-strip-signatures.mjs +11 -1
- package/driver/register-plan.mjs +7 -3
- package/driver/register-served.mjs +91 -0
- package/driver/remedy-accounting.mjs +38 -9
- package/driver/reviewer-open-points.mjs +20 -23
- package/driver/score-redaction.mjs +416 -20
- package/driver/screen-gate.mjs +3 -3
- package/driver/skills/clearance-register/SKILL.md +0 -8
- package/driver/skills/clearance-register/digest.md +1 -1
- package/driver/skills/clearance-register/providers/corsearch.md +1 -1
- package/driver/skills/clearance-register/register-recipes.md +3 -9
- package/driver/skills/clearance-search/SKILL.md +2 -2
- package/driver/skills/clearance-search/phase2-execution.md +1 -1
- package/driver/skills/knockout-assess/SKILL.md +2 -0
- package/driver/stages-knockout.mjs +22 -0
- package/driver/suite-census.json +152 -26
- package/driver/unit-inventory.mjs +38 -43
- package/mcp-server/CHANGELOG.md +8 -0
- package/mcp-server/package.json +1 -1
- package/node_modules/brace-expansion/index.js +78 -22
- package/node_modules/brace-expansion/package.json +1 -1
- package/node_modules/readdir-glob/node_modules/brace-expansion/index.js +78 -22
- package/node_modules/readdir-glob/node_modules/brace-expansion/package.json +1 -1
- package/package.json +1 -1
- package/portal-ui/package.json +2 -2
- package/providers/_shared/term-shape.mjs +1 -1
- package/providers/jx/src/core.js +0 -1
- package/providers/jx/src/judge.js +0 -1
- package/providers/jx/src/nativeread.js +0 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/signa/src/core.js +125 -17
- package/scripts/e2e-first-time.mjs +12 -4
- package/scripts/e2e-scenario-ops.mjs +25 -2
- package/scripts/e2e.mjs +246 -24
- package/scripts/mint-reference-strip-backlog.mjs +36 -2
- package/scripts/score.mjs +102 -29
- package/shared/identifier-scan.mjs +31 -1
package/scripts/e2e.mjs
CHANGED
|
@@ -57,6 +57,7 @@ import { isLiveQueueMarker, isQueueSidecar, liveQueueState, LIVE_QUEUE_STATES, T
|
|
|
57
57
|
// leave the real one to park tomorrow's re-run as a duplicate. usage-ledger.mjs is a pure leaf too
|
|
58
58
|
// (node:fs + node:path + queue-markers.mjs), so it drags no driver machinery in either.
|
|
59
59
|
import { matterLedgerPath } from "../driver/usage-ledger.mjs";
|
|
60
|
+
import { registerServedLine } from "../driver/register-served.mjs"; // which register a run recorded as having served it
|
|
60
61
|
import { probeWorker } from "../driver/queue-watch-probe.mjs"; // the drain this deployment actually has
|
|
61
62
|
// Teardown asks whether a process is actually producing a run before it rewrites the record that says so.
|
|
62
63
|
import { claimLivenessForCodename, claimForbidsDestruction } from "../driver/claim-liveness.mjs";
|
|
@@ -336,6 +337,75 @@ const allScenarios = () => {
|
|
|
336
337
|
.sort((a, b) => byScenarioNumber(a.id, b.id));
|
|
337
338
|
};
|
|
338
339
|
|
|
340
|
+
// ── A SCENARIO MAY NAME THE REGISTER ITS NUMBERS WERE MEASURED AGAINST ──────────────────────────────
|
|
341
|
+
//
|
|
342
|
+
// THE DEFECT THIS CLOSES. A scenario's register floors are counts, and a count is an answer one register
|
|
343
|
+
// gave. Run the same scenario against a different register and the floor decides nothing about the
|
|
344
|
+
// engine: measured 2026-09-30 on a knockout whose floors are `identical: 8` and `containing: 300`, the
|
|
345
|
+
// other register answered 2 and 471 for the SAME mark. One floor failed and one passed, in one run, and
|
|
346
|
+
// neither outcome was about the engine. A red like that costs a round: it reads as a regression, it is
|
|
347
|
+
// investigated as one, and the run that produced it is already spent.
|
|
348
|
+
//
|
|
349
|
+
// WHAT THIS DOES NOT DO, deliberately. It does not encode which register returns more. The measurement
|
|
350
|
+
// says the two DISAGREE — 2 against 8 on one predicate and 471 against 300 on another, in opposite
|
|
351
|
+
// directions — and nothing here has established why. A floor per register would need that why, and a
|
|
352
|
+
// harness rule must not smuggle one in. So a scenario states which register its numbers came from, and
|
|
353
|
+
// the harness refuses to spend the run anywhere else.
|
|
354
|
+
//
|
|
355
|
+
// OPTIONAL BY CONSTRUCTION, because the store is a DIFFERENT REPO. A scenario that declares nothing
|
|
356
|
+
// behaves exactly as it does today. There is no ordering problem to manage and no day on which this
|
|
357
|
+
// harness refuses a store that has not caught up.
|
|
358
|
+
//
|
|
359
|
+
// WHY THIS DOES NOT ASK THE DRIVER. `driver.config.mjs` resolves the provider, and this file may not
|
|
360
|
+
// import it — its unset-env defaults are PRODUCTION, which is the rule stated at four points above. The
|
|
361
|
+
// variable is read here directly and with no fallback: unset stays unset, and unset is reported as its
|
|
362
|
+
// own state rather than resolved to anything.
|
|
363
|
+
//
|
|
364
|
+
// CONFIGURED, NOT SERVED, and the difference is a live gap. What a run was configured for is the
|
|
365
|
+
// launch value; what actually served it is a separate record the runs do not yet carry (measured
|
|
366
|
+
// 2026-09-30: absent on all four runs of a round, on both the knockout and the clearance path). When
|
|
367
|
+
// that record exists, an assertion could read what served. Until then this is a PRE-RUN refusal on the
|
|
368
|
+
// configured value, which is the honest instrument available — and it is placed before the spend, where
|
|
369
|
+
// being approximately right still saves the money.
|
|
370
|
+
export const REGISTER_ENV = "CLEAROTRON_DATABASE";
|
|
371
|
+
|
|
372
|
+
/** The registers a scenario declares its numbers were measured against, lowercased. `[]` when it declares none. PURE. */
|
|
373
|
+
export function registersDeclaredBy(sc) {
|
|
374
|
+
const v = sc?.register;
|
|
375
|
+
const list = Array.isArray(v) ? v : v == null ? [] : [v];
|
|
376
|
+
return list.map((x) => String(x ?? "").trim().toLowerCase()).filter(Boolean);
|
|
377
|
+
}
|
|
378
|
+
|
|
379
|
+
/** The register this process is configured for, or `null` when the variable names none. No default, ever. PURE apart from the read. */
|
|
380
|
+
export function configuredRegister(env = process.env) {
|
|
381
|
+
return String(env?.[REGISTER_ENV] ?? "").trim().toLowerCase() || null;
|
|
382
|
+
}
|
|
383
|
+
|
|
384
|
+
/**
|
|
385
|
+
* The refusal text for running `sc` here, or `null` when there is nothing to refuse. PURE.
|
|
386
|
+
*
|
|
387
|
+
* Three cases, and the middle one is the reason this returns text rather than a boolean: a scenario that
|
|
388
|
+
* declares a register and an environment that names none is NOT a match and NOT a mismatch. Running it
|
|
389
|
+
* would spend against whatever the driver defaults to, which this file is not allowed to ask about, so
|
|
390
|
+
* the refusal says exactly that rather than guessing either way.
|
|
391
|
+
*/
|
|
392
|
+
export function registerRefusal(sc, env = process.env) {
|
|
393
|
+
const want = registersDeclaredBy(sc);
|
|
394
|
+
if (!want.length) return null;
|
|
395
|
+
const have = configuredRegister(env);
|
|
396
|
+
const declares = want.length === 1 ? want[0] : `one of ${want.join(", ")}`;
|
|
397
|
+
if (!have) {
|
|
398
|
+
return `${sc.id} states that its numbers were measured against ${declares}, and ${REGISTER_ENV} names no register `
|
|
399
|
+
+ `on this instance. This harness does not resolve a default — the module that would is the one it may not import. `
|
|
400
|
+
+ `Set ${REGISTER_ENV} to ${declares} and run again.`;
|
|
401
|
+
}
|
|
402
|
+
if (want.includes(have)) return null;
|
|
403
|
+
return `${sc.id} states that its numbers were measured against ${declares}, and this instance is configured for ${have}. `
|
|
404
|
+
+ `What one register answered is not what another answers, so here this scenario would judge the register `
|
|
405
|
+
+ `and not the engine — and a red read as a regression arrives on a run that is already spent. `
|
|
406
|
+
+ `Set ${REGISTER_ENV} to ${declares}, or run a scenario whose numbers were measured against ${have}.`;
|
|
407
|
+
}
|
|
408
|
+
|
|
339
409
|
// ── — the store is the input to the most expensive thing this repo does ─────────────────────────
|
|
340
410
|
//
|
|
341
411
|
// Every job block in the store, through BOTH admission gates, against the outcome the scenario declares.
|
|
@@ -439,6 +509,20 @@ export function lintScenarios(scenarios) {
|
|
|
439
509
|
for (const f of sc.expect?.artifacts ?? []) {
|
|
440
510
|
if (/^\/|\.\./.test(f)) wrong.push(`${label}: artifact ${JSON.stringify(f)} must be a plain run-relative name`);
|
|
441
511
|
}
|
|
512
|
+
// ── `register`, IF STATED, MUST BE STATABLE ────────────────────────────────────────────────────
|
|
513
|
+
//
|
|
514
|
+
// SHAPE ONLY, AND NOT THE NAME. Checking the value against the known providers would mean importing
|
|
515
|
+
// `driver.config.mjs`, which this file may not do. That is not a hole worth patching another way: a
|
|
516
|
+
// misspelled register never runs anywhere, and the refusal it produces prints the scenario's word
|
|
517
|
+
// and the instance's word side by side, which is a typo shown rather than described. What IS worth
|
|
518
|
+
// refusing is a value nothing can compare at all — an empty string, a number, an empty list — since
|
|
519
|
+
// that would declare a register and then silently compare against nothing.
|
|
520
|
+
if (Object.prototype.hasOwnProperty.call(sc, "register")) {
|
|
521
|
+
const raw = Array.isArray(sc.register) ? sc.register : [sc.register];
|
|
522
|
+
const bad = raw.length === 0 || raw.some((x) => typeof x !== "string" || !x.trim());
|
|
523
|
+
if (bad) wrong.push(`${label}: \`register\` is ${JSON.stringify(sc.register)} — it must be a non-empty register name, or a non-empty list of them. `
|
|
524
|
+
+ `A scenario states this when its numbers are counts one register returned, so that a run against another is refused before it spends.`);
|
|
525
|
+
}
|
|
442
526
|
const allAsserts = [...(sc.expect?.assert ?? []), ...(sc.cases ?? []).flatMap((c) => c.expect?.assert ?? [])];
|
|
443
527
|
for (const a of allAsserts) {
|
|
444
528
|
if (/^_driver\/scope-ledger\.json/.test(String(a.path ?? ""))) {
|
|
@@ -1213,6 +1297,11 @@ export function outboxPackets({ runId = null, runIds = [], queueBase = null } =
|
|
|
1213
1297
|
return { packets: [...hit].sort(), unreadable: null };
|
|
1214
1298
|
}
|
|
1215
1299
|
|
|
1300
|
+
// WITHHELD UNLESS ASKED, and the default is off so nothing has to remember to turn it on. Set once from
|
|
1301
|
+
// argv; `evalAssertion` takes it as an option so a test can drive both paths without touching argv.
|
|
1302
|
+
export let REPORT_NAMES = false;
|
|
1303
|
+
export const setReportNames = (v) => { REPORT_NAMES = Boolean(v); };
|
|
1304
|
+
|
|
1216
1305
|
// ── assertions ───────────────────────────────────────────────────────────────────────────────────────
|
|
1217
1306
|
// Every op is implemented or explicitly reported UNIMPLEMENTED. An op that silently passes because
|
|
1218
1307
|
// nobody wrote it is the exact failure mode this whole suite exists to prevent.
|
|
@@ -1339,7 +1428,70 @@ function bandShapeUnder(full, file, field) {
|
|
|
1339
1428
|
return { doc, unread, shallow, verdict };
|
|
1340
1429
|
}
|
|
1341
1430
|
|
|
1342
|
-
|
|
1431
|
+
/**
|
|
1432
|
+
* A quotation from a RUN, or where to find it.
|
|
1433
|
+
*
|
|
1434
|
+
* WHY THIS EXISTS. `report` is the cheap instrument — free, re-runnable, and the doctrine tells a lane to
|
|
1435
|
+
* re-run it rather than re-run the clearance — so it is the one most often pointed at a real matter, in a
|
|
1436
|
+
* session that records whatever it prints. Its INVESTIGATE rows earned their usefulness by showing the
|
|
1437
|
+
* sentence they object to, and that sentence is the engine's own words about a client's matter.
|
|
1438
|
+
*
|
|
1439
|
+
* A LOCATION IS AS ACTIONABLE AS A QUOTE AND CARRIES NOTHING. "sentence 14 of knockout-findings.md" sends
|
|
1440
|
+
* a reader to the same words, in the file that already holds them, without copying them anywhere else.
|
|
1441
|
+
* The one job that needs them on the page — reading a surprising row against the text — asks with
|
|
1442
|
+
* `--names`, the same flag the scorer takes for the same reason.
|
|
1443
|
+
*
|
|
1444
|
+
* The count is never withheld. How many claims exceeded their evidence is the finding; which words they
|
|
1445
|
+
* used is the detail.
|
|
1446
|
+
*/
|
|
1447
|
+
/**
|
|
1448
|
+
* One door's refusal line.
|
|
1449
|
+
*
|
|
1450
|
+
* A DOOR'S REFUSAL TEXT IS THE DOOR'S, NOT OURS, and that is the reason to gate it rather than a reason
|
|
1451
|
+
* not to. Measured across every receipt on this box — 397 door answers, 213 refusals, 113 distinct
|
|
1452
|
+
* reasons — not one echoes the order: they carry an HTTP status and the server's own error envelope, or
|
|
1453
|
+
* a socket error. So this guards a case that has not happened, and it is worth one line, because the
|
|
1454
|
+
* half of these that CAN echo is structural: an `initialize` refusal is sent before any order exists and
|
|
1455
|
+
* cannot carry one, a `tools/call` refusal is sent after and can. Which it does is the door's own
|
|
1456
|
+
* error-message discipline, which nothing here governs and no clipping saves — an echoing server puts
|
|
1457
|
+
* the mark near the front, well inside the 160 characters this used to print.
|
|
1458
|
+
*
|
|
1459
|
+
* A MISSING REASON IS NOT A WITHHELD ONE. "(no reason recorded)" is a fact about the receipt and says so
|
|
1460
|
+
* plainly; offering to print it with `--names` would promise words that do not exist.
|
|
1461
|
+
*/
|
|
1462
|
+
export function doorReasonLine(r, names = REPORT_NAMES) {
|
|
1463
|
+
if (r?.reason == null || String(r.reason).trim() === "") return "(no reason recorded)";
|
|
1464
|
+
return fromTheRun({ names, quotes: [String(r.reason)],
|
|
1465
|
+
where: "the door's own words are in the round receipt, at rounds[].cases[].answers[].reason" });
|
|
1466
|
+
}
|
|
1467
|
+
|
|
1468
|
+
function fromTheRun({ names, quotes, where }) {
|
|
1469
|
+
// THE LOCATION IS KEPT WHEN THE WORDS ARE SHOWN. It costs one clause beside the quotation it stands in
|
|
1470
|
+
// for, and a reader chasing a surprising row wants to go and read around it rather than only at it.
|
|
1471
|
+
if (names) return `${quotes.map((q) => `"${String(q).slice(0, 160)}"`).join(" · ")} (${where})`;
|
|
1472
|
+
return `${where} — run again with --names to read them here`;
|
|
1473
|
+
}
|
|
1474
|
+
|
|
1475
|
+
/**
|
|
1476
|
+
* The row that fires when a listing does not hold the mark an assertion asked for.
|
|
1477
|
+
*
|
|
1478
|
+
* It is the WIDEST row in either floor op: it printed the name asked for and every name the listing did
|
|
1479
|
+
* hold, in clear, with no flag involved, and it fires precisely when somebody is investigating — a mark
|
|
1480
|
+
* is absent, so a reader is reading the report. It exists twice, once per floor op, with one verb each.
|
|
1481
|
+
*
|
|
1482
|
+
* The COUNT is never withheld. A listing that held nothing is a different defect from one that held four
|
|
1483
|
+
* other marks, and that distinction is the finding.
|
|
1484
|
+
*/
|
|
1485
|
+
function noSuchMark({ names, field, marks, file, verb }) {
|
|
1486
|
+
const listed = (marks ?? []).map((x) => x?.name).filter((x) => typeof x === "string");
|
|
1487
|
+
return { ok: false, saw: `the asked-for mark is not in ${file}, which ${verb} ${listed.length} mark(s)`
|
|
1488
|
+
+ (listed.length
|
|
1489
|
+
? ` — ${fromTheRun({ names, quotes: [field, ...listed],
|
|
1490
|
+
where: `the name asked for is the field of this assertion's path, and the names ${verb} are the "marks" array of ${file}` })}`
|
|
1491
|
+
: "") };
|
|
1492
|
+
}
|
|
1493
|
+
|
|
1494
|
+
function evalAssertion(a, runDir, { names = REPORT_NAMES } = {}) {
|
|
1343
1495
|
const [file, field] = String(a.path ?? "").split(":");
|
|
1344
1496
|
const full = join(runDir, file || "");
|
|
1345
1497
|
|
|
@@ -1566,13 +1718,19 @@ function evalAssertion(a, runDir) {
|
|
|
1566
1718
|
const held = recordsFile
|
|
1567
1719
|
? (recordsFile.marks ?? []).reduce((n, m) => n + (m.records ?? []).length, 0)
|
|
1568
1720
|
: null;
|
|
1569
|
-
|
|
1721
|
+
// Carried WITH ITS POSITION, because the index is what a withheld row prints instead of the words.
|
|
1722
|
+
// `indexOf` on the sentence would be wrong the moment a document repeats one: both rows would send
|
|
1723
|
+
// the reader to the first copy, and a run that says the same thing twice is exactly the run somebody
|
|
1724
|
+
// is reading the report about.
|
|
1725
|
+
const badAt = sentences.map((s, i) => ({ s, i })).filter(({ s }) => aboutRegister.test(s) && !staysInside.test(s)
|
|
1570
1726
|
&& (exceedsACount.test(s) || (singularAbsence.test(s) && !(held > 0))));
|
|
1727
|
+
const bad = badAt.map(({ s }) => s);
|
|
1571
1728
|
const basis = held === null
|
|
1572
1729
|
? "_driver/register-records.json is absent or unreadable, so this is as strict as a run holding zero records — identical verdict, different reason: one is a fact about the search, this is a fact about the check's own evidence"
|
|
1573
1730
|
: `the run holds ${held} register record(s), so a status read off one is supported and only a claim over the field is not`;
|
|
1574
1731
|
return { ok: bad.length === 0,
|
|
1575
|
-
saw: bad.length ? `${bad.length} register claim(s) wider than the records this run holds —
|
|
1732
|
+
saw: bad.length ? `${bad.length} register claim(s) wider than the records this run holds — `
|
|
1733
|
+
+ `${fromTheRun({ names, quotes: bad.slice(0, 2), where: `sentence ${badAt.slice(0, 2).map(({ i }) => i + 1).join(" and ")} of ${file}` })} (${basis})`
|
|
1576
1734
|
: `${sentences.filter((s) => aboutRegister.test(s)).length} register sentence(s), each a count, a labelled expectation, or a status the records support — ${basis}` };
|
|
1577
1735
|
}
|
|
1578
1736
|
// `survivor-not-clear` — a mark this lane did not knock out is a SURVIVOR, never a clear. The two words
|
|
@@ -1712,7 +1870,7 @@ function evalAssertion(a, runDir) {
|
|
|
1712
1870
|
const want = field ? String(field).trim().toLowerCase() : null;
|
|
1713
1871
|
if (!want) return { ok: false, saw: "no mark given (path must be <file>:<MARK NAME>)" };
|
|
1714
1872
|
const m = (doc.marks ?? []).find((x) => String(x?.name ?? "").trim().toLowerCase() === want);
|
|
1715
|
-
if (!m) return {
|
|
1873
|
+
if (!m) return noSuchMark({ names, field, marks: doc.marks, file, verb: "counted" });
|
|
1716
1874
|
const floors = a.value && typeof a.value === "object" ? a.value : null;
|
|
1717
1875
|
if (!floors) return { ok: false, saw: "value must be an object of predicate floors, e.g. {\"identical\": 45}" };
|
|
1718
1876
|
const saw = [], short = [], untaken = [];
|
|
@@ -1720,7 +1878,12 @@ function evalAssertion(a, runDir) {
|
|
|
1720
1878
|
const cell = m.counts?.[pred];
|
|
1721
1879
|
if (!cell) { untaken.push(`${pred}: no such predicate on this run's sidecar`); continue; }
|
|
1722
1880
|
if (!Number.isFinite(cell.total)) {
|
|
1723
|
-
|
|
1881
|
+
// THE SAME FIELD AND THE SAME PROVENANCE as the `unavailable` twenty lines below, which is
|
|
1882
|
+
// withheld — this one is the engine's own prose about why a count could not be taken, out of the
|
|
1883
|
+
// run's sidecar. Withheld with it, and located by the predicate it belongs to.
|
|
1884
|
+
untaken.push(`${pred}: NOT TAKEN — ${cell.unavailable === undefined || cell.unavailable === null
|
|
1885
|
+
? "no reason recorded"
|
|
1886
|
+
: fromTheRun({ names, quotes: [String(cell.unavailable)], where: `the reason is counts.${pred}.unavailable of ${file}` })}`);
|
|
1724
1887
|
continue;
|
|
1725
1888
|
}
|
|
1726
1889
|
saw.push(`${pred}=${cell.total} (floor ${floor})`);
|
|
@@ -1744,11 +1907,14 @@ function evalAssertion(a, runDir) {
|
|
|
1744
1907
|
if (!existsSync(full)) return { ok: false, saw: `${file} absent — the filings lane wrote nothing, not even its refusal` };
|
|
1745
1908
|
const doc = readJson(full);
|
|
1746
1909
|
if (!doc) return { ok: false, saw: `${file} present but unparseable` };
|
|
1747
|
-
if (doc.unavailable) return { ok: false, saw: `the filings were never listed —
|
|
1910
|
+
if (doc.unavailable) return { ok: false, saw: `the filings were never listed — `
|
|
1911
|
+
+ `${fromTheRun({ names, quotes: [String(doc.unavailable)], where: `the reason is the "unavailable" field of ${file}` })}. `
|
|
1912
|
+
+ `A listing that was refused is not a register that holds nothing.` };
|
|
1748
1913
|
const want = field ? String(field).trim().toLowerCase() : null;
|
|
1749
1914
|
if (!want) return { ok: false, saw: "no mark given (path must be <file>:<MARK NAME>)" };
|
|
1750
|
-
const
|
|
1751
|
-
|
|
1915
|
+
const markIndex = (doc.marks ?? []).findIndex((x) => String(x?.name ?? "").trim().toLowerCase() === want);
|
|
1916
|
+
const m = markIndex === -1 ? null : doc.marks[markIndex];
|
|
1917
|
+
if (!m) return noSuchMark({ names, field, marks: doc.marks, file, verb: "listed" });
|
|
1752
1918
|
const floors = a.value && typeof a.value === "object" ? a.value : {};
|
|
1753
1919
|
const minRecords = Number.isFinite(floors.records) ? floors.records : 1;
|
|
1754
1920
|
const minOffices = Number.isFinite(floors.offices) ? floors.offices : 1;
|
|
@@ -1756,8 +1922,15 @@ function evalAssertion(a, runDir) {
|
|
|
1756
1922
|
const offices = [...new Set(records.map((r) => String(r?.territory ?? "").trim().toLowerCase()).filter(Boolean))];
|
|
1757
1923
|
// A term the register refused is REDUCED COVERAGE, and it is named whichever way the floor goes: met,
|
|
1758
1924
|
// it qualifies what was proved; missed, it says the shortfall may be the fetch rather than the register.
|
|
1759
|
-
|
|
1760
|
-
|
|
1925
|
+
// A TERM IS A SPELLING OF A MARK and the reason is the engine's own prose, so both are withheld by
|
|
1926
|
+
// default; the INDEX still says which of the run's terms to go and read.
|
|
1927
|
+
const failedRows = (m.terms ?? []).map((t, i) => ({ t, i })).filter(({ t }) => t?.ok !== true);
|
|
1928
|
+
const failed = names
|
|
1929
|
+
? failedRows.map(({ t }) => `${t?.term ?? "?"}: ${String(t?.reason ?? "no reason recorded").slice(0, 120)}`)
|
|
1930
|
+
// NAMED BY POSITION IN THE FILE, both coordinates. `term 2 of knockout-filings.json` is ambiguous
|
|
1931
|
+
// the moment the listing holds more than one mark, and it always does — so the row would send a
|
|
1932
|
+
// reader to a term belonging to a different mark and read just as confidently.
|
|
1933
|
+
: failedRows.map(({ i }) => `term ${i + 1} of mark ${markIndex + 1} of ${file}`);
|
|
1761
1934
|
const met = records.length >= minRecords && offices.length >= minOffices;
|
|
1762
1935
|
const body = `${records.length} record(s) (floor ${minRecords}) across ${offices.length} office(s) [${offices.join(", ") || "none"}] (floor ${minOffices})`;
|
|
1763
1936
|
if (met) return { ok: true, saw: failed.length ? `${body} — floor met, but ${failed.length} term(s) were refused and this run covered less than it asked for: ${failed.join(" · ")}` : body };
|
|
@@ -2095,7 +2268,11 @@ export function provenanceLines(cost, today = new Date()) {
|
|
|
2095
2268
|
|
|
2096
2269
|
function cmdList() {
|
|
2097
2270
|
console.log("\nE2E scenarios — each is one complete clearance unless marked $0");
|
|
2098
|
-
console.log(`store: ${storeLine(STORE)}
|
|
2271
|
+
console.log(`store: ${storeLine(STORE)}`);
|
|
2272
|
+
// WHAT THIS INSTANCE IS CONFIGURED FOR, once at the top, because it decides which of the scenarios
|
|
2273
|
+
// below `run` will start. Printed even when nothing declares a register: a reader choosing a scenario
|
|
2274
|
+
// needs to know the variable names none BEFORE the refusal tells them.
|
|
2275
|
+
console.log(`register: ${configuredRegister() ?? `${REGISTER_ENV} names none`}\n`);
|
|
2099
2276
|
sweepStoreOrDie();
|
|
2100
2277
|
for (const s of allScenarios()) {
|
|
2101
2278
|
const cost = s.cost?.measured ? `~${s.cost.wallMinutes} min` : "UNMEASURED";
|
|
@@ -2110,6 +2287,16 @@ function cmdList() {
|
|
|
2110
2287
|
// all, and an absent benchmark is not a met one.
|
|
2111
2288
|
for (const l of turnaroundVerdict(bandForScenario(s)).lines) console.log(` ${l}`);
|
|
2112
2289
|
for (const l of provenanceLines(s.cost)) console.log(` ${l}`);
|
|
2290
|
+
// WHETHER `run` WOULD START THIS ONE HERE, at a glance, so the choice is made in the list rather
|
|
2291
|
+
// than discovered one scenario at a time. The refusal's own words, not a second wording of them.
|
|
2292
|
+
{
|
|
2293
|
+
const declared = registersDeclaredBy(s);
|
|
2294
|
+
if (declared.length) {
|
|
2295
|
+
const no = registerRefusal(s);
|
|
2296
|
+
console.log(` register: measured against ${declared.join(", ")} — ${no ? "WOULD REFUSE HERE" : "runnable here"}`);
|
|
2297
|
+
if (no) console.log(` ${no}`);
|
|
2298
|
+
}
|
|
2299
|
+
}
|
|
2113
2300
|
// — which scenarios prove the register HIT path, at a glance. Unconditional, and an
|
|
2114
2301
|
// unstated label prints as loudly as a stated one.
|
|
2115
2302
|
{
|
|
@@ -2173,6 +2360,13 @@ async function cmdRun(id) {
|
|
|
2173
2360
|
// Which store this came from, on the record before the run spends. The synthetic and the real scenario
|
|
2174
2361
|
// share an ID, so the ledger afterwards cannot tell you which one ran unless the run says so now.
|
|
2175
2362
|
console.log(`store: ${storeLine(STORE)}`);
|
|
2363
|
+
// BEFORE THE SPEND, because that is the only place this check is worth anything: a scenario whose
|
|
2364
|
+
// floors were measured against another register produces a red that reads as an engine regression,
|
|
2365
|
+
// and by then the run is paid for. `die` rather than a warning — a warning printed above a three-hour
|
|
2366
|
+
// run is a warning nobody reads until the verdict.
|
|
2367
|
+
{ const no = registerRefusal(s); if (no) die(`REFUSING: ${no}`); }
|
|
2368
|
+
console.log(`register: ${configuredRegister() ?? `${REGISTER_ENV} names none`}${
|
|
2369
|
+
registersDeclaredBy(s).length ? ` — the scenario states its numbers were measured against ${registersDeclaredBy(s).join(", ")}` : ""}`);
|
|
2176
2370
|
// Every scenario spends, R0 included — so every scenario refuses on stale code. R0 was exempted here
|
|
2177
2371
|
// on the belief that it is refused at the door before any model call, which its own `why` also claimed.
|
|
2178
2372
|
// It is not: R0d's FIRST submission is expected to admit (that is how it produces a duplicate to
|
|
@@ -3091,6 +3285,10 @@ async function cmdReport(id, { round: requestedToken = null } = {}) {
|
|
|
3091
3285
|
// — the handover's "commit SHA on test" field, READ rather than reconstructed. An absence
|
|
3092
3286
|
// is printed as itself: a run that predates the stamp has none, and inventing one from a reflog
|
|
3093
3287
|
// is the reconstruction this replaces.
|
|
3288
|
+
// — WHICH REGISTER SERVED IT, beside the engine build because it is the same class of fact: what
|
|
3289
|
+
// this run actually ran against, read as a field rather than searched for as a substring. An absence
|
|
3290
|
+
// prints as itself, because "not recorded" is not "none served" and a reader must not have to guess.
|
|
3291
|
+
console.log(` register: ${registerServedLine(readJson(join(run.runDir, "status.json")))}`);
|
|
3094
3292
|
const eb = engineBuildOf(run.runDir);
|
|
3095
3293
|
if (!eb) console.log(` engine build: NOT RECORDED — this run predates the run-start stamp (#1423); any attribution for it is a reflog reconstruction`);
|
|
3096
3294
|
else {
|
|
@@ -3177,7 +3375,7 @@ async function cmdReport(id, { round: requestedToken = null } = {}) {
|
|
|
3177
3375
|
notProbed.push(`${ref}: the doors refused, but this receipt records no REASON — re-run with the current e2e.mjs to check expect.reasonMatches`);
|
|
3178
3376
|
console.log(` reason: NOT RECORDED by the round that wrote this receipt — not a pass`);
|
|
3179
3377
|
} else {
|
|
3180
|
-
for (const r of refusal.reasons) console.log(` [${r.door}] ${
|
|
3378
|
+
for (const r of refusal.reasons) console.log(` [${r.door}] ${doorReasonLine(r)}`);
|
|
3181
3379
|
if (refusal.wantReason && refusal.missed.length)
|
|
3182
3380
|
toInvestigate.push(`${ref}: refused, but ${refusal.missed.join(", ")} did not say "${refusal.wantReason}" — a refusal for the wrong reason reads exactly like the right one`);
|
|
3183
3381
|
else if (refusal.wantReason) console.log(` [ ok ] every door's reason carries "${refusal.wantReason}"`);
|
|
@@ -3392,8 +3590,27 @@ async function cmdReport(id, { round: requestedToken = null } = {}) {
|
|
|
3392
3590
|
// A multi-name knockout stamps NO single url BY DESIGN: one address would be the first
|
|
3393
3591
|
// name standing for the batch, so the packet carries `reports[{mark, url}]` instead. Read it —
|
|
3394
3592
|
// from the run dir, or from the pool meta once the run dir is archived — and probe every per-mark
|
|
3395
|
-
// address exactly as the single-url arm does.
|
|
3396
|
-
//
|
|
3593
|
+
// address exactly as the single-url arm does.
|
|
3594
|
+
//
|
|
3595
|
+
// ── WHAT THE TWO ABSENCE ARMS BELOW MEASURE, AND WHAT THEY CANNOT SEE ───────────────────────────
|
|
3596
|
+
//
|
|
3597
|
+
// They measure one thing: no entry carries a url. They used to REPORT a second thing they never
|
|
3598
|
+
// looked at — that CLEAROTRON_REPORTS_URL is unset — and the row said it flatly, as a fact about
|
|
3599
|
+
// the instance.
|
|
3600
|
+
//
|
|
3601
|
+
// IT IS NOT ALWAYS TRUE, measured on a test instance 2026-09-30: the variable was present and
|
|
3602
|
+
// non-empty both in the instance's env file and in the worker process's own environment, and the
|
|
3603
|
+
// run's eight report entries each carried a `url` KEY WITH AN EMPTY VALUE. The stamp had run and
|
|
3604
|
+
// produced nothing. On that run the stated cause was simply wrong, and the cause of the empty
|
|
3605
|
+
// stamp is still unknown.
|
|
3606
|
+
//
|
|
3607
|
+
// WHAT IT COSTS TO STATE A CAUSE YOU DID NOT MEASURE: acting on the row, the obvious repair is to
|
|
3608
|
+
// restore the variable from a backup — overwriting a working value with an older one and calling
|
|
3609
|
+
// it a fix. That was nearly done here.
|
|
3610
|
+
//
|
|
3611
|
+
// So the row states the measurement and the investigate line keeps the hypothesis, marked as one.
|
|
3612
|
+
// A reader who wants the cause reads the variable on the instance and the stamp's own output; this
|
|
3613
|
+
// check is not in a position to tell them.
|
|
3397
3614
|
const batchReports = (() => {
|
|
3398
3615
|
try { return JSON.parse(readFileSync(driverDir(runDir, "delivery.json"), "utf8")).reports ?? []; } catch { /* archived or pre-batch */ }
|
|
3399
3616
|
try { return JSON.parse(readFileSync(join(poolDir, "meta.json"), "utf8")).reports ?? []; } catch { /* no pool entry */ }
|
|
@@ -3411,13 +3628,14 @@ async function cmdReport(id, { round: requestedToken = null } = {}) {
|
|
|
3411
3628
|
else if (error) { failures++; toInvestigate.push(`${ref}: could not probe ${r.mark}'s report URL (${error}) — ${r.url}`); }
|
|
3412
3629
|
}
|
|
3413
3630
|
} else if (batchReports.length) {
|
|
3414
|
-
console.log(` [FAIL] the run's stamped URL resolves\n delivered as a batch of ${batchReports.length},
|
|
3415
|
-
failures++; toInvestigate.push(`${ref}: batch delivered with no per-report URL stamped
|
|
3631
|
+
console.log(` [FAIL] the run's stamped URL resolves\n delivered as a batch of ${batchReports.length}, and not one of those entries carries a url`);
|
|
3632
|
+
failures++; toInvestigate.push(`${ref}: batch delivered with no per-report URL stamped — cause NOT measured here; read CLEAROTRON_REPORTS_URL on the instance AND what the stamp wrote, because a set variable can still stamp empty`);
|
|
3416
3633
|
} else {
|
|
3417
|
-
// A delivered run with no URL at all is the same absence one step earlier
|
|
3418
|
-
//
|
|
3419
|
-
|
|
3420
|
-
|
|
3634
|
+
// A delivered run with no URL at all is the same absence one step earlier, and the same limit
|
|
3635
|
+
// applies: this arm sees that the record carries no address, never why. The handoff packet then
|
|
3636
|
+
// carries no address for anyone to open, whatever produced that.
|
|
3637
|
+
console.log(` [FAIL] the run's stamped URL resolves\n delivered, and status.json carries no url`);
|
|
3638
|
+
failures++; toInvestigate.push(`${ref}: delivered with no report URL stamped — cause NOT measured here; read CLEAROTRON_REPORTS_URL on the instance AND what the stamp wrote, because a set variable can still stamp empty`);
|
|
3421
3639
|
}
|
|
3422
3640
|
}
|
|
3423
3641
|
|
|
@@ -4040,7 +4258,7 @@ function cmdTeardown(id) {
|
|
|
4040
4258
|
// stamp can never disagree about whether a round is done.
|
|
4041
4259
|
// adds `SCENARIO_FILE` — the ONE filename pattern, exported so the test that pins the widening
|
|
4042
4260
|
// reads the same regex `list` and the sweep read, rather than a copy that agrees today.
|
|
4043
|
-
export { evalAssertion, OPS, queueDrainState, DOORS, runLedger, investigate, brief, secs, queueOutcomes,
|
|
4261
|
+
export { evalAssertion, fromTheRun, OPS, queueDrainState, DOORS, runLedger, investigate, brief, secs, queueOutcomes,
|
|
4044
4262
|
TERMINAL_BY_SUFFIX, TERMINAL_BY_SUFFIX_RAN, SCENARIO_FILE };
|
|
4045
4263
|
|
|
4046
4264
|
// ── main ─────────────────────────────────────────────────────────────────────────────────────────────
|
|
@@ -4064,6 +4282,10 @@ if (invokedDirectly) {
|
|
|
4064
4282
|
if (a === "--round") {
|
|
4065
4283
|
requestedRound = raw[++i] ?? null;
|
|
4066
4284
|
if (!requestedRound || requestedRound.startsWith("--")) die("--round needs a round TOKEN: e2e.mjs report <ID> --round <token>\n The token is on `run`'s output and in the ROUNDS block of any report.");
|
|
4285
|
+
} else if (a === "--names") {
|
|
4286
|
+
// The words the run wrote, on the page. Off by default because `report` is the cheap instrument and
|
|
4287
|
+
// is pointed at real matters in sessions that record what they print.
|
|
4288
|
+
setReportNames(true);
|
|
4067
4289
|
} else if (a.startsWith("--round=")) {
|
|
4068
4290
|
requestedRound = a.slice("--round=".length);
|
|
4069
4291
|
if (!requestedRound) die("--round= needs a round TOKEN: e2e.mjs report <ID> --round=<token>");
|
|
@@ -4071,13 +4293,13 @@ if (invokedDirectly) {
|
|
|
4071
4293
|
}
|
|
4072
4294
|
const [cmd, arg, ...extra] = positional;
|
|
4073
4295
|
if (extra.length) die(`unexpected argument "${extra[0]}"\n`
|
|
4074
|
-
+ "usage: e2e.mjs list | run <ID> [--stale] | status | report <ID> [--round <token>] | teardown <ID> [--waive-unread]");
|
|
4296
|
+
+ "usage: e2e.mjs list | run <ID> [--stale] | status | report <ID> [--round <token>] [--names] | teardown <ID> [--waive-unread]");
|
|
4075
4297
|
switch (cmd) {
|
|
4076
4298
|
case "list": cmdList(); break;
|
|
4077
4299
|
case "run": if (!arg) die("usage: e2e.mjs run <ID> [--stale]"); await cmdRun(arg); break;
|
|
4078
4300
|
case "status": cmdStatus(); break;
|
|
4079
|
-
case "report": if (!arg) die("usage: e2e.mjs report <ID> [--round <token>]"); await cmdReport(arg, { round: requestedRound }); break;
|
|
4301
|
+
case "report": if (!arg) die("usage: e2e.mjs report <ID> [--round <token>] [--names]"); await cmdReport(arg, { round: requestedRound }); break;
|
|
4080
4302
|
case "teardown": if (!arg) die("usage: e2e.mjs teardown <ID> [--waive-unread]"); cmdTeardown(arg); break;
|
|
4081
|
-
default: die("usage: e2e.mjs list | run <ID> [--stale] | status | report <ID> [--round <token>] | teardown <ID> [--waive-unread]");
|
|
4303
|
+
default: die("usage: e2e.mjs list | run <ID> [--stale] | status | report <ID> [--round <token>] [--names] | teardown <ID> [--waive-unread]");
|
|
4082
4304
|
}
|
|
4083
4305
|
}
|
|
@@ -36,17 +36,51 @@ if (p.laid) console.log(`mint-reference-strip-backlog: ${p.laid} tracked path(s)
|
|
|
36
36
|
const tracked = p.files;
|
|
37
37
|
const minted = censusOf(ROOT, tracked, publishedReader(ROOT, (f) => readFileSync(join(ROOT, f), "utf8")));
|
|
38
38
|
|
|
39
|
+
// ── EVERY ROW IS EXPLAINED, OR THE TABLE IS A NUMBER NOBODY CAN ACT ON ────────────────────────────
|
|
40
|
+
//
|
|
41
|
+
// A count on its own tells a reader a file has residue in it. It does not tell them whether the line is
|
|
42
|
+
// residue at all — and once the table reached zero, the next row to appear is either a regression to
|
|
43
|
+
// repair or a known-good the scanner cannot tell from one. The first `.js` row is the second kind: an
|
|
44
|
+
// adapter header names a list separator as `(, ; / etc)`, which matches the third signature and is also
|
|
45
|
+
// correct English about punctuation. Without a sentence beside it that row is a number that will never
|
|
46
|
+
// fall, and a floor with one of those in it stops being read.
|
|
47
|
+
//
|
|
48
|
+
// So notes are carried by the minter rather than typed into the artifact: a re-mint keeps the note for a
|
|
49
|
+
// file that still has rows, drops it for a file that no longer does (a repair takes its explanation with
|
|
50
|
+
// it), and says which rows are still unexplained. It never invents one — an unexplained row is reported,
|
|
51
|
+
// and the arm that reads this table refuses it.
|
|
52
|
+
const NOTES = {
|
|
53
|
+
"providers/clarivate/src/core.js":
|
|
54
|
+
"NOT residue — the header names a list separator as `(, ; / etc)`. It matches the third signature "
|
|
55
|
+
+ "and is correct English about punctuation, so it is recorded rather than repaired.",
|
|
56
|
+
};
|
|
57
|
+
const notesFor = (files) => Object.fromEntries(
|
|
58
|
+
Object.keys(files).filter((f) => NOTES[f]).map((f) => [f, NOTES[f]]).sort(([a], [b]) => a < b ? -1 : 1));
|
|
59
|
+
const unexplained = (files) => Object.keys(files).filter((f) => !NOTES[f]).sort();
|
|
60
|
+
|
|
39
61
|
if (process.argv.includes("--check")) {
|
|
40
62
|
const have = JSON.parse(readFileSync(TABLE, "utf8"));
|
|
41
63
|
const a = JSON.stringify(have.files), b = JSON.stringify(minted.files);
|
|
64
|
+
if (JSON.stringify(have.notes ?? {}) !== JSON.stringify(notesFor(minted.files))) {
|
|
65
|
+
console.error("reference-strip backlog: the committed notes do not match this tree's rows.");
|
|
66
|
+
console.error(" re-mint with: node scripts/mint-reference-strip-backlog.mjs");
|
|
67
|
+
process.exit(1);
|
|
68
|
+
}
|
|
42
69
|
if (a !== b || have.total !== minted.total) {
|
|
43
70
|
console.error("reference-strip backlog is STALE against the tree.");
|
|
44
71
|
console.error(` committed total ${have.total}, tree has ${minted.total}`);
|
|
45
72
|
console.error(" re-mint with: node scripts/mint-reference-strip-backlog.mjs");
|
|
46
73
|
process.exit(1);
|
|
47
74
|
}
|
|
48
|
-
|
|
75
|
+
const open = unexplained(minted.files);
|
|
76
|
+
const held = Object.keys(minted.files).length - open.length;
|
|
77
|
+
console.log(`reference-strip backlog: current — ${minted.total} matching line(s); `
|
|
78
|
+
+ `${open.length} file(s) to repair, ${held} recorded as not residue`);
|
|
49
79
|
} else {
|
|
50
|
-
writeFileSync(TABLE, JSON.stringify({ signatures: SIGNATURES.map((s) => s.name), ...minted
|
|
80
|
+
writeFileSync(TABLE, JSON.stringify({ signatures: SIGNATURES.map((s) => s.name), ...minted,
|
|
81
|
+
notes: notesFor(minted.files) }, null, 2) + "\n");
|
|
51
82
|
console.log(`minted ${TABLE}: ${minted.total} line(s) across ${Object.keys(minted.files).length} file(s)`);
|
|
83
|
+
const open = unexplained(minted.files);
|
|
84
|
+
if (open.length) console.log(`mint-reference-strip-backlog: ${open.length} file(s) carry rows with no note — `
|
|
85
|
+
+ `each is a line to repair, or a note to add here if it is not residue:\n ${open.join("\n ")}`);
|
|
52
86
|
}
|