clearotron 0.3.2-beta.6 → 0.3.2-beta.7
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/bin/update.mjs +24 -0
- package/build-info.json +2 -2
- package/driver/CHANGELOG.md +7 -0
- package/driver/connotation-search.mjs +45 -0
- package/driver/contract-e3-backlog.mjs +4 -4
- package/driver/contract-vocabulary.mjs +17 -17
- package/driver/gateway.mjs +6 -3
- package/driver/package.json +1 -1
- package/driver/pipeline.mjs +191 -3
- package/driver/publish/templates/report.css +10 -1
- package/driver/result-noun-fields.mjs +5 -2
- package/driver/suite-census.json +22 -10
- package/driver/verify.mjs +23 -5
- package/mcp-server/CHANGELOG.md +4 -0
- package/mcp-server/http-server.mjs +4 -0
- package/mcp-server/lib/audit.mjs +11 -1
- package/mcp-server/lib/http-handler.mjs +6 -2
- package/mcp-server/package.json +1 -1
- package/mcp-server/server.mjs +1 -1
- package/package.json +1 -1
- package/portal-ui/package.json +1 -1
- package/providers/clarivate/src/capabilities.js +5 -5
- package/providers/clarivate/src/core.js +1 -1
- package/providers/corsearch/src/core.js +2 -2
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/perplexity/src/core.js +3 -3
- package/providers/signa/src/capabilities.js +5 -6
- package/providers/signa/src/core.js +1 -1
- package/scripts/release-duplicate-notes.mjs +246 -0
- package/scripts/release-publish-guard.mjs +64 -6
- package/scripts/report-print-check.mjs +194 -0
- package/shared/driver-dir.mjs +1 -1
package/bin/update.mjs
CHANGED
|
@@ -346,6 +346,30 @@ export async function update(argv = process.argv.slice(2)) {
|
|
|
346
346
|
return installed;
|
|
347
347
|
}
|
|
348
348
|
|
|
349
|
+
// ── AND THE STAMP THE PULL CANNOT MOVE ─────────────────────────────────────────────────────────────
|
|
350
|
+
//
|
|
351
|
+
// `build-info.json` names the commit an archive was packed from. It is written by `prepack`, which this
|
|
352
|
+
// route never runs, and it is untracked, so the pull above cannot bring it forward either. A checkout
|
|
353
|
+
// that was ever packed by hand keeps whatever that pack wrote, and the file then ages while the tree
|
|
354
|
+
// moves: measured on the test box 2026-09-16, it named a commit five days and two minor versions behind
|
|
355
|
+
// the tree it was sitting in.
|
|
356
|
+
//
|
|
357
|
+
// NOTHING THE PRODUCT READS IS FOOLED BY IT, which is why this stamps rather than refuses. `engineCommit`
|
|
358
|
+
// takes git first and never lets a packed stamp override a live checkout, so no run has been
|
|
359
|
+
// misattributed and no client has seen a wrong version. The reader it misleads is a person — checking on
|
|
360
|
+
// the box which build is deployed, answered confidently and wrongly, with nothing on the file to say it
|
|
361
|
+
// is stale. This verb is what moves the commit on this route, so it is the cheapest place to keep the
|
|
362
|
+
// answer true.
|
|
363
|
+
//
|
|
364
|
+
// A failure here does not fail the update. The code and its dependencies are already correct, and
|
|
365
|
+
// reporting a half-done deploy because a note could not be rewritten would be the louder lie.
|
|
366
|
+
const stamped = runInCheckout("node", ["scripts/write-build-info.mjs"]);
|
|
367
|
+
if (stamped !== 0) {
|
|
368
|
+
console.error("\n The update succeeded, but build-info.json could not be re-stamped. A reader of that");
|
|
369
|
+
console.error(" file on this box would be told the commit of an earlier pack rather than this one.");
|
|
370
|
+
console.error(" What this install RUNS is unaffected — that is named from the checkout itself.\n");
|
|
371
|
+
}
|
|
372
|
+
|
|
349
373
|
// ── THE BUNDLE THE PULL COULD NOT UPDATE ─────────────────────────────────────────────────────────
|
|
350
374
|
//
|
|
351
375
|
// `portal-ui/dist` is untracked on the public tree, so `git pull` above can never bring it forward.
|
package/build-info.json
CHANGED
package/driver/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,12 @@
|
|
|
1
1
|
# clearotron-driver
|
|
2
2
|
|
|
3
|
+
## 0.3.2-beta.7
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- 4bde529: Fixed: A clearance that completed all but one or two of its local-language searches now delivers the report. Before, a single search that did not complete threw the whole clearance away, including the fifty-nine that had run. The report says which term's search was short.
|
|
8
|
+
- b5b789c: Fixed: The exported PDF no longer prints a collapsed arrow above sections that are already fully open.
|
|
9
|
+
|
|
3
10
|
## 0.3.2-beta.6
|
|
4
11
|
|
|
5
12
|
### Patch Changes
|
|
@@ -276,6 +276,51 @@ export const queryKey = (s) => String(s ?? "")
|
|
|
276
276
|
.trim()
|
|
277
277
|
.toLowerCase();
|
|
278
278
|
|
|
279
|
+
/**
|
|
280
|
+
* The POPULATION behind `parsePrRiskResults`, because that reader reduces its input and nothing at a
|
|
281
|
+
* call site said so.
|
|
282
|
+
*
|
|
283
|
+
* `parsePrRiskResults` folds rows into a map keyed on the raw query text, so two rows carrying the same
|
|
284
|
+
* query become one and its output is smaller than the ledger it read. Every count taken during one
|
|
285
|
+
* evening's diagnosis was post-fold, by two people, and neither had named the raw population — so "the
|
|
286
|
+
* seat wrote 59 rows" and "59 rows survived the fold" were indistinguishable, and they are different
|
|
287
|
+
* facts with different causes. A seat that recorded one query twice and skipped another looks identical
|
|
288
|
+
* to a seat that simply skipped one, from the folded count alone.
|
|
289
|
+
*
|
|
290
|
+
* Returns both counts so a caller can say which it means. `rows` is what the ledger carries; `distinct`
|
|
291
|
+
* is what the fold leaves; the difference is rows that repeat a query already counted.
|
|
292
|
+
*/
|
|
293
|
+
/**
|
|
294
|
+
* THE TWO LABELS THE MEANING GATE PUTS ON A QUERY IT COULD NOT JOIN, and they are a CONTRACT rather
|
|
295
|
+
* than prose: `gateway.mjs` reads them back to choose which repair a seat is offered, and the two
|
|
296
|
+
* repairs are opposite. Re-wording either one in the validator, with the reader matching on its own
|
|
297
|
+
* copy of the old text, silently collapses that choice — the hint keeps being sent and stops being
|
|
298
|
+
* the right one. Both sides now build and detect from here, so a wording change is one edit.
|
|
299
|
+
*
|
|
300
|
+
* Anything a gate wants to ADD — which file it searched, what a neighbour is evidence of — goes
|
|
301
|
+
* OUTSIDE these markers, so the sentence can grow without moving the contract.
|
|
302
|
+
*/
|
|
303
|
+
export const CONNOTATION_UNMATCHED_MARK = "[unmatched; nearest recorded:";
|
|
304
|
+
export const CONNOTATION_NO_RESEMBLANCE_MARK = "[no recorded query resembles this one]";
|
|
305
|
+
|
|
306
|
+
export function prRiskPopulation(ledgerRaw) {
|
|
307
|
+
let parsed;
|
|
308
|
+
try { parsed = JSON.parse(ledgerRaw); } catch { return { rows: 0, distinct: 0, repeated: 0 }; }
|
|
309
|
+
const batches = Array.isArray(parsed) ? parsed : [parsed];
|
|
310
|
+
let rows = 0;
|
|
311
|
+
const seen = new Set();
|
|
312
|
+
for (const b of batches) {
|
|
313
|
+
const pr = b?.extras?.pr_risk;
|
|
314
|
+
if (!Array.isArray(pr)) continue;
|
|
315
|
+
for (const e of pr) {
|
|
316
|
+
if (!e || typeof e.query !== "string" || !e.query.trim()) continue;
|
|
317
|
+
rows += 1;
|
|
318
|
+
seen.add(e.query.trim());
|
|
319
|
+
}
|
|
320
|
+
}
|
|
321
|
+
return { rows, distinct: seen.size, repeated: rows - seen.size };
|
|
322
|
+
}
|
|
323
|
+
|
|
279
324
|
export function parsePrRiskResults(ledgerRaw) {
|
|
280
325
|
let parsed;
|
|
281
326
|
try { parsed = JSON.parse(ledgerRaw); } catch { return []; }
|
|
@@ -185,7 +185,7 @@ export const E3_BACKLOG = [
|
|
|
185
185
|
// it one: authored once in renderConnotationObligations, and reaching the seat through the perplexity
|
|
186
186
|
// MCP server (driver/engine/mcp/perplexity-server.mjs:111) rather than a stage message. Three rows
|
|
187
187
|
// pointing at one block would fabricate two authored sites the surface does not have.
|
|
188
|
-
where: "driver/connotation-search.mjs:
|
|
188
|
+
where: "driver/connotation-search.mjs:1480-1493 (renderConnotationObligations; delivered to the seat via driver/engine/mcp/perplexity-server.mjs:111 — a TOOL RESPONSE, no longer a stage message)",
|
|
189
189
|
surface: "tool-response",
|
|
190
190
|
// ── (a) THE ORIGINAL SUBJECT WAS DELETED BY DESIGN — DISCHARGED, NOT FAILED ────────────────────
|
|
191
191
|
// The old rows dictated `receipt_id` (an 8-char token the seat copied) and `quote`. Both are gone:
|
|
@@ -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:1171 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:847 CORRECTION_KIND_RE = /\\[kind:\\s*([a-z][a-z-]*)\\s*\\]/i → parseCorrectionKinds (verify.mjs:1070), 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:864 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,7 +17,7 @@
|
|
|
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:1228 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
|
|
@@ -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:1554 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" },
|
|
@@ -128,9 +128,9 @@ export const VOCABULARY = [
|
|
|
128
128
|
{ token: "variantmodel_romanization_missing", stages: ["prelim-variants"], site: "driver/verify.mjs" },
|
|
129
129
|
{ token: "variantmodel_family_incomplete", stages: ["prelim-variants"], site: "driver/verify.mjs" },
|
|
130
130
|
{ token: "variantmodel_term_markup", stages: ["prelim-variants"], site: "driver/verify.mjs" },
|
|
131
|
-
{ token: "variantmodel_missing", stages: ["prelim-variants"], site: "driver/verify.mjs:
|
|
131
|
+
{ token: "variantmodel_missing", stages: ["prelim-variants"], site: "driver/verify.mjs:1171 checkFindingsSibling, 1230 checkJson" },
|
|
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:1241) → 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`
|
|
@@ -142,7 +142,7 @@ export const VOCABULARY = [
|
|
|
142
142
|
// `fail(` / `throw new Error(` / `=>` string literals, and every record module returns
|
|
143
143
|
// `{ok: false, reason}` instead — measured, all four extract ZERO tokens. So this row is authored, not
|
|
144
144
|
// extracted, and nothing re-derives it if the module grows a member. Filed as.
|
|
145
|
-
{ token: "variantmodel_", stages: ["prelim-variants"], site: "driver/verify.mjs:
|
|
145
|
+
{ token: "variantmodel_", stages: ["prelim-variants"], site: "driver/verify.mjs:1171 checkFindingsSibling → 742 (JSON family); driver/prelim-variants-record.mjs acceptPrelimVariants (scope-ledger transport family)", family: "driver/variant-manifest-model.mjs (token-first throws) + driver/prelim-variants-record.mjs", dynamic: "D3" },
|
|
146
146
|
{ token: "blindframe_", stages: ["blind-frame"], site: "driver/verify.mjs blindFrame → checkJson", family: "driver/blind-frame-model.mjs", dynamic: "D3" },
|
|
147
147
|
// — THE SKEPTIC TRANSPORT FAMILY, WHICH HAD NO ROW AT ALL. Nine tokens minted by acceptSkeptic
|
|
148
148
|
// and not one of them was covered here: the conversion that moved them to the acceptance boundary moved
|
|
@@ -417,33 +417,33 @@ export const TRIPWIRE_OUT_OF_SCOPE = [
|
|
|
417
417
|
export const INNER_CODES = Object.freeze([
|
|
418
418
|
// ── connotation-search.mjs — namespaced one-for-one by verify.mjs ────────────────────────────────
|
|
419
419
|
//
|
|
420
|
-
// CONNOTATION_FORM_REASONS at connotation-search.mjs:
|
|
420
|
+
// CONNOTATION_FORM_REASONS at connotation-search.mjs:1776 is `CONNOTATION_REASONS` minus
|
|
421
421
|
// `no_recorded_queries`, and the `connotation_` family row (dynamic D1) is declared against exactly
|
|
422
422
|
// that list. The four call codes are folded by a template — `connotation_${callFail.reason}` at
|
|
423
423
|
// verify.mjs — so they are namespaced by construction rather than one branch at a time.
|
|
424
|
-
{ code: "call_never_made", mints: ["driver/connotation-search.mjs:
|
|
424
|
+
{ code: "call_never_made", mints: ["driver/connotation-search.mjs:2052"], rollsUpTo: ["connotation_call_never_made"],
|
|
425
425
|
why: "CALL_AUDIT_ROWS. The typed transport's four call states, handed in by disposition-call-audit.mjs and namespaced at verify.mjs." },
|
|
426
|
-
{ code: "call_truncated", mints: ["driver/connotation-search.mjs:
|
|
426
|
+
{ code: "call_truncated", mints: ["driver/connotation-search.mjs:2053"], rollsUpTo: ["connotation_call_truncated"],
|
|
427
427
|
why: "As call_never_made — same table, same projection." },
|
|
428
|
-
{ code: "call_schema_violation", mints: ["driver/connotation-search.mjs:
|
|
428
|
+
{ code: "call_schema_violation", mints: ["driver/connotation-search.mjs:2054"], rollsUpTo: ["connotation_call_schema_violation"],
|
|
429
429
|
why: "As call_never_made — same table, same projection." },
|
|
430
|
-
{ code: "call_partial", mints: ["driver/connotation-search.mjs:
|
|
430
|
+
{ code: "call_partial", mints: ["driver/connotation-search.mjs:2055"], rollsUpTo: ["connotation_call_partial"],
|
|
431
431
|
why: "As call_never_made — same table, same projection. This is the pair #1211 cites as its worked example: the composite is covered, the bare form reaches no stage." },
|
|
432
|
-
{ code: "quote_unbound", mints: ["driver/connotation-search.mjs:
|
|
432
|
+
{ code: "quote_unbound", mints: ["driver/connotation-search.mjs:2186"], rollsUpTo: ["connotation_quote_unbound"],
|
|
433
433
|
why: "The ruled-but-unbound row. Projected at verify.mjs:1099, and reported only once nothing is unruled." },
|
|
434
|
-
{ code: "token_absent", mints: ["driver/connotation-search.mjs:
|
|
434
|
+
{ code: "token_absent", mints: ["driver/connotation-search.mjs:2213"], rollsUpTo: ["connotation_token_absent"],
|
|
435
435
|
why: "#592 split this out of no_ruling. Row-level only — repairs.mjs:352 says so in as many words: `never a top-level token`." },
|
|
436
|
-
{ code: "cite_absent", mints: ["driver/connotation-search.mjs:
|
|
436
|
+
{ code: "cite_absent", mints: ["driver/connotation-search.mjs:2222"], rollsUpTo: ["connotation_cite_absent"],
|
|
437
437
|
why: "#592, as token_absent. repairs.mjs:353: `row-level only — never a top-level token`." },
|
|
438
|
-
{ code: "no_ruling", mints: ["driver/connotation-search.mjs:
|
|
438
|
+
{ code: "no_ruling", mints: ["driver/connotation-search.mjs:2229"], rollsUpTo: ["connotation_no_ruling"],
|
|
439
439
|
why: "The residual the other two split off from, and it has representatives — see the mint site. Declared as a corrective token at repairs.mjs:308 and in stages.mjs's E1 rows, always namespaced." },
|
|
440
440
|
// THE ONE THAT IS RENAMED, NOT NAMESPACED — and it is the reason this table stores the composite as
|
|
441
441
|
// data rather than deriving it. Every derivation anyone would write is `connotation_` + the code, and
|
|
442
442
|
// for this row that produces `connotation_no_recorded_queries`, which nothing mints and nothing covers.
|
|
443
443
|
// verify.mjs:912-913 states the ruling: a sweep that did not RUN is a canonical-only decision with its
|
|
444
444
|
// own token and its own remedy, so the projector at verify.mjs:909 deliberately does not handle it.
|
|
445
|
-
{ code: "no_recorded_queries", mints: ["driver/connotation-search.mjs:
|
|
446
|
-
why: "RENAMED, not namespaced: verify.mjs emits `connotation_search_missing`. It is excluded from CONNOTATION_FORM_REASONS at connotation-search.mjs:
|
|
445
|
+
{ code: "no_recorded_queries", mints: ["driver/connotation-search.mjs:2070"], rollsUpTo: ["connotation_search_missing"],
|
|
446
|
+
why: "RENAMED, not namespaced: verify.mjs emits `connotation_search_missing`. It is excluded from CONNOTATION_FORM_REASONS at connotation-search.mjs:1776 for exactly this reason." },
|
|
447
447
|
|
|
448
448
|
// ── case-law-ledger.mjs — ONE token, the codes as census payload ─────────────────────────────────
|
|
449
449
|
//
|
|
@@ -489,7 +489,7 @@ export const INNER_CODES = Object.freeze([
|
|
|
489
489
|
// the census output would have been half a ruling. verify.mjs:1075 already states the rule this row
|
|
490
490
|
// records: `coverage_form_damaged`, never a bare `form_damaged`.
|
|
491
491
|
{ code: "form_damaged",
|
|
492
|
-
mints: ["driver/connotation-search.mjs:
|
|
492
|
+
mints: ["driver/connotation-search.mjs:2075", "driver/connotation-search.mjs:2151", "driver/coverage-form.mjs:818"],
|
|
493
493
|
rollsUpTo: ["connotation_form_damaged", "coverage_form_damaged"],
|
|
494
494
|
why: "Minted in two lanes and namespaced per lane: verify.mjs:1019 for the meaning sweep, verify.mjs:1119 for the register digest. The namespacing is what keeps them apart — see verify.mjs:1075." },
|
|
495
495
|
]);
|
package/driver/gateway.mjs
CHANGED
|
@@ -255,7 +255,8 @@ import { undispatchableFiringDirectives } from "./frame-diff-model.mjs";
|
|
|
255
255
|
import { witnessStageMethodology, describeMethodologyDrift } from "./methodology-witness.mjs";
|
|
256
256
|
// — the meaning-sweep form and its accumulator. Both PURE and acyclic: connotation-search.mjs
|
|
257
257
|
// imports nothing, disposition-union.mjs imports only it, and neither reaches back here.
|
|
258
|
-
import { parsePrRiskResults, connotationObligations, parseDispositionForm, rulingsProse, CONNOTATION_FORM_TOKEN_SRC,
|
|
258
|
+
import { parsePrRiskResults, connotationObligations, parseDispositionForm, rulingsProse, CONNOTATION_FORM_TOKEN_SRC,
|
|
259
|
+
CONNOTATION_UNMATCHED_MARK, CONNOTATION_NO_RESEMBLANCE_MARK, CONNOTATION_FORM_TOKEN_RE } from "./connotation-search.mjs";
|
|
259
260
|
import { unionDispositionForm, formSidecarPath } from "./disposition-union.mjs";
|
|
260
261
|
import { unionCoverageForm } from "./coverage-union.mjs";
|
|
261
262
|
import { coverageFormStamp, readCoverageForm, readCoverageFormInput, writeCoverageForm } from "./coverage-form-io.mjs";
|
|
@@ -2613,8 +2614,10 @@ export function correctionHint(lastFail, { gridLedgerName = "common-law-grid.jso
|
|
|
2613
2614
|
// applies by looking at its own ledger, and neither wastes an attempt: if the search did run, fix
|
|
2614
2615
|
// the row's wording; if it did not, run it and append the row. Where both labels appear, both
|
|
2615
2616
|
// sentences are sent, as before.
|
|
2616
|
-
|
|
2617
|
-
|
|
2617
|
+
// Detected from the validator's own constants, never a copy of its prose: these two choose between
|
|
2618
|
+
// opposite repairs, and a re-worded label with a stale matcher here keeps sending the wrong one.
|
|
2619
|
+
const anyUnmatched = String(dropped).includes(CONNOTATION_UNMATCHED_MARK);
|
|
2620
|
+
const anyUnresembled = String(dropped).includes(CONNOTATION_NO_RESEMBLANCE_MARK);
|
|
2618
2621
|
hint = anyUnmatched && !anyUnresembled
|
|
2619
2622
|
? `these dictated meaning queries ARE recorded in ${gridLedgerName} extras.pr_risk[] under a ` +
|
|
2620
2623
|
`different wording, which is why the driver cannot match them: ${dropped}. Do NOT re-run them — ` +
|
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.7",
|
|
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
|
@@ -201,6 +201,39 @@ async function defaultRecordFetcher(uri, ctx) {
|
|
|
201
201
|
return activeProvider().recordFetch(uri, ctx);
|
|
202
202
|
}
|
|
203
203
|
|
|
204
|
+
/**
|
|
205
|
+
* THE ENGINE RE-ISSUES A DICTATED MEANING SEARCH ITSELF, without asking the assessing model to redo the
|
|
206
|
+
* sweep — the same shape as `defaultRecordFetcher` above, which fetches a register record in code when
|
|
207
|
+
* one was dropped unexamined rather than sending the model back for it.
|
|
208
|
+
*
|
|
209
|
+
* "Through the same tool" is literal and costs no new machinery: the research provider's grid functions
|
|
210
|
+
* are a plain module with no listener, so the component the model calls and this one call the SAME
|
|
211
|
+
* functions on the same provider. There is no second implementation to drift.
|
|
212
|
+
*
|
|
213
|
+
* NARROW BY CONSTRUCTION. The spec handed over carries only the queries that are missing, so this buys
|
|
214
|
+
* those cells rather than re-running a sweep that already cost real money and real minutes.
|
|
215
|
+
*
|
|
216
|
+
* It DECIDES nothing. The caller re-merges and the gate judges, so a re-issue that comes back empty is
|
|
217
|
+
* simply a re-merge that still fails, and is disclosed as such rather than counted as searched.
|
|
218
|
+
*/
|
|
219
|
+
async function defaultConnotationReissuer(queries, { spec, modelOverride = null } = {}) {
|
|
220
|
+
const apiKey = process.env.PERPLEXITY_API_KEY || "";
|
|
221
|
+
if (!apiKey) return { ok: false, cause: "no research credential in the driver environment", rows: [] };
|
|
222
|
+
const core = await import("../providers/perplexity/src/core.js");
|
|
223
|
+
const narrow = { ...spec, connotation: { ...(spec.connotation ?? {}), queries: queries.slice() } };
|
|
224
|
+
try {
|
|
225
|
+
const task = core.buildGridProgramTask(narrow);
|
|
226
|
+
const data = await core.callAgentAPI(apiKey,
|
|
227
|
+
core.buildRequestBody({ task, preset: "pro-search", modelOverride, enableSandbox: true }));
|
|
228
|
+
const cap = core.captureGridFromResponse(data, narrow);
|
|
229
|
+
if (!cap?.ok) return { ok: false, cause: String(cap?.error ?? "capture failed").slice(0, 160), rows: [] };
|
|
230
|
+
const rows = parsePrRiskResults(cap.ledgerJson).filter((r) => queries.includes(r.query));
|
|
231
|
+
return { ok: true, rows, requested: queries.length };
|
|
232
|
+
} catch (e) {
|
|
233
|
+
return { ok: false, cause: String(e?.message ?? e).slice(0, 160), rows: [] };
|
|
234
|
+
}
|
|
235
|
+
}
|
|
236
|
+
|
|
204
237
|
// The ACTIVE provider's capability contract and the offices this DEPLOYMENT cannot reach both moved to
|
|
205
238
|
// register-unreachable.mjs, unchanged, because Depth 2's count and record lanes need the same
|
|
206
239
|
// two answers and a second binding of the member→variable lookup is how two lanes come to disagree
|
|
@@ -9170,6 +9203,64 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9170
9203
|
runLog(run.runDir, { event: "common-law-half-quarantined", half: h, fail: String(clHalves[h].fail).slice(0, 140) });
|
|
9171
9204
|
}
|
|
9172
9205
|
}
|
|
9206
|
+
// The engine's own re-issue, spent at most once per run — the bound spans BOTH the half-stage
|
|
9207
|
+
// hook below and the merge gate's disclosure, so a run never buys the same search twice.
|
|
9208
|
+
let clConnotationReissueSpent = false;
|
|
9209
|
+
// ── THE MEANING HALF FELL SHORT BY A SEARCH OR TWO: RE-ISSUE, THEN DELIVER ───────────────────
|
|
9210
|
+
//
|
|
9211
|
+
// This is where the clearance actually died. The half's own validator refuses when a dictated
|
|
9212
|
+
// meaning search is missing from its receipts, and every attempt refused identically — eight of
|
|
9213
|
+
// them — so the run was thrown away with fifty-nine of sixty searches done and recorded.
|
|
9214
|
+
//
|
|
9215
|
+
// Quarantine above cannot help here: that is for a half that failed wholesale on transient
|
|
9216
|
+
// infrastructure, where re-running the half converges. This half SUCCEEDED at almost everything.
|
|
9217
|
+
// So the engine re-issues the missing searches itself, through the same provider, and re-judges the
|
|
9218
|
+
// half on its own artifact. If they landed, the half is simply valid and nothing is disclosed.
|
|
9219
|
+
//
|
|
9220
|
+
// If they did not, the half is accepted WITH THE SHORTFALL RECORDED — an honest gap per unfinished
|
|
9221
|
+
// search, carrying its term, which the merge gate then discloses and the coverage row reads. That is
|
|
9222
|
+
// the owner's ruling of 2026-09-17 and it is not the false-clean the gates guard: false-clean is
|
|
9223
|
+
// silence, and this says on the client's own page which search did not complete.
|
|
9224
|
+
if (clSplit && !clHalves[MEANING_SEAT].ok && /connotation_query_unrecorded/.test(String(clHalves[MEANING_SEAT].fail ?? ""))) {
|
|
9225
|
+
const h = MEANING_SEAT;
|
|
9226
|
+
let short = [];
|
|
9227
|
+
try {
|
|
9228
|
+
const spec = JSON.parse(readFileSync(P.gridSpecHalf(h), "utf8"));
|
|
9229
|
+
const halfLedger = JSON.parse(readFileSync(P.commonLawGridHalf(h), "utf8"));
|
|
9230
|
+
short = findDroppedConnotationQueries(spec, halfLedger);
|
|
9231
|
+
if (short.length) {
|
|
9232
|
+
runLog(run.runDir, { event: "connotation-reissue", half: h, count: short.length });
|
|
9233
|
+
note(`the meaning half recorded all but ${short.length} of its dictated searches — the engine re-issues `
|
|
9234
|
+
+ `${short.length === 1 ? "it" : "them"} itself rather than asking for the sweep again`);
|
|
9235
|
+
clConnotationReissueSpent = true;
|
|
9236
|
+
const reissuer = opts.connotationReissuer ?? defaultConnotationReissuer;
|
|
9237
|
+
const r = await reissuer(short, { spec });
|
|
9238
|
+
runLog(run.runDir, { event: "connotation-reissue-result", ok: !!r?.ok,
|
|
9239
|
+
recovered: Array.isArray(r?.rows) ? r.rows.length : 0,
|
|
9240
|
+
cause: r?.ok ? null : String(r?.cause ?? "").slice(0, 160) });
|
|
9241
|
+
const rows = (r?.ok && Array.isArray(r.rows)) ? r.rows : [];
|
|
9242
|
+
halfLedger.extras = halfLedger.extras ?? {};
|
|
9243
|
+
halfLedger.extras.pr_risk = [...(Array.isArray(halfLedger.extras.pr_risk) ? halfLedger.extras.pr_risk : []), ...rows];
|
|
9244
|
+
const stillShort = findDroppedConnotationQueries(spec, halfLedger);
|
|
9245
|
+
if (stillShort.length) {
|
|
9246
|
+
halfLedger.gaps = Array.isArray(halfLedger.gaps) ? halfLedger.gaps : [];
|
|
9247
|
+
for (const q of stillShort)
|
|
9248
|
+
halfLedger.gaps.push({ term: q, platform: "connotation",
|
|
9249
|
+
error: "the search did not complete after a re-ask and a re-issue by the engine" });
|
|
9250
|
+
runLog(run.runDir, { event: "connotation-unfinished-disclosed", count: stillShort.length, half: h });
|
|
9251
|
+
note(`${stillShort.length} dictated meaning quer${stillShort.length === 1 ? "y" : "ies"} did not complete — `
|
|
9252
|
+
+ "recorded as honest gaps and DISCLOSED on the coverage row; the run delivers the rest");
|
|
9253
|
+
}
|
|
9254
|
+
atomicWrite(P.commonLawGridHalf(h), JSON.stringify(halfLedger, null, 2) + "\n");
|
|
9255
|
+
// The half is accepted on its re-judged artifact. Same shape as quarantine above: the stage is
|
|
9256
|
+
// not re-run and the model is not asked again.
|
|
9257
|
+
clHalves[h].ok = true;
|
|
9258
|
+
gatherResults[clHalves[h].i] = { ...gatherResults[clHalves[h].i], ok: true, fail: null, clHalfShortfall: stillShort.length };
|
|
9259
|
+
}
|
|
9260
|
+
} catch (err) {
|
|
9261
|
+
runLog(run.runDir, { event: "connotation-reissue-failed", error: String(err?.message ?? err).slice(0, 160) });
|
|
9262
|
+
}
|
|
9263
|
+
}
|
|
9173
9264
|
gather.forEach((g, i) => must(gatherResults[i], g.name + (g.axis ? `:${g.axis}` : "")));
|
|
9174
9265
|
// winning session key per axis → a later escalation resumes the EXACT key the unit succeeded on.
|
|
9175
9266
|
// (register units only — a common-law half's axis is its half id, tracked in clHalves, never here.)
|
|
@@ -9184,7 +9275,7 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9184
9275
|
// synthesis, replay) reads exactly the artifacts a single-member run would have written. Re-run after
|
|
9185
9276
|
// every routed half followup (closure / frame-reopen) — the half files stay the source of truth and
|
|
9186
9277
|
// the canonical pair is always derived, which also makes the merge resume-idempotent.
|
|
9187
|
-
const mergeCommonLawArtifacts = !clSplit ? null : () => {
|
|
9278
|
+
const mergeCommonLawArtifacts = !clSplit ? null : ({ discloseUnfinished = false } = {}) => {
|
|
9188
9279
|
// Each half's coverage truth = its plugin-written main ledger FOLDED with any plugin-written
|
|
9189
9280
|
// supplementary sibling(s) the closure lane produced. Returns the batches as an ARRAY (mergeGrids'
|
|
9190
9281
|
// batchesOf flattens it, cells union across all of them); a missing/corrupt main leaves its cells to
|
|
@@ -9355,6 +9446,40 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9355
9446
|
// behaviour was zero retries after full spend, with a re-run that failed identically; the new
|
|
9356
9447
|
// behaviour is one retry, then the same honest death.
|
|
9357
9448
|
const dropped = findDroppedConnotationQueries(fullSpec, mergedGrid);
|
|
9449
|
+
if (dropped.length && discloseUnfinished) {
|
|
9450
|
+
// ── THE LAST STEP OF THE OWNER'S RULING: a run that completed 59 of 60 dictated searches
|
|
9451
|
+
// DELIVERS, and says on the client's own page that it was partial and for which term.
|
|
9452
|
+
//
|
|
9453
|
+
// This is NOT the false-clean the note below guards against, and the difference is the whole
|
|
9454
|
+
// reason it is allowed here. False-clean is SILENCE — a hole counted as searched, nobody able to
|
|
9455
|
+
// see what was not done. This records each unfinished query as an honest gap carrying its own
|
|
9456
|
+
// term, which is the vocabulary the note above says dictated queries did not have, and that gap
|
|
9457
|
+
// is what the coverage row reads. A reader is told the investigation was partial and which term
|
|
9458
|
+
// did not complete.
|
|
9459
|
+
//
|
|
9460
|
+
// It is reached ONLY after the bounded attempts are spent: the model's single routed remedy and
|
|
9461
|
+
// the engine's own re-issue of the search. On the first pass this branch does not exist — the
|
|
9462
|
+
// gate throws exactly as before and the park loop gets its retries. Disclosure is the last
|
|
9463
|
+
// resort, never the first answer.
|
|
9464
|
+
const errored = findErroredConnotationQueries(fullSpec, mergedGrid);
|
|
9465
|
+
const explained = new Set(errored.map((e) => e.query));
|
|
9466
|
+
const gaps = Array.isArray(mergedGrid.gaps) ? mergedGrid.gaps : (mergedGrid.gaps = []);
|
|
9467
|
+
for (const q of dropped) {
|
|
9468
|
+
if (explained.has(q)) continue; // the provider already said why; its row stands
|
|
9469
|
+
gaps.push({ term: q, platform: "connotation",
|
|
9470
|
+
error: "the search did not complete after a re-ask and a re-issue by the engine" });
|
|
9471
|
+
}
|
|
9472
|
+
// THE CANONICAL LEDGER IS WRITTEN ABOVE, BEFORE THIS GATE RUNS, so mutating the merged object
|
|
9473
|
+
// here reaches nothing: the report reads the file. Re-write it with the gaps in place, or the
|
|
9474
|
+
// disclosure exists only in memory and the coverage row has nothing to read — which is the
|
|
9475
|
+
// silent half of the very defect this branch exists to prevent.
|
|
9476
|
+
atomicWrite(P.commonLawGrid, JSON.stringify(mergedGrid, null, 2) + "\n");
|
|
9477
|
+
runLog(run.runDir, { event: "connotation-unfinished-disclosed", count: dropped.length,
|
|
9478
|
+
explained: errored.length, terms: dropped.length });
|
|
9479
|
+
note(`${dropped.length} dictated meaning quer${dropped.length === 1 ? "y" : "ies"} did not complete after `
|
|
9480
|
+
+ "the bounded attempts — recorded as honest gaps and DISCLOSED on the coverage row; the run delivers");
|
|
9481
|
+
return { mergedGrid, fullSpec, connotationUnfinished: dropped.slice() };
|
|
9482
|
+
}
|
|
9358
9483
|
if (dropped.length) {
|
|
9359
9484
|
const errored = findErroredConnotationQueries(fullSpec, mergedGrid);
|
|
9360
9485
|
const allExplained = errored.length === dropped.length;
|
|
@@ -9385,6 +9510,54 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9385
9510
|
// retrying a defect buys nothing; this is one dictated repair, not a retry). Quarantined-half shapes
|
|
9386
9511
|
// keep their transient park path untouched (the remedy never runs for them).
|
|
9387
9512
|
let clConnotationRemedySpent = false;
|
|
9513
|
+
/**
|
|
9514
|
+
* THE ENGINE'S OWN ATTEMPT, THEN DELIVERY WITH THE GAP DISCLOSED.
|
|
9515
|
+
*
|
|
9516
|
+
* Reached only after the model has had its single routed repair turn. The engine re-issues the
|
|
9517
|
+
* missing searches itself through the same provider, appends whatever comes back to the half's own
|
|
9518
|
+
* ledger, and re-merges. If the searches are there now, the run is simply clean and nothing is
|
|
9519
|
+
* disclosed. If they are still missing, the run DELIVERS and the coverage row says the investigation
|
|
9520
|
+
* was partial and which term did not complete.
|
|
9521
|
+
*
|
|
9522
|
+
* A clearance that completed 59 of 60 dictated searches used to throw the whole report away. The
|
|
9523
|
+
* failure it is replacing is not a quality gate doing its job: the work was done, one search of
|
|
9524
|
+
* sixty did not land, and the client lost everything including the 59.
|
|
9525
|
+
*/
|
|
9526
|
+
const reissueThenDisclose = async (e) => {
|
|
9527
|
+
if (!clConnotationReissueSpent) {
|
|
9528
|
+
clConnotationReissueSpent = true;
|
|
9529
|
+
let missing = [];
|
|
9530
|
+
try {
|
|
9531
|
+
const spec = JSON.parse(readFileSync(P.gridSpecHalf(MEANING_SEAT), "utf8"));
|
|
9532
|
+
const merged = JSON.parse(readFileSync(P.commonLawGrid, "utf8"));
|
|
9533
|
+
missing = findDroppedConnotationQueries(spec, merged);
|
|
9534
|
+
if (missing.length) {
|
|
9535
|
+
runLog(run.runDir, { event: "connotation-reissue", count: missing.length });
|
|
9536
|
+
note(`${missing.length} dictated meaning quer${missing.length === 1 ? "y" : "ies"} missing after the `
|
|
9537
|
+
+ "model's one repair turn — the engine re-issues them itself through the same tool");
|
|
9538
|
+
const reissuer = opts.connotationReissuer ?? defaultConnotationReissuer;
|
|
9539
|
+
const r = await reissuer(missing, { spec });
|
|
9540
|
+
runLog(run.runDir, { event: "connotation-reissue-result", ok: !!r?.ok,
|
|
9541
|
+
recovered: Array.isArray(r?.rows) ? r.rows.length : 0,
|
|
9542
|
+
cause: r?.ok ? null : String(r?.cause ?? "").slice(0, 160) });
|
|
9543
|
+
if (r?.ok && r.rows?.length) {
|
|
9544
|
+
// Append into the half's OWN ledger, so the ordinary re-merge picks them up and nothing
|
|
9545
|
+
// downstream needs to know this call happened.
|
|
9546
|
+
const half = JSON.parse(readFileSync(P.commonLawGridHalf(MEANING_SEAT), "utf8"));
|
|
9547
|
+
half.extras = half.extras ?? {};
|
|
9548
|
+
half.extras.pr_risk = [...(Array.isArray(half.extras.pr_risk) ? half.extras.pr_risk : []), ...r.rows];
|
|
9549
|
+
atomicWrite(P.commonLawGridHalf(MEANING_SEAT), JSON.stringify(half, null, 2) + "\n");
|
|
9550
|
+
}
|
|
9551
|
+
}
|
|
9552
|
+
} catch (err) {
|
|
9553
|
+
// Never fatal: a re-issue that cannot even be attempted leaves the run exactly where it was,
|
|
9554
|
+
// and the disclosure below is what the ruling actually guarantees.
|
|
9555
|
+
runLog(run.runDir, { event: "connotation-reissue-failed", error: String(err?.message ?? err).slice(0, 160) });
|
|
9556
|
+
}
|
|
9557
|
+
try { return mergeCommonLawArtifacts(); } catch { /* still short — disclose below */ }
|
|
9558
|
+
}
|
|
9559
|
+
return mergeCommonLawArtifacts({ discloseUnfinished: true });
|
|
9560
|
+
};
|
|
9388
9561
|
const mergeWithConnotationRemedy = !clSplit ? null : async () => {
|
|
9389
9562
|
try { return mergeCommonLawArtifacts(); }
|
|
9390
9563
|
catch (e) {
|
|
@@ -9408,7 +9581,21 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9408
9581
|
// the token inline must keep opening this channel.
|
|
9409
9582
|
const tok = connotationRemedyToken(e);
|
|
9410
9583
|
const anyQuarantined = GRID_SEATS.some((h) => clHalves[h].quarantined);
|
|
9411
|
-
|
|
9584
|
+
// A quarantined half keeps its transient park path: that half is re-run whole and the re-merge
|
|
9585
|
+
// converges, so there is nothing here to disclose yet.
|
|
9586
|
+
// THE DROPPED-QUERY FAILURE CARRIES NO FORM TOKEN, and matching on the form tokens alone is why
|
|
9587
|
+
// this channel never opened for it: `connotationRemedyToken` reads the connotation FORM
|
|
9588
|
+
// vocabulary, and "merged half-grids dropped N dictated connotation queries" is not in it. The
|
|
9589
|
+
// run therefore died at the merge with the recovery path sitting unused beside it.
|
|
9590
|
+
const droppedQueries = /dropped \d+ dictated connotation quer/i.test(String(e?.message ?? ""));
|
|
9591
|
+
if (anyQuarantined) throw e;
|
|
9592
|
+
if (droppedQueries) return await reissueThenDisclose(e);
|
|
9593
|
+
if (!tok) throw e;
|
|
9594
|
+
// ── ONE RE-ASK OF THE MODEL, THEN THE ENGINE TAKES OVER ──────────────────────────────────
|
|
9595
|
+
// The loop stops being a loop. The model gets exactly one routed repair turn; after that the
|
|
9596
|
+
// engine re-issues the search itself and, failing that, the run delivers with the gap disclosed.
|
|
9597
|
+
// Before this, a second arrival here re-threw and the clearance died after a full paid gather.
|
|
9598
|
+
if (clConnotationRemedySpent) return await reissueThenDisclose(e);
|
|
9412
9599
|
clConnotationRemedySpent = true;
|
|
9413
9600
|
runLog(run.runDir, { event: "connotation-remedy", detail: tok.slice(0, 200) });
|
|
9414
9601
|
// — WHICH HALVES OWE ROWS, READ FROM THE FORM. This used to re-derive the violation set here,
|
|
@@ -9442,7 +9629,8 @@ async function pipelineInner(job, opts = {}) {
|
|
|
9442
9629
|
const r = await stage("common-law-half", { ...ctx, axis: h }, { force: true, followup, sessionKey: clHalves[h].sessionKey, trigger: "connotation-remedy" });
|
|
9443
9630
|
if (!r.ok) note(`connotation remedy (half ${h}) failed mechanically (${r.fail}) — the re-merge below decides`);
|
|
9444
9631
|
});
|
|
9445
|
-
return mergeCommonLawArtifacts();
|
|
9632
|
+
try { return mergeCommonLawArtifacts(); }
|
|
9633
|
+
catch (e2) { return await reissueThenDisclose(e2); }
|
|
9446
9634
|
}
|
|
9447
9635
|
};
|
|
9448
9636
|
if (clSplit) await mergeWithConnotationRemedy();
|
|
@@ -339,7 +339,16 @@
|
|
|
339
339
|
walks a rendered page and refuses any disclosure that is in neither list; the public suite does
|
|
340
340
|
not run it, so the commit that added this fold could ship the half it did not think about. */
|
|
341
341
|
.rrow>summary::before,.rgroup>summary::before,details.scope>summary::before,
|
|
342
|
-
details.searched>summary::before
|
|
342
|
+
details.searched>summary::before,
|
|
343
|
+
/* `.cgroup` ("Dead filings — 6 names") and `.ma-more` ("By class and market (3)") are the same kind of
|
|
344
|
+
thing as the four above: summaries that CARRY content, so they print as static headers rather than
|
|
345
|
+
vanishing. They were in neither list, so the exported PDF drew their ▸ over content this block has
|
|
346
|
+
already forced fully open — thirteen of them, eight `.cgroup` and five `.ma-more`, across THREE of
|
|
347
|
+
the four search types: full country 5, global preliminary 5, multi-country focus 3. The knockout
|
|
348
|
+
report carries neither class and nothing in it changes. Found by the print check that now runs in
|
|
349
|
+
CI; the fixture the hand-run control walks carries neither class either, which is why two earlier
|
|
350
|
+
folds were caught there and these thirteen were not. */
|
|
351
|
+
.cgroup>summary::before,.markassess details.ma-more>summary::before{content:""!important}
|
|
343
352
|
details{border-top:1px solid var(--line)}
|
|
344
353
|
/* …AND THE HERO'S FOLD IS NOT A SECTION. The rule above separates the disclosures that ARE sections
|
|
345
354
|
of the document; `details.sub-more` is one paragraph of the verdict caption folded away from
|
|
@@ -74,8 +74,11 @@ export const RESULT_NOUN_FIELDS = Object.freeze([
|
|
|
74
74
|
why: "the one member that reported an invocation — taint-rerun's `r.ok ? \"ok\" : …`, which travels on a StageFailure packet — now carries a `cleared` sibling read from the taint, the remedy 10/5 -> 11/6 at the profile-store receipt's `outcome: pr.outcome`. Classified by READING ITS WRITING SITE, which is profiles.mjs and not this file: the value is one of three literals chosen by a situation the resolver decided (`overlay` and `bundled-fallback` are `pass`, `env-arrived-late` is `blocked`), never a call's return read as a verdict. `bundled-fallback` being `pass` is the point of the whole receipt — a legitimate install that nobody was told about is what this row says out loud." },
|
|
75
75
|
{ file: "driver/pipeline.mjs", noun: "permanent", sites: 1, atWriteSite: 1, verdict: "result",
|
|
76
76
|
why: "`permanent.length` — a count of the classified set" },
|
|
77
|
-
{ file: "driver/pipeline.mjs", noun: "recovered", sites:
|
|
78
|
-
why: "each sits on a branch reached only after the gap was cleared; one follows a `throw` that guarantees the ledger exists"
|
|
77
|
+
{ file: "driver/pipeline.mjs", noun: "recovered", sites: 5, atWriteSite: 5, verdict: "result",
|
|
78
|
+
why: "each sits on a branch reached only after the gap was cleared; one follows a `throw` that guarantees the ledger exists. "
|
|
79
|
+
+ "The two newest record what an engine-side re-issue of a missing meaning search actually brought back, counted off the rows "
|
|
80
|
+
+ "in hand at the write site rather than from anything the call promised — an attempt that recovered nothing writes 0, which "
|
|
81
|
+
+ "is the fact the disclosure downstream depends on" },
|
|
79
82
|
{ file: "driver/pipeline.mjs", noun: "settled", sites: 5, atWriteSite: 4, verdict: "result",
|
|
80
83
|
why: "counts off the union and the doubt ledger" },
|
|
81
84
|
{ file: "driver/pipeline.mjs", noun: "verified", sites: 3, atWriteSite: 1, verdict: "result",
|