clearotron 0.3.2-beta.12 → 0.3.2-beta.14
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/CONTRIBUTING.md +6 -5
- package/INSTALL.md +3 -4
- package/README.md +2 -1
- package/bin/clearotron.mjs +7 -1
- package/bin/example.mjs +61 -23
- package/bin/onboard.mjs +16 -2
- package/bin/start.mjs +6 -3
- package/build-info.json +2 -2
- package/docs/CLIENT-MCP.md +6 -6
- package/docs/DELIVERY.md +3 -3
- package/docs/ONBOARDING.md +1 -1
- package/docs/PORTAL.md +3 -3
- package/docs/RELEASES.md +1 -1
- package/docs/SECURITY.md +1 -1
- package/docs/architecture/04-configuration-reference.md +4 -4
- package/docs/architecture/05-config-governance.md +10 -10
- package/docs/architecture/06-operations-runbook.md +3 -3
- package/docs/architecture/07-quality-and-audit.md +1 -1
- package/docs/architecture/08-development-guide.md +2 -2
- package/docs/architecture/09-security-and-data.md +1 -1
- package/docs/decisions/0002-no-dark-functionality.md +1 -1
- package/docs/decisions/0006-what-the-public-repository-carries.md +3 -3
- package/driver/CHANGELOG.md +21 -0
- package/driver/ask-ledger.mjs +1 -1
- package/driver/band-shape.mjs +1 -1
- package/driver/bundled-demos.mjs +2 -2
- package/driver/card-budget.mjs +2 -2
- package/driver/case-law-ledger.mjs +2 -2
- package/driver/connotation-search.mjs +5 -5
- package/driver/contract-e3-backlog.mjs +13 -13
- package/driver/coverage-form.mjs +2 -2
- package/driver/demo-container.mjs +26 -2
- package/driver/disposition-tool.mjs +2 -2
- package/driver/engine/CONTRACT.md +3 -3
- package/driver/engine/mcp/codex-config.mjs +3 -11
- package/driver/engine/mcp/gather-config.mjs +1 -1
- package/driver/engine/openai-agent.mjs +2 -2
- package/driver/findings-model.mjs +21 -6
- package/driver/gateway.mjs +1 -1
- package/driver/package.json +1 -1
- package/driver/pipeline-knockout.mjs +1 -1
- package/driver/pipeline.mjs +4 -4
- package/driver/placement-union.mjs +1 -1
- package/driver/portal-mcp-client.mjs +1 -1
- package/driver/portal-report.mjs +4 -1
- package/driver/portal-service.mjs +17 -5
- package/driver/predelivery-lint.mjs +9 -4
- package/driver/progress.mjs +37 -1
- package/driver/publish/render-knockout.mjs +31 -6
- package/driver/publish/render.mjs +6 -6
- package/driver/record-discard.mjs +1 -1
- package/driver/register-count.mjs +1 -1
- package/driver/register-digest-record.mjs +1 -1
- package/driver/report-card-record.mjs +2 -2
- package/driver/roster-verdict.mjs +2 -2
- package/driver/search-policy.mjs +1 -1
- package/driver/skeptic-record.mjs +1 -1
- package/driver/stages.mjs +1 -1
- package/driver/suite-census.json +40 -10
- package/driver/systemd/README.md +1 -1
- package/driver/unit-inventory.mjs +2 -2
- package/driver/verify.mjs +1 -1
- package/mcp-server/CHANGELOG.md +8 -0
- package/mcp-server/CONNECT.md +8 -8
- package/mcp-server/lib/runs.mjs +1 -1
- package/mcp-server/package.json +1 -1
- package/mcp-server/serve.mjs +27 -0
- package/package.json +1 -1
- package/portal-ui/package.json +1 -1
- package/providers/free-tier/src/capabilities.js +2 -2
- package/providers/jx/src/core.js +3 -3
- package/providers/jx/src/turn-envelope.mjs +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/perplexity/README.md +1 -1
- package/providers/signa/src/capabilities.js +2 -2
- package/providers/signa/src/core.js +2 -2
- package/providers/uspto-local/src/core.js +1 -1
- package/providers/uspto-local/src/sync.js +1 -1
- package/scripts/README.md +2 -6
- package/scripts/citation-anchor-report.mjs +1 -1
- package/scripts/citation-line-check.mjs +3 -3
- package/scripts/e2e.mjs +1 -1
- package/scripts/env-audit.mjs +1 -1
- package/scripts/env-classify.mjs +1 -1
- package/scripts/pack-publishable.mjs +1 -1
- package/scripts/release-artifact-seal.mjs +2 -2
- package/scripts/settings-render-check.mjs +5 -1
- package/scripts/strip-tracker-citations.mjs +4 -4
- package/scripts/test-run.mjs +46 -3
- package/shared/browser-temp-root.mjs +10 -3
- package/shared/client-door.mjs +18 -6
- package/shared/connect-clients.mjs +2 -0
- package/shared/demo-start-args.mjs +10 -0
- package/shared/identifier-scan.mjs +2 -2
- package/shared/invocation.mjs +1 -1
- package/shared/reference-guard-classes.mjs +5 -3
- package/shared/stdio-connect.mjs +80 -28
- package/shared/toml-string.mjs +25 -0
- package/shared/writing-standard-classes.mjs +2 -3
|
@@ -60,7 +60,7 @@ Every `CLEAROTRON_*` name read by shipping code declares an effect class, and an
|
|
|
60
60
|
whatever the declaration test enforces. Do not re-derive it by hand; that is how 172, 159 and 352 all
|
|
61
61
|
came to describe the same repository.
|
|
62
62
|
|
|
63
|
-
## Addendum — 2026-08-20, on implementation
|
|
63
|
+
## Addendum — 2026-08-20, on implementation
|
|
64
64
|
|
|
65
65
|
All four switches are deleted, as ruled. One consequence above is narrowed by what the code turned out
|
|
66
66
|
to be, and it is recorded here rather than in a pull request body, because this document is what the
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# 0006 — What the public repository carries
|
|
2
2
|
|
|
3
|
-
**Accepted 2026-08-17
|
|
3
|
+
**Accepted 2026-08-17.**
|
|
4
4
|
|
|
5
5
|
## Context
|
|
6
6
|
|
|
@@ -74,7 +74,7 @@ announces which mode it is in, once, so a reader of any run can tell which colum
|
|
|
74
74
|
"change the code" is the clause it answers to and this repository's working practice is that agents change
|
|
75
75
|
the code. `CLAUDE.md` is not carried: it was only ever a one-line pointer at `AGENTS.md`, a second copy of
|
|
76
76
|
one subject is a future contradiction ([ADR-0004](0004-documentation-structure.md)), and the de-identified
|
|
77
|
-
public cut names that file specifically.
|
|
77
|
+
public cut names that file specifically. Decided 2026-08-19, on the question raised against the
|
|
78
78
|
recovery — where the two files had been held back because the revert that stripped them recorded no
|
|
79
79
|
decision either way, and `shared/withheld-paths.mjs` did not cover them.
|
|
80
80
|
|
|
@@ -89,7 +89,7 @@ vendor-branded duplicate of it does not, however small.
|
|
|
89
89
|
citations across code, comments, env examples and docs; all but six were rewritten, and the six declared
|
|
90
90
|
are this record's own, where naming what is dropped is the content.
|
|
91
91
|
- **A binding to a withheld document is retired.** `driver/doc-constants.mjs` pinned four prose figures in
|
|
92
|
-
`docs/KNOCKOUT.md` and `docs/REGISTER-HIT-COUNTS.md`; those rows are gone, and the test pinning
|
|
92
|
+
`docs/KNOCKOUT.md` and `docs/REGISTER-HIT-COUNTS.md`; those rows are gone, and the test pinning the
|
|
93
93
|
one-file-states-it-twice case now anchors to the shape rather than to a named document.
|
|
94
94
|
- **Withholding is reversible**, which is why this is a list rather than a `git rm`. This repository keeps
|
|
95
95
|
every word.
|
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,26 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.3.2-beta.14
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- Fixed: The lines for connecting an AI assistant now work when a folder name contains spaces, and the Codex settings block now works on Windows.
|
|
8
|
+
- Fixed: The demo now opens by saying what it shows, without internal diagnostics or a Node warning, and its knockout sample states the rating it reached.
|
|
9
|
+
- Fixed: A knockout report ordered for named countries now lists those countries under Registers counted, instead of a count of the provider's registers.
|
|
10
|
+
|
|
11
|
+
## 0.3.2-beta.13
|
|
12
|
+
|
|
13
|
+
### Patch Changes
|
|
14
|
+
|
|
15
|
+
- Fixed: A conditional verdict whose conditions are all kept in the run record no longer shows an empty "conditional on" line. The rating word stands alone.
|
|
16
|
+
- Fixed: A running clearance's card and row show the stage it is in now, including a step back during a correction pass.
|
|
17
|
+
- Fixed: The portal's health check no longer includes an internal error message when the instructions store cannot be read. The message goes to the service log instead.
|
|
18
|
+
- Fixed: `doctor` no longer describes another program on the client door's port as this install's door. It says only that a process holds the port.
|
|
19
|
+
- Fixed: The connect lines name the exact Node your install runs on, and an older Node gets one plain line instead of a crash.
|
|
20
|
+
- Fixed: The demo no longer leaves copies of its samples in your temp directory, including when it is stopped mid-way.
|
|
21
|
+
- Fixed: Running the test suite no longer leaves demo copies in the machine's temporary directory.
|
|
22
|
+
- Fixed: On WSL, the Windows connect line names your Linux distribution, or says plainly to fill it in, instead of leaving it out.
|
|
23
|
+
|
|
3
24
|
## 0.3.2-beta.12
|
|
4
25
|
|
|
5
26
|
### Patch Changes
|
package/driver/ask-ledger.mjs
CHANGED
|
@@ -170,7 +170,7 @@ function endingForQids(qids, join, ts) {
|
|
|
170
170
|
return null;
|
|
171
171
|
}
|
|
172
172
|
|
|
173
|
-
/** The {ending, handoff} pair for one cross-check directive. Only `recall` is subject to
|
|
173
|
+
/** The {ending, handoff} pair for one cross-check directive. Only `recall` is subject to the
|
|
174
174
|
* discharge rule; every other net keeps the ending it always had. PURE. */
|
|
175
175
|
function recallEnding(name, qid, join, ts) {
|
|
176
176
|
const ending = endingForQids([qid], join, ts);
|
package/driver/band-shape.mjs
CHANGED
|
@@ -769,7 +769,7 @@ export function buildBandShape(band, { targets = [], inScopeClasses = [], crowdC
|
|
|
769
769
|
}
|
|
770
770
|
|
|
771
771
|
const fmtN = (n) => Number(n ?? 0).toLocaleString("en-US");
|
|
772
|
-
const cell = (v) => String(v ?? "").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim() || "—";
|
|
772
|
+
const cell = (v) => String(v ?? "").replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim() || "—";
|
|
773
773
|
|
|
774
774
|
// Bounded aggregate table: top rows by count, one honest remainder line — never for the floors.
|
|
775
775
|
function topTable(md, title, cols, entries, cap = 30) {
|
package/driver/bundled-demos.mjs
CHANGED
|
@@ -3,8 +3,8 @@
|
|
|
3
3
|
// bundled-demos.mjs — WHICH client bundles this repo ships, read from the directory that ships them.
|
|
4
4
|
//
|
|
5
5
|
// This was a hand-maintained triple. The directory grew a fourth bundle, the triple did not, and the
|
|
6
|
-
// set-equality it feeds could no longer match anything — so the
|
|
7
|
-
// notice a door that has silently fallen back to the bundled roster, returned PASS on
|
|
6
|
+
// set-equality it feeds could no longer match anything — so the roster-fallback detector, whose whole job is to
|
|
7
|
+
// notice a door that has silently fallen back to the bundled roster, returned PASS on exactly that
|
|
8
8
|
// condition. A guard that cannot match reports that it found nothing wrong.
|
|
9
9
|
//
|
|
10
10
|
// The same shape had already produced two FALSE REFUSALS on this check, both from an exact match
|
package/driver/card-budget.mjs
CHANGED
|
@@ -74,13 +74,13 @@ function insertBulletAtSectionHead(text, headingRe, headingLine, bullet, { paren
|
|
|
74
74
|
const m = text.match(headingRe);
|
|
75
75
|
if (m) {
|
|
76
76
|
const at = m.index + m[0].length;
|
|
77
|
-
return `${text.slice(0, at)}\n${bullet}\n${text.slice(at)
|
|
77
|
+
return `${text.slice(0, at)}\n${bullet}\n${text.slice(at)}`;
|
|
78
78
|
}
|
|
79
79
|
if (parentHeadingRe) {
|
|
80
80
|
const p = text.match(parentHeadingRe);
|
|
81
81
|
if (p) {
|
|
82
82
|
const at = p.index + p[0].length;
|
|
83
|
-
return `${text.slice(0, at)}\n${headingLine}\n\n${bullet}\n${text.slice(at)
|
|
83
|
+
return `${text.slice(0, at)}\n${headingLine}\n\n${bullet}\n${text.slice(at)}`;
|
|
84
84
|
}
|
|
85
85
|
return `${text.replace(/\s*$/, "")}\n\n${parentHeadingLine}\n\n${headingLine}\n\n${bullet}\n`;
|
|
86
86
|
}
|
|
@@ -114,8 +114,8 @@ export function parseCaseLawLedger(raw) {
|
|
|
114
114
|
/**
|
|
115
115
|
* The census a reader (and a scenario) asks the ledger for. PURE, and it never judges.
|
|
116
116
|
*
|
|
117
|
-
* `readByTerritory` is the one that answers the depth-dive condition: a dive names ONE territory
|
|
118
|
-
*
|
|
117
|
+
* `readByTerritory` is the one that answers the depth-dive condition: a dive names ONE territory,
|
|
118
|
+
* so "did this dive read anything in its own territory" is a lookup, not an inference.
|
|
119
119
|
*/
|
|
120
120
|
export function caseLawRetrievalCensus(ledger) {
|
|
121
121
|
const queries = ledger?.queries ?? [];
|
|
@@ -1363,7 +1363,7 @@ export function parseDispositionForm(raw) {
|
|
|
1363
1363
|
// EVERY OBLIGATION SHIPS. No head(), no slice(), no "…and N more". The register-digest sibling
|
|
1364
1364
|
// (gateway.mjs deferredSlicesRequiredRows) printed the first six qids per axis and elided the rest, and its
|
|
1365
1365
|
// own doc block records what that cost: "Under advice that is an economy; under a requirement it makes
|
|
1366
|
-
// compliance impossible." Same rule here
|
|
1366
|
+
// compliance impossible." Same rule here: a value a model must copy is complete, or it is
|
|
1367
1367
|
// visibly marked as cut. Nothing below is cut, so nothing below is marked.
|
|
1368
1368
|
/**
|
|
1369
1369
|
* The obligation set as the seat's own to-do list, for the tool result. Returns "" when nothing is owed —
|
|
@@ -1375,7 +1375,7 @@ export function renderConnotationObligations(ob, { ledgerPath = "your half ledge
|
|
|
1375
1375
|
// ── — THE PAGE IS NUMBERED OFF THE SAME FUNCTION THAT RESOLVES THE NUMBER ─────────────────
|
|
1376
1376
|
//
|
|
1377
1377
|
// `obligationRows` is the driver's row list, and a `row_index` is a position into it. Numbering this
|
|
1378
|
-
// page with its own counter would be two derivations of one ordering, which is the mistake
|
|
1378
|
+
// page with its own counter would be two derivations of one ordering, which is the mistake the
|
|
1379
1379
|
// segment display already names in its own comment: "the display and the check cannot disagree, which
|
|
1380
1380
|
// they would the moment either recomputed its own split". So the numbers come from the list itself.
|
|
1381
1381
|
//
|
|
@@ -1610,7 +1610,7 @@ export function renderDispositionTable(rows, ob) {
|
|
|
1610
1610
|
for (const e of ob?.queries ?? []) for (const r of e.results) if (r.id) byId.set(normId(r.id), r);
|
|
1611
1611
|
// — the rows are the FORM's, and a row the seat never ruled on has nothing to render: it is
|
|
1612
1612
|
// outstanding work, which the gate reports as a count, not a table row reading "—".
|
|
1613
|
-
const cell = (s) => String(s ?? "").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
1613
|
+
const cell = (s) => String(s ?? "").replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
1614
1614
|
const usable = (rows ?? []).filter((d) => d?.receipt_id && d?.ruling);
|
|
1615
1615
|
// ── — A PARKED ROW IS NOT AN OUTSTANDING ROW, AND THE FILTER ABOVE STOPPED BEING SAFE ─────────
|
|
1616
1616
|
//
|
|
@@ -1683,7 +1683,7 @@ const NO_POLICED_SECTION = "(no PR / reputational / connotation section)";
|
|
|
1683
1683
|
// id that is not among THAT row's own candidates — the driver wrote those ids, so this is a damaged
|
|
1684
1684
|
// form, not outstanding work, and its remedy is different.
|
|
1685
1685
|
// undisposed_cite_too_short deleted — cannot occur; nothing is retyped.
|
|
1686
|
-
// recurrent_uncited_form deleted — the form has no shape for the seat to get wrong.
|
|
1686
|
+
// recurrent_uncited_form deleted — the form has no shape for the seat to get wrong. The old
|
|
1687
1687
|
// cited-but-unbound state was a property of PROSE (right page, wrong query string on the line); a
|
|
1688
1688
|
// form row IS its binding, so the state has no representative.
|
|
1689
1689
|
// no_source_cite deleted — advisory, prose-only, never reached a token.
|
|
@@ -1786,7 +1786,7 @@ export const CONNOTATION_FORM_REASONS = Object.freeze(
|
|
|
1786
1786
|
*
|
|
1787
1787
|
* `quote_unbound` and `form_damaged` are deliberately absent: those rows ARE ruled.
|
|
1788
1788
|
*
|
|
1789
|
-
* `parked`
|
|
1789
|
+
* `parked` is absent for the opposite reason and it is the load-bearing one: a parked row
|
|
1790
1790
|
* is NOT ruled, and it is not outstanding either. Outstanding means "the seat still owes this", and the
|
|
1791
1791
|
* whole point of the park is that the seat has been told to stop — leaving it here would keep the stage
|
|
1792
1792
|
* failing forever on a row nobody may re-send, which is the deterministic death the park introduced.
|
|
@@ -456,7 +456,7 @@ export const E3_BACKLOG = [
|
|
|
456
456
|
where: "driver/stages.mjs:3182",
|
|
457
457
|
surface: "stage-message",
|
|
458
458
|
evidence: "MACHINE FINDINGS (MANDATORY): … a JSON OBJECT {\"schema_version\":<FINDINGS_SCHEMA_VERSION>,\"rated_under_framework\":\"…\",\"findings\":[...],\"coverage\":[...],\"context_notes\":[...],\"actions\":[...],\"ask_answers\":[...]} … Each finding object has EXACTLY these keys: {\"ordinal\",\"mark\",\"owner\",\"band\",\"net\",\"bor",
|
|
459
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
459
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson via validators.narrative",
|
|
460
460
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
461
461
|
},
|
|
462
462
|
// RETIRED 2026-08-16 — synthesis's dictated disposition list, and I am the one who left it standing.
|
|
@@ -479,7 +479,7 @@ export const E3_BACKLOG = [
|
|
|
479
479
|
where: "driver/stages.mjs:3219",
|
|
480
480
|
surface: "stage-message",
|
|
481
481
|
evidence: "- manageable …: {\"category\":\"<EXACTLY one of large-competitor / commercial-partner / troll / well-known-enforcer>\",\"reason\":\"<one-two lines…>\"}",
|
|
482
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
482
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson",
|
|
483
483
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
484
484
|
},
|
|
485
485
|
{
|
|
@@ -488,7 +488,7 @@ export const E3_BACKLOG = [
|
|
|
488
488
|
where: "driver/stages.mjs:3235",
|
|
489
489
|
surface: "stage-message",
|
|
490
490
|
evidence: "- meters: {\"mark_similarity\":{...},\"goods_proximity\":{...},\"use\":{...},\"enforcer\":{...}} — all four present, each {\"token\",\"basis\",\"source\"}. … mark_similarity = high | medium | low. goods_proximity = high | medium | low. enforcer = high | medium | low | unknown. use = confirmed | not-confirmed | un",
|
|
491
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
491
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson; driver/verify.mjs:1171 checkFindingsSibling gates meters.*.source; finding_basis_source_missing",
|
|
492
492
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
493
493
|
},
|
|
494
494
|
{
|
|
@@ -497,7 +497,7 @@ export const E3_BACKLOG = [
|
|
|
497
497
|
where: "driver/stages.mjs:3236",
|
|
498
498
|
surface: "stage-message",
|
|
499
499
|
evidence: "- quadrant: {\"x\",\"y\"} numbers in [0,1]. x = goods/services proximity (0 = distant, 1 = identical). y = mark similarity (0 = distinct, 1 = identical).",
|
|
500
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
500
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson",
|
|
501
501
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
502
502
|
},
|
|
503
503
|
{
|
|
@@ -506,7 +506,7 @@ export const E3_BACKLOG = [
|
|
|
506
506
|
where: "driver/stages.mjs:3237",
|
|
507
507
|
surface: "stage-message",
|
|
508
508
|
evidence: "- source: {\"source_type\",\"resolved_link\"}. source_type EXACTLY one of: register-vendor / register-euipo / common-law-marketplace / common-law-web / case-law",
|
|
509
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
509
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson",
|
|
510
510
|
removedByMove: "M1 removes the opaque `resolved_link` URI (ordinal / record-handle selection); the object shape and the source_type enum survive",
|
|
511
511
|
},
|
|
512
512
|
{
|
|
@@ -515,7 +515,7 @@ export const E3_BACKLOG = [
|
|
|
515
515
|
where: "driver/stages.mjs:3238",
|
|
516
516
|
surface: "stage-message",
|
|
517
517
|
evidence: "coverage[]: ONE object per coverage AREA, EXACTLY {\"area\",\"state\",\"note\"}. … state EXACTLY one of: confirmed-clean / coverage-limited / open / not-searched / note.",
|
|
518
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
518
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson; the render owns the coverage panel from these typed states",
|
|
519
519
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
520
520
|
},
|
|
521
521
|
{
|
|
@@ -533,7 +533,7 @@ export const E3_BACKLOG = [
|
|
|
533
533
|
where: "driver/stages.mjs:3304",
|
|
534
534
|
surface: "stage-message",
|
|
535
535
|
evidence: "MARK ASSESSMENT … STRUCTURED FORM …: either field may instead be an OBJECT {\"read\":\"…\",\"spectrum\":\"…\",\"per_class\":[{\"class\":\"5\",\"note\":\"…\"}],\"per_market\":[{\"market\":\"CN\",\"note\":\"…\"}],\"counter_registrations\":[{\"mark\":\"…\",\"uri\":\"/mark/…\",\"note\":\"…\"}],\"acquired\":\"<optional>\",\"note\":\"<optional residual>",
|
|
536
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
536
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson; the report collapses the rows behind toggles and the audit workbook renders them",
|
|
537
537
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
538
538
|
},
|
|
539
539
|
{
|
|
@@ -542,7 +542,7 @@ export const E3_BACKLOG = [
|
|
|
542
542
|
where: "driver/stages.mjs:3310",
|
|
543
543
|
surface: "stage-message",
|
|
544
544
|
evidence: "FOUR ANSWERS …: \"four_answers\": {\"third_party_rights\":{...},\"objection_likelihood\":{...},\"registrability\":{...},\"client_enforceability\":{...}} … Each answer … is {\"read\":\"…\",\"token\":\"…\",\"basis\":\"…\",\"ordinals\":[…]}. Tokens (closed enums …): third_party_rights = strong|moderate|weak; objection_likelih",
|
|
545
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
545
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson",
|
|
546
546
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
547
547
|
},
|
|
548
548
|
{
|
|
@@ -551,7 +551,7 @@ export const E3_BACKLOG = [
|
|
|
551
551
|
where: "driver/stages.mjs:3221",
|
|
552
552
|
surface: "stage-message",
|
|
553
553
|
evidence: "ACTIONS REGISTER …: emit \"actions\": [...] — ONE object per forward step …, each EXACTLY {\"id\",\"kind\",\"text\",\"ordinals\"} plus an OPTIONAL \"deadline\" and an OPTIONAL \"condition\". … kind: EXACTLY one of — consent / coexistence-agreement / territorial-delimitation / goods-amendment / mark-modification /",
|
|
554
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
554
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson; pipeline applyCoverageFloor legalActions arm derives the delivered disposition from the closed kind enum",
|
|
555
555
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
556
556
|
},
|
|
557
557
|
{
|
|
@@ -560,7 +560,7 @@ export const E3_BACKLOG = [
|
|
|
560
560
|
where: "driver/stages.mjs:3317",
|
|
561
561
|
surface: "stage-message",
|
|
562
562
|
evidence: "COVERAGE JUDGMENT …: emit \"coverage_judgment\": {\"sufficient\":<bool>, \"reason\":\"<one line…>\"} — EXACTLY those two keys. Do NOT emit \"rows\": the driver writes that register itself … anything you type there is replaced wholesale.",
|
|
563
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
563
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson. The \"Do NOT emit rows\" clause is the purest E3 case in the tree — the prompt names a field, dictates its shape and states in the same breath that code overwrites it",
|
|
564
564
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
565
565
|
},
|
|
566
566
|
{
|
|
@@ -575,7 +575,7 @@ export const E3_BACKLOG = [
|
|
|
575
575
|
// in E3_EVIDENCE_UNRESOLVED instead would have grown the not-checked slice for a row that is
|
|
576
576
|
// perfectly checkable, which is the avoidable coverage loss conversion 9's note names one row up.
|
|
577
577
|
evidence: "SEND ONE \"ask_answers\" ENTRY PER ASK, as a TOP-LEVEL field of the findings record you hand to the call (never as a narrative section — the driver renders the labelled line",
|
|
578
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
578
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson (finding_ask_answer_answer_missing); assembleReportMd · buildAskAnswersSection joins on the retyped ask string",
|
|
579
579
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
580
580
|
},
|
|
581
581
|
// DELETED at conversion 10, recorded rather than absorbed — E3's own rule for a
|
|
@@ -611,7 +611,7 @@ export const E3_BACKLOG = [
|
|
|
611
611
|
where: "driver/stages.mjs:3296",
|
|
612
612
|
surface: "stage-message",
|
|
613
613
|
evidence: "add it to the top-level \"context_notes\" array — each object EXACTLY {\"type\":\"famous-neighbour-ungrounded\",\"mark\",\"owner\",\"context\"}",
|
|
614
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
614
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson",
|
|
615
615
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
616
616
|
},
|
|
617
617
|
{
|
|
@@ -620,7 +620,7 @@ export const E3_BACKLOG = [
|
|
|
620
620
|
where: "driver/stages.mjs:3184",
|
|
621
621
|
surface: "stage-message",
|
|
622
622
|
evidence: "- owner: {\"name\",\"country\",\"registrations\":[...]}. … Each registration: {\"uri\", optionally \"classes\":[\"9\",\"41\"],\"status\",\"filed\",\"expiry\",\"jurisdiction\"}. The \"uri\" is the ONLY field that matters: the driver BINDS classes/status/filed/expiry/jurisdiction AND the owner name from the FETCHED record ke",
|
|
623
|
-
reparsedBy: "driver/findings-model.mjs:
|
|
623
|
+
reparsedBy: "driver/findings-model.mjs:844 parseFindingsJson + the record-binding join. Six of the seven keys are stated in the prompt and overwritten by code in the same sentence",
|
|
624
624
|
removedByMove: "M1 removes the opaque `uri` (ordinal / record-handle selection against the band); the object skeleton and the five overwritten keys survive",
|
|
625
625
|
},
|
|
626
626
|
{
|
package/driver/coverage-form.mjs
CHANGED
|
@@ -13,7 +13,7 @@
|
|
|
13
13
|
// PASS; ~86% of emitted tokens landed in no artifact, and the attempt that passed is the one that
|
|
14
14
|
// PATCHED). A cold ladder never patches.
|
|
15
15
|
//
|
|
16
|
-
// THE CURE IS
|
|
16
|
+
// THE CURE IS THE ONE APPLIED ONE GATE OVER: the driver writes the row and the model fills in the judgment. The
|
|
17
17
|
// axis, the coverage unit, the open crowd blocks with their qids and hit counts, and the deferred slices
|
|
18
18
|
// with their per-qid receipt reasons are all facts the driver holds BEFORE the stage dispatches. It
|
|
19
19
|
// writes them. The seat sets `status` and `reason`, and nothing else in the file is the seat's to write.
|
|
@@ -856,7 +856,7 @@ export function findCoverageFormViolations(rows, formError = null) {
|
|
|
856
856
|
// kind and nothing in it is a string the model had to copy. parseCoverageLedgerFull survives, but only as
|
|
857
857
|
// the ARCHIVED-RUN reader (loadCoverageLedger's prose fallback): no gate parses this table any more.
|
|
858
858
|
|
|
859
|
-
const cell = (s) => String(s ?? "").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
859
|
+
const cell = (s) => String(s ?? "").replace(/\\/g, "\\\\").replace(/\|/g, "\\|").replace(/\s+/g, " ").trim();
|
|
860
860
|
|
|
861
861
|
/** The `## Coverage ledger` section, rendered from the form's rows. "" when nothing is settled. PURE. */
|
|
862
862
|
export function renderCoverageLedgerSection(rows) {
|
|
@@ -97,6 +97,30 @@ export function prepareSample(dir, { repoRoot, tmp } = {}) {
|
|
|
97
97
|
}
|
|
98
98
|
|
|
99
99
|
|
|
100
|
+
/**
|
|
101
|
+
* THE COPIES GO WHEN THEIR PUBLISH IS DONE, AND AT EXIT WHATEVER HAPPENED.
|
|
102
|
+
*
|
|
103
|
+
* Every copy below is made in the system temp directory and nothing removed it: four per `demo --once`,
|
|
104
|
+
* one per demo start, some a full replayed run of about 1,850 files, and 36 GB of them on one machine
|
|
105
|
+
* (measured 2026-09-19). Each root is recorded as it is made. A caller releases them the moment its
|
|
106
|
+
* publish is done, and an exit hook removes whatever is left, on success and on failure. A caller that
|
|
107
|
+
* can be stopped by a signal turns the signal into an exit, so the hook runs then too.
|
|
108
|
+
*/
|
|
109
|
+
const copies = new Set();
|
|
110
|
+
let exitHooked = false;
|
|
111
|
+
function tracked(root) {
|
|
112
|
+
copies.add(root);
|
|
113
|
+
if (!exitHooked) { exitHooked = true; process.once("exit", releaseDemoCopies); }
|
|
114
|
+
return root;
|
|
115
|
+
}
|
|
116
|
+
/** Remove every copy made so far. Safe to call more than once. */
|
|
117
|
+
export function releaseDemoCopies() {
|
|
118
|
+
for (const d of [...copies]) {
|
|
119
|
+
try { rmSync(d, { recursive: true, force: true }); } catch { /* going anyway; the exit hook tries again */ }
|
|
120
|
+
copies.delete(d);
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
100
124
|
/**
|
|
101
125
|
* The directory a frozen demo should be PUBLISHED from — itself, or a copy when it is part of this tree.
|
|
102
126
|
*
|
|
@@ -117,7 +141,7 @@ export function publishSource(dir, { repoRoot, tmp = tmpdir() } = {}) {
|
|
|
117
141
|
const root = resolve(repoRoot ?? "");
|
|
118
142
|
const here = resolve(dir);
|
|
119
143
|
if (!root || !(here === root || here.startsWith(root + sep))) return dir;
|
|
120
|
-
const copy = join(mkdtempSync(join(tmp, "clearotron-demo-")), "sample");
|
|
144
|
+
const copy = join(tracked(mkdtempSync(join(tmp, "clearotron-demo-"))), "sample");
|
|
121
145
|
cpSync(here, copy, { recursive: true });
|
|
122
146
|
return copy;
|
|
123
147
|
}
|
|
@@ -135,7 +159,7 @@ export function publishContainer(root, { repoRoot, tmp = tmpdir() } = {}) {
|
|
|
135
159
|
const repo = resolve(repoRoot ?? "");
|
|
136
160
|
const here = resolve(root);
|
|
137
161
|
if (!repo || !(here === repo || here.startsWith(repo + sep))) return { dir: root, unusable: demoInventory(root).unusable };
|
|
138
|
-
const copy = join(mkdtempSync(join(tmp, "clearotron-demo-")), "sample");
|
|
162
|
+
const copy = join(tracked(mkdtempSync(join(tmp, "clearotron-demo-"))), "sample");
|
|
139
163
|
mkdirSync(copy, { recursive: true });
|
|
140
164
|
const { children, unusable } = demoInventory(here);
|
|
141
165
|
const left = [...unusable];
|
|
@@ -288,8 +288,8 @@ export function callsSoFar(dispositionsPath) {
|
|
|
288
288
|
export { PARK_AFTER_REFUSALS };
|
|
289
289
|
|
|
290
290
|
/**
|
|
291
|
-
* Which rows have been refused past the bound. PURE, and counted PER ROW ACROSS CALLS — the unit
|
|
292
|
-
* own banner records an earlier investigation dying on, when it joined per-row-final-state instead.
|
|
291
|
+
* Which rows have been refused past the bound. PURE, and counted PER ROW ACROSS CALLS — the unit this
|
|
292
|
+
* module's own banner records an earlier investigation dying on, when it joined per-row-final-state instead.
|
|
293
293
|
*
|
|
294
294
|
* A row that is ruled later is never re-parked, because the union only parks rows that are not ruled.
|
|
295
295
|
* @returns {string[]} row_ids, sorted, so the same ledger always yields the same answer.
|
|
@@ -145,7 +145,7 @@ Stages must name **abstract tiers**, not provider aliases. Per-engine maps:
|
|
|
145
145
|
| `sweep` | register-unit, case-law, skeptic, report-overview, report-card | `sonnet` | `$CLEAROTRON_OPENAI_MODEL_SWEEP` |
|
|
146
146
|
| `cheap` | saturation-probe | `haiku` | `$CLEAROTRON_OPENAI_MODEL_CHEAP` |
|
|
147
147
|
|
|
148
|
-
**AN UNHONOURED OVERRIDE IS AN ERROR, NOT A SUBSTITUTION** (
|
|
148
|
+
**AN UNHONOURED OVERRIDE IS AN ERROR, NOT A SUBSTITUTION** (2026-08-03). This
|
|
149
149
|
section used to declare two further tiers — `skeptic` → `google/gemini-3-flash-preview` and
|
|
150
150
|
`refutation` → `together/deepseek-ai/DeepSeek-V4-Pro` — each substituting an anthropic model on the
|
|
151
151
|
anthropic engine, "grade-moving, validated only in the paid A/B". The substitution never was validated
|
|
@@ -191,7 +191,7 @@ disarms the refusal and never the record.
|
|
|
191
191
|
| `max` | `max` | `xhigh` |
|
|
192
192
|
|
|
193
193
|
`off` used to map to `minimal` on codex and `low` on claude — a whole rung apart at the bottom, so a
|
|
194
|
-
cross-engine effort comparison at `off` was off by one
|
|
194
|
+
cross-engine effort comparison at `off` was off by one. `low` is the anthropic
|
|
195
195
|
floor, so codex came up to it and codex's `minimal` is deliberately unreachable from the driver's tier
|
|
196
196
|
vocabulary. `max` is the one sanctioned divergence: it means "this engine's top rung", and codex has no
|
|
197
197
|
`max`. `engine.anthropic.test.mjs` pins the two tables together and asserts that single exception.
|
|
@@ -200,7 +200,7 @@ vocabulary. `max` is the one sanctioned divergence: it means "this engine's top
|
|
|
200
200
|
(`assertEffectiveTier`), on the EFFECTIVE tier rather than the declared one: Haiku 4.5 rejects adaptive
|
|
201
201
|
thinking and the request bounces to sonnet, so the pairing measures sonnet and records haiku. Both ways
|
|
202
202
|
it can arise are runtime overrides that never touch the stage table (`CLEAROTRON_STAGE_THINKING`, an
|
|
203
|
-
`--experiment --model`), which is why the start-of-run scan alone could not see it
|
|
203
|
+
`--experiment --model`), which is why the start-of-run scan alone could not see it.
|
|
204
204
|
|
|
205
205
|
## 4. `claude -p` invocation (anthropic-agent)
|
|
206
206
|
|
|
@@ -5,6 +5,7 @@
|
|
|
5
5
|
// exact server/tool wiring. Two inputs, both produced today by the gateway gather block:
|
|
6
6
|
import { fileURLToPath } from "node:url";
|
|
7
7
|
import { dirname, join } from "node:path";
|
|
8
|
+
import { tomlString } from "../../../shared/toml-string.mjs";
|
|
8
9
|
|
|
9
10
|
// • mcpConfig — the claude-shaped JSON string `{mcpServers:{name:{command,args,env,connectionTimeoutMs?}}}`
|
|
10
11
|
// (buildGatherMcpConfig → JSON.stringify)
|
|
@@ -36,17 +37,8 @@ export const CRED_ENV_FORWARD = [
|
|
|
36
37
|
];
|
|
37
38
|
|
|
38
39
|
// ── TOML value escaping (basic strings) ──────────────────────────────────────────────────────────────
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
// TOML basic-string escapes: backslash, double-quote, and the C0 controls TOML names (\b \t \n \f \r).
|
|
42
|
-
const esc = str
|
|
43
|
-
.replace(/\\/g, "\\\\").replace(/"/g, '\\"')
|
|
44
|
-
.replace(/\x08/g, "\\b").replace(/\t/g, "\\t").replace(/\n/g, "\\n")
|
|
45
|
-
.replace(/\f/g, "\\f").replace(/\r/g, "\\r")
|
|
46
|
-
// any remaining control char → \uXXXX (TOML-legal)
|
|
47
|
-
.replace(/[\x00-\x1f]/g, (c) => "\\u" + c.charCodeAt(0).toString(16).padStart(4, "0"));
|
|
48
|
-
return `"${esc}"`;
|
|
49
|
-
}
|
|
40
|
+
// One encoder for every TOML block the product writes; re-exported so this module's callers keep their import.
|
|
41
|
+
export { tomlString };
|
|
50
42
|
function tomlStringArray(arr) {
|
|
51
43
|
return "[" + (arr || []).map(tomlString).join(", ") + "]";
|
|
52
44
|
}
|
|
@@ -212,7 +212,7 @@ const RECORDING = Object.freeze({
|
|
|
212
212
|
// SECOND — skeptic, same shape ( is the template). Its OWN key, so blind-frame's seat is never
|
|
213
213
|
// handed record_skeptic and skeptic is never handed record_blind_frame.
|
|
214
214
|
//
|
|
215
|
-
// skeptic's key carries a SECOND tool: search_run_artifacts, the sanctioned read surface (
|
|
215
|
+
// skeptic's key carries a SECOND tool: search_run_artifacts, the sanctioned read surface (the
|
|
216
216
|
// ratification hold, unlock path 1). A READ over the run's own tree, not retrieval — it dials nothing,
|
|
217
217
|
// writes nothing, and is bounded to CLEAROTRON_BAND_RUN_DIR by construction (skeptic-search.mjs). It
|
|
218
218
|
// replaces the artifact half of the Bash reads O3c measured; the skill-doc half was already the seeded
|
|
@@ -60,8 +60,8 @@ const codexBin = () => { const r = resolveEngineProgram("openai-agent"); return
|
|
|
60
60
|
// 3 stage families, and BYTE-IDENTICALLY on retry. That was true of that code and that CLI.
|
|
61
61
|
//
|
|
62
62
|
// What has changed is not the measurement but everything around it: three weeks of stage-contract
|
|
63
|
-
// work, and a codex CLI that has moved several minor versions. The ruling above supersedes 's
|
|
64
|
-
// conclusion; it does not erase its evidence. A reader who finds first must be able to get here.
|
|
63
|
+
// work, and a codex CLI that has moved several minor versions. The ruling above supersedes the measurement's
|
|
64
|
+
// conclusion; it does not erase its evidence. A reader who finds the measurement first must be able to get here.
|
|
65
65
|
//
|
|
66
66
|
// ── HOW A REGRESSION ANNOUNCES ITSELF — no experiment is scheduled, and none is needed ────────────
|
|
67
67
|
//
|
|
@@ -578,6 +578,18 @@ const clipClause = (s, max) => {
|
|
|
578
578
|
* count authority and the fallback. Returns null when either axis is missing (legacy sidecars —
|
|
579
579
|
* callers fall back to today's bare word). PURE.
|
|
580
580
|
*/
|
|
581
|
+
/**
|
|
582
|
+
* The conditions a client is actually told a CONDITIONAL verdict rests on: the client-voice clauses, and
|
|
583
|
+
* the reasons as their fallback. A clause stored as explicit null is a condition ruled to the run record
|
|
584
|
+
* alone, so its reason is not stated either; `undefined` (legacy/short) is not null. PURE.
|
|
585
|
+
*/
|
|
586
|
+
export function statedConditions({ reasons, clauses } = {}) {
|
|
587
|
+
const cs = Array.isArray(clauses) ? clauses : [];
|
|
588
|
+
const conds = (Array.isArray(reasons) ? reasons : []).filter((r, i) => cs[i] !== null).map((r) => String(r ?? "").trim()).filter(Boolean);
|
|
589
|
+
const cls = cs.map((c) => String(c ?? "").trim()).filter(Boolean);
|
|
590
|
+
return { conds, cls };
|
|
591
|
+
}
|
|
592
|
+
|
|
581
593
|
export function riskStatement({ tier, verdict, reasons, basis, clauses } = {}) {
|
|
582
594
|
const v = String(verdict || "").toUpperCase();
|
|
583
595
|
const t = String(tier || "").trim();
|
|
@@ -592,10 +604,13 @@ export function riskStatement({ tier, verdict, reasons, basis, clauses } = {}) {
|
|
|
592
604
|
if (v === "CONDITIONAL") {
|
|
593
605
|
// A clause stored as explicit null is a condition ruled to the run record alone: it is not the lede,
|
|
594
606
|
// it is not counted, and its reason is never the fallback text. `undefined` (legacy/short) is not null.
|
|
595
|
-
const
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
607
|
+
const { conds, cls } = statedConditions({ reasons, clauses });
|
|
608
|
+
// NOTHING STATED, NO "CONDITIONAL ON:" LINE (owner, 2026-09-19). When every condition is ruled to
|
|
609
|
+
// the run record, the line had nothing behind it and fell back to "the open conditions carried in the
|
|
610
|
+
// report": a conditional with no condition. The band word stands alone, and `stance` still says
|
|
611
|
+
// conditional to every consumer that keys on it.
|
|
612
|
+
if (!cls.length && !conds.length) return `${t}${basisNote ? `.${basisNote}` : ""}`;
|
|
613
|
+
const lede = cls[0] ?? conds[0];
|
|
599
614
|
const first = clipClause(sentenceCaseLead(lede), STATEMENT_CLAUSE_MAX);
|
|
600
615
|
const n = conds.length || cls.length;
|
|
601
616
|
const more = n > 1 ? ` (and ${n - 1} more)` : "";
|
|
@@ -1228,7 +1243,7 @@ export function parseFindingsJsonLenient(raw, opts = {}) {
|
|
|
1228
1243
|
// and drops the others, so a run's entire non-Latin field can collapse into one finding whose prose
|
|
1229
1244
|
// describes one of them.
|
|
1230
1245
|
//
|
|
1231
|
-
// The fix
|
|
1246
|
+
// The fix, at a third site: keep the Latin normalisation exactly as it was where it produces
|
|
1232
1247
|
// anything, and fall back to the RAW value (NFKC-folded, so full-width and compatibility forms of the
|
|
1233
1248
|
// same characters still key alike) where it does not. Latin keys are byte-identical to before — pinned
|
|
1234
1249
|
// by test, because a consolidation key that moved would re-merge every archived finding differently.
|
|
@@ -1721,7 +1736,7 @@ function validateNet(f, ord, mode) {
|
|
|
1721
1736
|
// (publish/index.mjs strict-parses the archived findings.json), and a delivered matter would stop
|
|
1722
1737
|
// republishing. So it engages at v7 and archived records parse byte-identically, forever.
|
|
1723
1738
|
//
|
|
1724
|
-
// THE LENIENT PATH IS EXEMPT, for validateNetRequired's reason
|
|
1739
|
+
// THE LENIENT PATH IS EXEMPT, for validateNetRequired's reason and one of its own. Dropping a
|
|
1725
1740
|
// finding because its sentence is shaped wrong would delete a real conflict over a punctuation mark —
|
|
1726
1741
|
// silence arrived at by enforcing a rule about clarity. It also keeps the token out of pipeline.mjs's A3
|
|
1727
1742
|
// per-finding salvage lane, which has nothing to salvage here (see the token's own note below).
|
package/driver/gateway.mjs
CHANGED
|
@@ -1146,7 +1146,7 @@ async function runStageLadder(name, opts, stageCodexHome = null) {
|
|
|
1146
1146
|
// `resolveModel(model)` / `engine.resolveModelId(model)`, computed here without ever looking at the
|
|
1147
1147
|
// provider's answer. So `--model gemini` logged `google/gemini-3.1-pro-preview` and ran sonnet (the
|
|
1148
1148
|
// engine's alias table substituted, silently), a haiku stage that bounced to sonnet logged haiku,
|
|
1149
|
-
// and every attribution downstream — the A/B arm's manifest, tokens.mjs' per-model rollup,
|
|
1149
|
+
// and every attribution downstream — the A/B arm's manifest, tokens.mjs' per-model rollup, the
|
|
1150
1150
|
// billing classes — was keyed to a model that had not run. Patching the log would not have fixed
|
|
1151
1151
|
// it; the log was already saying what the code believed.
|
|
1152
1152
|
//
|
package/driver/package.json
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
"name": "clearotron-driver",
|
|
3
3
|
"private": true,
|
|
4
4
|
"type": "module",
|
|
5
|
-
"version": "0.3.2-beta.
|
|
5
|
+
"version": "0.3.2-beta.14",
|
|
6
6
|
"license": "AGPL-3.0-only",
|
|
7
7
|
"description": "Deterministic driver for the trademark clearance workflow: orchestration in code (fan-out, fan-in barrier, gating, retries); the model does judgment leaves only, through a reasoning CLI spawned per stage.",
|
|
8
8
|
"engines": {
|
|
@@ -540,7 +540,7 @@ export async function knockoutInner(ctx, job, opts = {}) {
|
|
|
540
540
|
//
|
|
541
541
|
// ONLY when the counter is the real provider. On fixtures or an injected counter the offices this
|
|
542
542
|
// deployment can reach are not what the lane is talking to, and splitting on them would defer a
|
|
543
|
-
// register the fixture holds — a $0 lane reporting a gap it does not have (the shape
|
|
543
|
+
// register the fixture holds — a $0 lane reporting a gap it does not have (the shape a
|
|
544
544
|
// sibling defect had, and the reason `missing` carries the same guard).
|
|
545
545
|
const unreachable = countExec.source === "provider" ? registerUnavailableOffices(caps) : [];
|
|
546
546
|
ctx.registerUnreachable = unreachable;
|
package/driver/pipeline.mjs
CHANGED
|
@@ -4818,7 +4818,7 @@ export function terminalReasonFields(reason, { cap = 200, fullCap = 4000 } = {})
|
|
|
4818
4818
|
// `_driver/failure.json` carried reason/reasonVerbatim/failClass/terminalKind and no payload at all, so the
|
|
4819
4819
|
// operator was told one query had dropped and never which one — E2E had to replay
|
|
4820
4820
|
// findDroppedConnotationQueries against the preserved artifacts to name it. `reasonDetail` already reached
|
|
4821
|
-
// status.json
|
|
4821
|
+
// status.json; `reasonQuantity` reached neither file.
|
|
4822
4822
|
//
|
|
4823
4823
|
// Both are written UNCONDITIONALLY as string|null and number|null. Never omitted, never 0: "no key" and
|
|
4824
4824
|
// "this failure carries no count" are different facts, the same distinction `reasonTruncated` is written on
|
|
@@ -7929,7 +7929,7 @@ export function buildAskAnswersSection(askAnswers, intakeAsks) { // @internal
|
|
|
7929
7929
|
* change to the delivered report nobody asked for. So the divergence is now one-country-only and owned,
|
|
7930
7930
|
* rather than general and deferred.
|
|
7931
7931
|
*
|
|
7932
|
-
* This section itself is untouched by the ladder: the grouping is not extended and not re-keyed.
|
|
7932
|
+
* This section itself is untouched by the ladder: the grouping is not extended and not re-keyed. The
|
|
7933
7933
|
* graded entries render in a SIBLING section (buildGradedEntriesSection) using the same line grammar.
|
|
7934
7934
|
*/
|
|
7935
7935
|
export function buildReasonedNegativesSection(findings) { // @internal
|
|
@@ -15696,8 +15696,8 @@ async function pipelineInner(job, opts = {}) {
|
|
|
15696
15696
|
// — `reasonDetail` rides beside `reason`, never inside it. abbrev, not a bare slice: a cut that
|
|
15697
15697
|
// reads as a word is worse than one that reads as a cut, and this one is read by an engineer
|
|
15698
15698
|
// chasing a defect.
|
|
15699
|
-
// — one spelling of the payload for BOTH sinks. status.json already carried `reasonDetail
|
|
15700
|
-
//
|
|
15699
|
+
// — one spelling of the payload for BOTH sinks. status.json already carried `reasonDetail`;
|
|
15700
|
+
// `_driver/failure.json` carried neither it nor the count, and `reasonQuantity` reached
|
|
15701
15701
|
// no file at all — so the operator read "dropped 1 dictated connotation query" and had to replay the
|
|
15702
15702
|
// gate against the preserved artifacts to learn which one. Derived once here and passed to both
|
|
15703
15703
|
// writers, so the notice and the status file can never disagree about the payload.
|
|
@@ -68,7 +68,7 @@ function indexRows(rows, { bySelection = true, uriLeg = true } = {}) {
|
|
|
68
68
|
/**
|
|
69
69
|
* Union the seat's tiers into what the driver already holds.
|
|
70
70
|
*
|
|
71
|
-
* THE RULE, and why it is that way round — identical to
|
|
71
|
+
* THE RULE, and why it is that way round — identical to coverage-union.mjs and disposition-union.mjs, for identical reasons:
|
|
72
72
|
* · a SUBMITTED row the gate would accept wins, so the seat can CORRECT itself;
|
|
73
73
|
* · otherwise a PRIOR row the gate would accept is kept, so a killed or cold attempt cannot destroy
|
|
74
74
|
* work already done;
|
|
@@ -76,7 +76,7 @@ function post(urlStr, { headers = {}, body = "", wantId = null, timeoutMs = 3000
|
|
|
76
76
|
data += c;
|
|
77
77
|
// SSE early-exit: once the frame with our id is complete, we have the answer — stop reading
|
|
78
78
|
if (wantId != null && String(res.headers["content-type"] ?? "").includes("text/event-stream")
|
|
79
|
-
&& new RegExp(`"id"
|
|
79
|
+
&& new RegExp(`"id"\\s*:\\s*${wantId}`).test(data) && /\n\n/.test(data)) { res.destroy(); done(); }
|
|
80
80
|
});
|
|
81
81
|
res.on("end", done);
|
|
82
82
|
res.on("close", done);
|
package/driver/portal-report.mjs
CHANGED
|
@@ -850,7 +850,10 @@ export function sectionsOf(html) {
|
|
|
850
850
|
if (!nav) return [];
|
|
851
851
|
const out = [];
|
|
852
852
|
for (const m of nav[1].matchAll(STRIP_LINK_RE)) {
|
|
853
|
-
|
|
853
|
+
// Stripped until nothing changes, so a tag that a first pass reassembles cannot survive into the label.
|
|
854
|
+
let label = m[2];
|
|
855
|
+
for (let prev = null; prev !== label;) { prev = label; label = label.replace(/<[^>]*>/g, ""); }
|
|
856
|
+
label = label.replace(/\s+/g, " ").trim();
|
|
854
857
|
if (m[1] && label) out.push({ id: m[1], label });
|
|
855
858
|
}
|
|
856
859
|
return out;
|