clearotron 0.3.2-beta.4 → 0.3.2-beta.6
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/INSTALL.md +1 -0
- package/bin/onboard.mjs +65 -0
- package/bin/start.mjs +14 -0
- package/build-info.json +2 -2
- package/demo/knockout-search/run/_driver/predelivery-lint.json +26 -2
- package/driver/CHANGELOG.md +16 -0
- package/driver/common-law-receipts.mjs +26 -0
- package/driver/contract-e3-backlog.mjs +3 -3
- package/driver/contract-vocabulary.mjs +6 -6
- package/driver/package.json +1 -1
- package/driver/pipeline.mjs +4 -2
- package/driver/publish/render-knockout.mjs +231 -98
- package/driver/publish/render.mjs +399 -152
- package/driver/publish/templates/report.css +103 -3
- package/driver/registry-fidelity.mjs +28 -0
- package/driver/stages.mjs +3 -3
- package/driver/suite-census.json +31 -19
- package/driver/verify.mjs +27 -1
- package/mcp-server/CHANGELOG.md +8 -0
- package/mcp-server/lib/audit.mjs +14 -2
- package/mcp-server/lib/http-handler.mjs +52 -13
- package/mcp-server/lib/scrub.mjs +1 -1
- package/mcp-server/package.json +1 -1
- package/package.json +1 -1
- package/portal-ui/package.json +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/shared/driver-dir.mjs +1 -1
package/INSTALL.md
CHANGED
|
@@ -331,6 +331,7 @@ terminal and is rewritten on every start.
|
|
|
331
331
|
~/trademark/config/profiles the company profiles
|
|
332
332
|
~/trademark/grants.json who may use this install
|
|
333
333
|
~/trademark/portal-audit.log what the portal was asked to do
|
|
334
|
+
~/trademark/telemetry/trademark-mcp-access.jsonl what a connected assistant asked for, and what it was told
|
|
334
335
|
~/trademark/portal-local-credential.json the sign-in this install minted for you
|
|
335
336
|
```
|
|
336
337
|
|
package/bin/onboard.mjs
CHANGED
|
@@ -2702,6 +2702,71 @@ export async function runCheck() {
|
|
|
2702
2702
|
+ `Run \`${invoke("start")}\` — it creates the list.`);
|
|
2703
2703
|
}
|
|
2704
2704
|
}
|
|
2705
|
+
// ── IS THIS DOOR'S ACCESS LOG BEING WRITTEN, AND WHERE? ───────────────────────────────────────────
|
|
2706
|
+
//
|
|
2707
|
+
// A client's assistant can list runs, read reports and pull artifacts through this door. The record of
|
|
2708
|
+
// what a key did is the only way to answer a client, a lawyer or an incident review afterwards, and a
|
|
2709
|
+
// log nobody can find is a log nobody checks.
|
|
2710
|
+
//
|
|
2711
|
+
// THE OVERRIDE IS READ FROM THE UNIT, NOT FROM THIS PROCESS. `TRADEMARK_MCP_AUDIT_LOG` decides where
|
|
2712
|
+
// the writer appends, and the writer is the service — so this command's own environment is not the
|
|
2713
|
+
// answer and using it would produce a confident report about a file the door never touches. Where the
|
|
2714
|
+
// two disagree, that disagreement is the finding and is said out loud rather than resolved silently.
|
|
2715
|
+
//
|
|
2716
|
+
// WITH NO OVERRIDE THERE ARE TWO CANDIDATES, and which one the writer picked depends on what existed
|
|
2717
|
+
// when IT started — a legacy path is kept if it is already there, so that a compliance trail is not
|
|
2718
|
+
// split in two by a moved default. `auditPaths()` is the module's own reader for exactly this and is
|
|
2719
|
+
// called rather than re-derived; re-deriving that branch here is the defect its source warns about.
|
|
2720
|
+
try {
|
|
2721
|
+
const { auditPaths: auditCandidates } = await import(pathToFileURL(join(REPO, "mcp-server", "lib", "audit.mjs")).href);
|
|
2722
|
+
const seen = hosted ? unitValue(unitEnv, "TRADEMARK_MCP_AUDIT_LOG") : { state: "unset", value: null };
|
|
2723
|
+
if (seen.state === "unknown") {
|
|
2724
|
+
info(couldNotDetermine("TRADEMARK_MCP_AUDIT_LOG", unitEnv) + " The access log's location cannot be named.");
|
|
2725
|
+
} else {
|
|
2726
|
+
const mine = String(process.env.TRADEMARK_MCP_AUDIT_LOG ?? "").trim();
|
|
2727
|
+
if (seen.state === "unset" && mine) {
|
|
2728
|
+
warn(`this shell sets TRADEMARK_MCP_AUDIT_LOG and the units do not, so the door is NOT writing where `
|
|
2729
|
+
+ `this command would look. The door's log is at its default; ${mine} is this shell's answer only.`);
|
|
2730
|
+
}
|
|
2731
|
+
const paths = seen.state === "set" ? [seen.value] : auditCandidates();
|
|
2732
|
+
let wrote = null;
|
|
2733
|
+
for (const p of paths) {
|
|
2734
|
+
try { const s = statSync(p); if (s.size > 0) { wrote = p; break; } } catch { /* not this one */ }
|
|
2735
|
+
}
|
|
2736
|
+
if (wrote) {
|
|
2737
|
+
ok(`the client door's access log is being written: ${wrote}`);
|
|
2738
|
+
} else {
|
|
2739
|
+
// NOT-YET-WRITTEN IS NOT UNWRITABLE. A door nobody has called through has no log, and saying it
|
|
2740
|
+
// is broken would be the same lie as calling an unreachable unit inactive.
|
|
2741
|
+
//
|
|
2742
|
+
// NAME THE ONE THE WRITER WOULD PICK, WHICH IS NOT SIMPLY THE FIRST. With no override there are
|
|
2743
|
+
// two candidates and the older is kept only when it is ALREADY THERE, so that a compliance trail
|
|
2744
|
+
// is not split by a moved default. Naming the older one unconditionally would print the
|
|
2745
|
+
// integrator platform's folder at setup on installs that have no such file — a folder this engine
|
|
2746
|
+
// does not require and whose mention was deliberately removed from this command. Where it does
|
|
2747
|
+
// exist it IS the log and is named, because that is then the true answer.
|
|
2748
|
+
const existing = paths.find((c) => { try { statSync(c); return true; } catch { return false; } });
|
|
2749
|
+
const willUse = existing ?? paths[paths.length - 1];
|
|
2750
|
+
const dir = dirname(willUse);
|
|
2751
|
+
let writable = null;
|
|
2752
|
+
try { accessSync(dir, constants.W_OK); writable = true; } catch (e) { writable = e?.code === "ENOENT" ? null : false; }
|
|
2753
|
+
if (writable === false) {
|
|
2754
|
+
blocking(`the client door's access log cannot be written: ${dir} is not writable by this user. `
|
|
2755
|
+
+ "Every call through this door would go unrecorded, and an absent record reads as nothing happened.");
|
|
2756
|
+
} else if (writable === null) {
|
|
2757
|
+
info(`no access log yet at ${willUse} and its directory does not exist — the door creates it on the `
|
|
2758
|
+
+ "first call. Nothing has come through it, or nothing has since the path last changed.");
|
|
2759
|
+
} else {
|
|
2760
|
+
info(`no access log yet at ${willUse}, and its directory is writable — nothing has come through `
|
|
2761
|
+
+ "this door yet. The first call creates it.");
|
|
2762
|
+
}
|
|
2763
|
+
}
|
|
2764
|
+
}
|
|
2765
|
+
} catch (e) {
|
|
2766
|
+
info(`could not check the client door's access log (${e?.code ?? e?.message ?? e}) — this is a report `
|
|
2767
|
+
+ "that this command could not look, not that the log is missing.");
|
|
2768
|
+
}
|
|
2769
|
+
|
|
2705
2770
|
try {
|
|
2706
2771
|
const { clientDoorState: doorState, describeDoorState, connectKeyReport, CLIENT_DOOR_UNIT: doorUnit } = await import(pathToFileURL(join(REPO, "shared", "client-door.mjs")).href);
|
|
2707
2772
|
const { loadGrants: readGrantsFile, isRevoked: revokedCheck } = await import(pathToFileURL(join(REPO, "shared", "scope.mjs")).href);
|
package/bin/start.mjs
CHANGED
|
@@ -449,6 +449,10 @@ export function installPaths(base) {
|
|
|
449
449
|
locks: join(base, "locks"),
|
|
450
450
|
grants: join(base, "grants.json"),
|
|
451
451
|
audit: join(base, "portal-audit.log"),
|
|
452
|
+
// The connector doors' access log — the record of what a client key did, which is what answers a
|
|
453
|
+
// client or a review afterwards. NAMED IN THE LAYOUT rather than built at the call site, for the
|
|
454
|
+
// reason the store below gives: a demo's isolation must be a property of where things live.
|
|
455
|
+
accessLog: join(base, "telemetry", "trademark-mcp-access.jsonl"),
|
|
452
456
|
// The saved-search store is a git repository because recipe-service commits every save through
|
|
453
457
|
// `git add` + `git commit` — that is the product's config-store model, not something invented here.
|
|
454
458
|
// It is its own directory, NOT `base`, so the commit path never sees the pool, the queue or the
|
|
@@ -680,6 +684,16 @@ export function childEnv({ ports, paths, user, portalSecret, tokenSecret, opsTok
|
|
|
680
684
|
"PROFILE_AUDIT": "",
|
|
681
685
|
"RECIPE_AUDIT": "",
|
|
682
686
|
"CLEAROTRON_FEEDBACK_DIR": "",
|
|
687
|
+
// AN EXPLICIT PATH, NOT "" LIKE THE TWO ABOVE. Empty is unset, and unset is what sends this one to
|
|
688
|
+
// the REAL install's home: its default is computed from `homedir()`, not from any directory a demo
|
|
689
|
+
// controls. So a demo left to fall back writes its access lines into the install beside it, and the
|
|
690
|
+
// demo stops being one directory a reader can delete. Measured as exactly that: a demo run left
|
|
691
|
+
// `telemetry/trademark-mcp-access.jsonl` in the install it booted next to.
|
|
692
|
+
//
|
|
693
|
+
// ONLY IN A DEMO. A real install must NOT have this pinned: with no override the writer keeps an
|
|
694
|
+
// older log where one already exists, so that moving the default does not split a compliance trail
|
|
695
|
+
// in two. Pinning the name here would override that and do the splitting.
|
|
696
|
+
"TRADEMARK_MCP_AUDIT_LOG": paths.accessLog,
|
|
683
697
|
} : {}),
|
|
684
698
|
});
|
|
685
699
|
return {
|
package/build-info.json
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
{
|
|
2
|
-
"ts": "2026-09-
|
|
2
|
+
"ts": "2026-09-16T16:48:57.832Z",
|
|
3
3
|
"lane": "knockout",
|
|
4
4
|
"checks": [
|
|
5
5
|
{
|
|
@@ -43,9 +43,33 @@
|
|
|
43
43
|
"surface": "findings",
|
|
44
44
|
"pass": true,
|
|
45
45
|
"detail": ""
|
|
46
|
+
},
|
|
47
|
+
{
|
|
48
|
+
"id": "reviewer-note-subject",
|
|
49
|
+
"family": "voice",
|
|
50
|
+
"surface": "findings",
|
|
51
|
+
"pass": true,
|
|
52
|
+
"detail": ""
|
|
53
|
+
},
|
|
54
|
+
{
|
|
55
|
+
"id": "plain-language-vocabulary",
|
|
56
|
+
"family": "voice",
|
|
57
|
+
"surface": "findings",
|
|
58
|
+
"pass": false,
|
|
59
|
+
"detail": "the lawyer's vocabulary on lines a reader meets before opening anything — rewrite the sentence in the words the reader already owns, do not swap the word: the batch summary: \"belt-and-braces\" → \"extra\"; the batch summary: \"chunk\" (an engine word — cut it)"
|
|
60
|
+
},
|
|
61
|
+
{
|
|
62
|
+
"id": "plain-language-sentence-length",
|
|
63
|
+
"family": "voice",
|
|
64
|
+
"surface": "findings",
|
|
65
|
+
"pass": false,
|
|
66
|
+
"detail": "a default-visible sentence carrying more than one idea (over 25 words) — split it, conclusion first: the batch summary: 28 words; the batch summary: 28 words; the batch summary: 27 words; the batch summary: 39 words; the batch summary: 26 words; +4 more"
|
|
46
67
|
}
|
|
47
68
|
],
|
|
48
|
-
"failures": [
|
|
69
|
+
"failures": [
|
|
70
|
+
"plain-language-vocabulary",
|
|
71
|
+
"plain-language-sentence-length"
|
|
72
|
+
],
|
|
49
73
|
"notApplicable": [
|
|
50
74
|
{
|
|
51
75
|
"id": "names-cell-populated",
|
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.3.2-beta.6
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 911b0b4: New: The report a client opens has been redrawn. It opens with what was asked, then the rating with its reasons, then the conflicts. What a search is and is not is no longer narrated at length.
|
|
8
|
+
|
|
9
|
+
New: A finding's full detail shows the goods as the register recorded them, and the record's own dates. This is on a full country search.
|
|
10
|
+
|
|
11
|
+
New: A knockout report closes with what happens next. The paragraph moves out of the long read rather than being repeated.
|
|
12
|
+
|
|
13
|
+
## 0.3.2-beta.5
|
|
14
|
+
|
|
15
|
+
### Patch Changes
|
|
16
|
+
|
|
17
|
+
- a4ea94d: For operators: The connector access log now records what happened to each call, and records calls that were refused as well as calls that got through. A line also names which door took the call, so a client key and a staff session can be told apart. Before, a call was recorded only as having been made, and a refused one left no line at all. `doctor` now names where that log is being written. It reads that from the service's own settings, not from the shell you are typing in.
|
|
18
|
+
|
|
3
19
|
## 0.3.2-beta.4
|
|
4
20
|
|
|
5
21
|
### Patch Changes
|
|
@@ -611,6 +611,32 @@ export function findDroppedConnotationQueries(spec, mergedGrid) {
|
|
|
611
611
|
* Deliberately NOT counted as searched: laundering an honest error into a clean receipt would be a worse
|
|
612
612
|
* defect than the one being fixed.
|
|
613
613
|
*/
|
|
614
|
+
/**
|
|
615
|
+
* Of a caller's OWN list of missing queries, which ones did the plugin honestly report as having thrown?
|
|
616
|
+
*
|
|
617
|
+
* The sibling below derives its missing list itself, from the full spec. This one is handed one, because
|
|
618
|
+
* the meaning-seat gate computes a different list: it joins per half on `queryKey` where this module
|
|
619
|
+
* joins the merged grid on `norm`. Two callers, two populations, ONE author of what a gap row means —
|
|
620
|
+
* which is the point of the split. Reuse the key, not the question.
|
|
621
|
+
*
|
|
622
|
+
* Returns the CALLER'S query verbatim, never the gap row's term, so the caller can index what comes back
|
|
623
|
+
* against the list it passed in. A gap term that matched is by definition the same query under `norm`,
|
|
624
|
+
* and handing back the other spelling would make the caller join twice on two keys to use the answer.
|
|
625
|
+
*/
|
|
626
|
+
export function erroredConnotationQueriesAmong(missingQueries, mergedGrid) {
|
|
627
|
+
const gaps = (Array.isArray(mergedGrid?.gaps) ? mergedGrid.gaps : [])
|
|
628
|
+
.filter((g) => g && String(g.platform ?? "").toLowerCase() === "connotation");
|
|
629
|
+
if (!gaps.length) return [];
|
|
630
|
+
const out = [];
|
|
631
|
+
for (const m of missingQueries ?? []) {
|
|
632
|
+
const k = norm(String(m ?? ""));
|
|
633
|
+
if (!k) continue;
|
|
634
|
+
const hit = gaps.find((g) => norm(String(g.term ?? "").trim()) === k);
|
|
635
|
+
if (hit) out.push({ query: m, error: String(hit.error ?? "unspecified") });
|
|
636
|
+
}
|
|
637
|
+
return out;
|
|
638
|
+
}
|
|
639
|
+
|
|
614
640
|
export function findErroredConnotationQueries(spec, mergedGrid) {
|
|
615
641
|
const missing = new Set(findDroppedConnotationQueries(spec, mergedGrid).map(norm));
|
|
616
642
|
if (!missing.size) return [];
|
|
@@ -488,7 +488,7 @@ export const E3_BACKLOG = [
|
|
|
488
488
|
where: "driver/stages.mjs:3231",
|
|
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:813 parseFindingsJson; driver/verify.mjs:
|
|
491
|
+
reparsedBy: "driver/findings-model.mjs:813 parseFindingsJson; driver/verify.mjs:1153 checkFindingsSibling gates meters.*.source; finding_basis_source_missing",
|
|
492
492
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
493
493
|
},
|
|
494
494
|
{
|
|
@@ -684,7 +684,7 @@ export const E3_BACKLOG = [
|
|
|
684
684
|
// because an un-anchorable row lands in the NOT-CHECKED slice, and that is coverage lost rather than
|
|
685
685
|
// a pass.
|
|
686
686
|
evidence: "is ONE of coverage-disposition | fact | rating | narrative — pick the one your own legal read says the correction IS: coverage-disposition (a coverage row / disposition placement is wrong or dishonest)",
|
|
687
|
-
reparsedBy: "driver/verify.mjs:
|
|
687
|
+
reparsedBy: "driver/verify.mjs:829 CORRECTION_KIND_RE = /\\[kind:\\s*([a-z][a-z-]*)\\s*\\]/i → parseCorrectionKinds (verify.mjs:1052), consumed in pipelineInner() in pipeline.mjs for the run.jsonl `correction-kinds` histogram, which since #1558 also carries `kindChannelOk` — the counts are DERIVED from the parsed rows, and that key states whether the reviewer's kind channel produced anything at all rather than leaving a reader to infer it by comparing untyped against total. Telemetry only today",
|
|
688
688
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
689
689
|
},
|
|
690
690
|
{
|
|
@@ -697,7 +697,7 @@ export const E3_BACKLOG = [
|
|
|
697
697
|
// `[on: -]` case went from a value to an ABSENCE — you omit the field — which is the one part a
|
|
698
698
|
// reader could get wrong from the old wording, since there is no value meaning "no finding".
|
|
699
699
|
evidence: "**AND EVERY FLAG CARRIES WHICH FINDING IT IS ABOUT** — the `on` field, an array of ordinals. Same rule as `kind`: you send the values, the driver renders the token.",
|
|
700
|
-
reparsedBy: "driver/verify.mjs:
|
|
700
|
+
reparsedBy: "driver/verify.mjs:846 CORRECTION_ON_RE = /\\[on:\\s*([0-9,\\s-]*?)\\s*\\]/i. SKILL-FILE ONLY — the stage message at stages.mjs:1842-1877 never mentions `[on:]`. This is #850's \"the element shape is in the skill file, not the stage message\" in its purest form: an E3 lint reading stages.mjs alone sees the [kind:] token and misses its twin",
|
|
701
701
|
removedByMove: "NOTHING ON THE #850 PLAN REMOVES THIS",
|
|
702
702
|
},
|
|
703
703
|
{
|
|
@@ -17,12 +17,12 @@
|
|
|
17
17
|
//
|
|
18
18
|
// D1 verify.mjs:940 fail(`connotation_${reason}:…`) — reason iterates the table at line 894
|
|
19
19
|
// D2 verify.mjs fail(String(e.message)) — parseFindingsJson throws token-first
|
|
20
|
-
// D3 verify.mjs:
|
|
20
|
+
// D3 verify.mjs:1210 checkJson: fail(String(e.message)) — FIVE parsers reach this one site
|
|
21
21
|
// D4 verify.mjs parseCoverageLedgerJson, same shape
|
|
22
22
|
// D5 verify.mjs:1504 fail(`${unaccounted[0].token}:…`) — token minted in a DATA ROW
|
|
23
23
|
// D6 verify.mjs:1567 fail(`${violations[0].token}…`) — validatePlanFeasibility in register-plan.mjs
|
|
24
24
|
// D7 verify.mjs:1558 fail(`${v2[0].token}${detail}…`) — register-plan.mjs:2018 disclosureTextByAxis
|
|
25
|
-
// D8 verify.mjs:
|
|
25
|
+
// D8 verify.mjs:2425 caseLawLedgerFail fail(caseLawLedgerFail(…)) — token built in case-law-ledger.mjs:204 caseLawLedgerFail
|
|
26
26
|
//
|
|
27
27
|
// A partition built on the 60 tokens a regex CAN see would run green while blind to the rest, which is
|
|
28
28
|
// worse than having no E2 at all: it certifies a partition it never checked. So the census is authored
|
|
@@ -109,7 +109,7 @@ export const VOCABULARY = [
|
|
|
109
109
|
{ token: "coverage_form_missing", stages: ["register-digest"], site: "driver/verify.mjs" },
|
|
110
110
|
{ token: "coverage_form_empty", stages: ["register-digest"], site: "driver/verify.mjs" },
|
|
111
111
|
{ token: "coverage_status_offenum", stages: ["register-digest"], site: "driver/verify.mjs:2050" },
|
|
112
|
-
{ token: "coverage_deferred_unaccounted", stages: ["register-digest"], site: "driver/verify.mjs:
|
|
112
|
+
{ token: "coverage_deferred_unaccounted", stages: ["register-digest"], site: "driver/verify.mjs:1536 coverageFormFail", family: "driver/register-plan.mjs:1642 PROVIDER_HARD_ERROR_PREFIX — token on a data row", dynamic: "D5" },
|
|
113
113
|
{ token: "coverage_clean_unexecuted", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs:1417 validatePlanFeasibility", dynamic: "D6" },
|
|
114
114
|
{ token: "coverage_clean_skipped", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs:1766 searchedJurisdictionsFromPlan", dynamic: "D6" },
|
|
115
115
|
{ token: "coverage_clean_unverified_incomplete", stages: ["register-digest"], site: "driver/verify.mjs, the matterContext validator", family: "driver/register-plan.mjs:2018 disclosureTextByAxis", dynamic: "D7" },
|
|
@@ -130,7 +130,7 @@ export const VOCABULARY = [
|
|
|
130
130
|
{ token: "variantmodel_term_markup", stages: ["prelim-variants"], site: "driver/verify.mjs" },
|
|
131
131
|
{ token: "variantmodel_missing", stages: ["prelim-variants"], site: "driver/verify.mjs:1153, 1212" },
|
|
132
132
|
// Recovered during E2 authoring, absent from the draft census: variant-manifest.json is strict-parsed
|
|
133
|
-
// through checkSiblingJson (verify.mjs:
|
|
133
|
+
// through checkSiblingJson (verify.mjs:1223) → checkJson (:742), so the WHOLE variantmodel_* family
|
|
134
134
|
// reaches prelim-variants, not just the four literal tokens above.
|
|
135
135
|
// CONVERSION 3 widened this family's SOURCE without widening its prefix. `acceptPrelimVariants` raises
|
|
136
136
|
// `variantmodel_scope_layer_invalid`, `_scope_status_invalid`, `_scope_item_missing` and `_scope_pipe`
|
|
@@ -184,7 +184,7 @@ export const VOCABULARY = [
|
|
|
184
184
|
// ── case-law / narrative-refutation ────────────────────────────────────────────────────────────────
|
|
185
185
|
{ token: "caselaw_ledger_missing", stages: ["case-law"], site: "driver/verify.mjs:1682, 1685" },
|
|
186
186
|
{ token: "caselaw_ledger_unparseable", stages: ["case-law"], site: "driver/verify.mjs" },
|
|
187
|
-
{ token: "caselaw_ledger", stages: ["case-law"], site: "driver/verify.mjs:
|
|
187
|
+
{ token: "caselaw_ledger", stages: ["case-law"], site: "driver/verify.mjs:2425 caseLawLedgerFail", family: "driver/case-law-ledger.mjs caseLawLedgerFail (census reasons)", dynamic: "D8" },
|
|
188
188
|
{ token: "no_verdict_line", stages: ["narrative-refutation"], site: "driver/verify.mjs" },
|
|
189
189
|
{ token: "plan_audit_missing", stages: ["narrative-refutation"], site: "driver/verify.mjs" },
|
|
190
190
|
|
|
@@ -477,7 +477,7 @@ export const INNER_CODES = Object.freeze([
|
|
|
477
477
|
// `axis_invalid` cause into its own family before counting the rest. A ruling naming only
|
|
478
478
|
// `coverage_no_status` would be true of most `no_status` records and false of the ones that matter most.
|
|
479
479
|
{ code: "no_status", mints: ["driver/coverage-form.mjs:836"], rollsUpTo: ["coverage_no_status", "coverage_form_axis_invalid"],
|
|
480
|
-
why: "Two composites, split on the record's `cause`: verify.mjs
|
|
480
|
+
why: "Two composites, split on the record's `cause`: `checkFindingsSibling()` in verify.mjs for cause `axis_invalid`, `dispositionForm()` there for the rest. NOT the `@returns` annotation above `COVERAGE_CAUSES` in coverage-form.mjs — that is the annotation, not the mint." },
|
|
481
481
|
{ code: "engine_vocabulary", mints: ["driver/coverage-form.mjs:826"], rollsUpTo: ["coverage_form_engine_vocabulary"],
|
|
482
482
|
why: "#669 — the seat wrote an engine token into the `reason` sentence that reaches the reader's page. Checked on settled rows too, because a row the seat considers finished is exactly the one whose sentence gets printed. Namespaced at verify.mjs:1165; the bare code names a row, never a stage." },
|
|
483
483
|
|
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.6",
|
|
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": {
|
package/driver/pipeline.mjs
CHANGED
|
@@ -6437,8 +6437,10 @@ function injectFrontMatter(path, fields, runDir = null) {
|
|
|
6437
6437
|
// spec 62 — the per-field CONFIGURATION PROVENANCE rows for a project run: for each PROJECT_KEY, its effective
|
|
6438
6438
|
// value (compact) and where it came from (project | customer | house). Read off the frozen sidecar `pr` (both
|
|
6439
6439
|
// origins and the effective values live there), serialised to a single-line JSON string for the report
|
|
6440
|
-
// front-matter
|
|
6441
|
-
//
|
|
6440
|
+
// front-matter. It was rendered internal-only in the scope fold; the 2026-09-16 redesign takes that fold
|
|
6441
|
+
// off the page, and this row set went with it — it was the one block in there labelled internal, which is
|
|
6442
|
+
// the class that ruling removed. IT IS STILL RECORDED, here and in the workbook, which is where a reader
|
|
6443
|
+
// who wants to know which layer set a knob now finds out. Absent origins ⇒ [] ⇒ nothing injected.
|
|
6442
6444
|
function originRowsForReport(pr) {
|
|
6443
6445
|
if (!pr?.origins) return [];
|
|
6444
6446
|
const short = (s, n = 48) => { const t = String(s ?? "").trim(); return t.length > n ? `${t.slice(0, n - 1)}…` : t; };
|