clearotron 0.3.2-beta.8 → 0.3.2-beta.9
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/.env.example +34 -3
- package/CONTRIBUTING.md +8 -8
- package/INSTALL.md +6 -6
- package/SECURITY.md +3 -3
- package/bin/brandowner.mjs +3 -3
- package/bin/framework-preflight.mjs +1 -1
- package/bin/start.mjs +18 -4
- package/build-info.json +2 -2
- package/docs/DELIVERY.md +2 -1
- package/docs/INTAKE.md +1 -1
- package/docs/ONBOARDING.md +1 -1
- package/docs/architecture/03-run-lifecycle.md +6 -6
- package/docs/architecture/04-configuration-reference.md +6 -3
- package/docs/architecture/05-config-governance.md +6 -1
- package/docs/architecture/05-customer-profiles.md +2 -2
- package/docs/architecture/06-operations-runbook.md +3 -3
- package/docs/architecture/08-development-guide.md +6 -6
- package/docs/configuration.md +5 -5
- package/docs/decisions/0003-credential-model.md +1 -1
- package/docs/writing-standard.md +4 -0
- package/driver/CHANGELOG.md +48 -0
- package/driver/README.md +3 -3
- package/driver/binding-layers.mjs +1 -1
- package/driver/citation-census.json +3 -3
- package/driver/{prelim-variants-record.mjs → clearance-variants-record.mjs} +24 -24
- package/driver/common-law-receipts.mjs +2 -2
- package/driver/company-bundle.mjs +3 -3
- package/driver/compose-read.mjs +8 -14
- package/driver/consumption-ledger.mjs +2 -2
- package/driver/contract-arm2-baseline.json +2 -3
- package/driver/contract-dictation-registry.mjs +19 -19
- package/driver/contract-e3-backlog.mjs +19 -19
- package/driver/contract-e3-baseline.json +14 -14
- package/driver/contract-vocabulary.mjs +24 -17
- package/driver/deliver-trigger.sh +16 -16
- package/driver/demo-container.mjs +3 -3
- package/driver/dev-portal.mjs +3 -3
- package/driver/disposition-call.mjs +1 -1
- package/driver/doubt-ledger.mjs +2 -2
- package/driver/drainer-identity.mjs +34 -8
- package/driver/driver.config.mjs +95 -45
- package/driver/engine/mcp/README.md +1 -1
- package/driver/engine/mcp/dispositions-server.mjs +3 -3
- package/driver/engine/mcp/gather-config.mjs +9 -9
- package/driver/engine/mcp/perplexity-server.mjs +2 -2
- package/driver/engine/mcp/recording-server.mjs +5 -5
- package/driver/enqueue-schema.mjs +6 -2
- package/driver/findings-model.mjs +5 -2
- package/driver/flag-snapshot.mjs +6 -3
- package/driver/form-neighbourhood.mjs +54 -7
- package/driver/framework.mjs +4 -4
- package/driver/gateway.mjs +12 -6
- package/driver/jx-lanes.mjs +2 -2
- package/driver/jx-units.mjs +1 -1
- package/driver/jx.mjs +30 -2
- package/driver/knockout-review-record.mjs +56 -4
- package/driver/known-conflicts.mjs +1 -1
- package/driver/named-band.mjs +1 -33
- package/driver/ordinary-words.mjs +51 -0
- package/driver/outbox-backoff.mjs +31 -16
- package/driver/package.json +1 -1
- package/driver/partial-payload-baseline.json +2 -2
- package/driver/phase0.mjs +3 -3
- package/driver/pipeline-knockout.mjs +5 -5
- package/driver/pipeline.mjs +206 -68
- package/driver/placement-form.mjs +77 -1
- package/driver/placement-model.mjs +1 -1
- package/driver/portal-report.mjs +92 -5
- package/driver/portal-service.mjs +34 -8
- package/driver/portal-upstream.mjs +1 -1
- package/driver/preserve-merge.mjs +3 -3
- package/driver/product-rows.mjs +2 -2
- package/driver/products.mjs +1 -1
- package/driver/profiles/README.md +3 -3
- package/driver/profiles/demo-brand-owner.json +2 -2
- package/driver/profiles.mjs +55 -17
- package/driver/progress.mjs +18 -8
- package/driver/provider-usage.mjs +8 -8
- package/driver/publish/index.mjs +110 -5
- package/driver/publish/knockout.mjs +29 -4
- package/driver/publish/pool-admin.mjs +1 -1
- package/driver/publish/publish-inputs.mjs +18 -2
- package/driver/publish/render-knockout.mjs +115 -24
- package/driver/publish/render.mjs +153 -34
- package/driver/publish/search-depth.mjs +133 -4
- package/driver/publish/templates/report.css +60 -3
- package/driver/publish/xlsx.mjs +8 -1
- package/driver/queue-order.mjs +2 -2
- package/driver/recording-agreement.mjs +1 -1
- package/driver/reference-score.mjs +1 -1
- package/driver/register-count.mjs +50 -5
- package/driver/register-coverage.mjs +67 -0
- package/driver/register-grant-vocabulary.mjs +1 -1
- package/driver/register-plan.mjs +19 -2
- package/driver/registry-fidelity.mjs +3 -3
- package/driver/repair-composers.mjs +1 -1
- package/driver/repair-contract.mjs +1 -1
- package/driver/replay-archive.mjs +6 -6
- package/driver/report-overview-record.mjs +2 -2
- package/driver/run-requirements.mjs +3 -3
- package/driver/runner.mjs +2 -2
- package/driver/scope-facts.mjs +20 -5
- package/driver/scope-ledger.mjs +5 -5
- package/driver/search-policy.mjs +22 -12
- package/driver/skills/README.md +15 -15
- package/driver/skills/blind-frame/SKILL.md +2 -2
- package/driver/skills/case-law-citation/SKILL.md +4 -4
- package/driver/skills/case-law-citation/sources/eurlex.md +1 -1
- package/driver/skills/{prelim-common-law → clearance-common-law}/SKILL.md +22 -22
- package/driver/skills/{prelim-common-law → clearance-common-law}/perplexity-prompts.md +1 -1
- package/driver/skills/{prelim-register → clearance-register}/SKILL.md +10 -10
- package/driver/skills/{prelim-register → clearance-register}/digest.md +2 -2
- package/driver/skills/{prelim-register → clearance-register}/providers/README.md +1 -1
- package/driver/skills/{prelim-register → clearance-register}/providers/clarivate.md +37 -35
- package/driver/skills/{prelim-register → clearance-register}/providers/corsearch.md +20 -11
- package/driver/skills/{prelim-register → clearance-register}/providers/signa.md +5 -5
- package/driver/skills/{prelim-register → clearance-register}/register-recipes.md +3 -3
- package/driver/skills/{prelim-register → clearance-register}/status-rules.md +2 -2
- package/driver/skills/{prelim-register → clearance-register}/stealth-filer-indicators.md +1 -1
- package/driver/skills/{prelim-register → clearance-register}/unit.md +2 -2
- package/driver/skills/{prelim-search → clearance-search}/SKILL.md +31 -31
- package/driver/skills/{prelim-search → clearance-search}/delivery-contract.md +1 -1
- package/driver/skills/{prelim-search → clearance-search}/phase2-execution.md +18 -18
- package/driver/skills/{prelim-search → clearance-search}/synthesis-rules.md +7 -7
- package/driver/skills/{prelim-variants → clearance-variants}/SKILL.md +18 -18
- package/driver/skills/{prelim-variants → clearance-variants}/transliteration-scripts.md +5 -5
- package/driver/skills/frame-diff/SKILL.md +1 -1
- package/driver/skills/knockout-assess/SKILL.md +10 -7
- package/driver/skills/matter-frame/SKILL.md +3 -3
- package/driver/skills/narrative-refutation/SKILL.md +9 -9
- package/driver/skills/placement-inquiry/SKILL.md +5 -5
- package/driver/stage-context.mjs +1 -1
- package/driver/stages-knockout.mjs +4 -4
- package/driver/stages.mjs +53 -53
- package/driver/status-snapshot.mjs +2 -2
- package/driver/suite-census.json +218 -92
- package/driver/surface-exit-verdict.mjs +58 -0
- package/driver/systemd/README.md +2 -2
- package/driver/systemd/clearotron-worker.service +1 -1
- package/driver/terminal-clamp.mjs +2 -0
- package/driver/usage-ledger.mjs +1 -1
- package/driver/variant-manifest-model.mjs +4 -4
- package/driver/verify-knockout.mjs +27 -0
- package/driver/verify.mjs +67 -6
- package/driver/whatif-queue.mjs +1 -1
- package/driver/wordlists/en.txt +63906 -0
- package/mcp-server/CHANGELOG.md +4 -0
- package/mcp-server/README.md +1 -1
- package/mcp-server/lib/README.md +1 -1
- package/mcp-server/lib/options.mjs +8 -7
- package/mcp-server/lib/plan.mjs +18 -2
- package/mcp-server/lib/runs.mjs +1 -1
- package/mcp-server/lib/usage.mjs +3 -3
- package/mcp-server/lib/whatif.mjs +1 -1
- package/mcp-server/package.json +1 -1
- package/mcp-server/server.mjs +3 -0
- package/package.json +12 -11
- package/portal-ui/dist/assets/{index-CVOIvdhc.css → index-CtvwLCti.css} +207 -3
- package/portal-ui/dist/assets/{index-6jzO9HiX.js → index-EVaSo5-g.js} +1459 -482
- package/portal-ui/dist/index.html +2 -2
- package/portal-ui/package.json +1 -1
- package/providers/README.md +1 -1
- package/providers/_shared/enumerate.mjs +6 -6
- package/providers/_shared/execute-plan.mjs +3 -3
- package/providers/_shared/ledger.mjs +119 -5
- package/providers/_shared/provider-text.mjs +2 -2
- package/providers/_shared/screen.mjs +2 -2
- package/providers/_shared/script-form.mjs +3 -3
- package/providers/_shared/territory-codes.mjs +23 -3
- package/providers/clarivate/README.md +1 -1
- package/providers/clarivate/src/capabilities.js +12 -12
- package/providers/clarivate/src/core.js +37 -43
- package/providers/corsearch/README.md +1 -1
- package/providers/corsearch/src/capabilities.js +5 -5
- package/providers/corsearch/src/core.js +3 -3
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/perplexity/src/core.js +1 -1
- package/providers/signa/README.md +1 -1
- package/providers/signa/src/capabilities.js +42 -49
- package/providers/signa/src/core.js +106 -29
- package/providers/uspto-local/src/sync.js +1 -1
- package/scripts/README.md +1 -0
- package/scripts/ask-ai-render-check.mjs +127 -1
- package/scripts/authority-boundary-probe.mjs +4 -4
- package/scripts/backfill-started-at.mjs +2 -2
- package/scripts/census-merge-driver.mjs +33 -2
- package/scripts/citation-anchor-report.mjs +181 -0
- package/scripts/dead-names.mjs +1 -1
- package/scripts/deprecate-below.mjs +66 -8
- package/scripts/drain-preflight.mjs +1 -1
- package/scripts/e2e.mjs +174 -0
- package/scripts/env-audit.mjs +27 -0
- package/scripts/env-classify.mjs +20 -2
- package/scripts/freeze-example-run.mjs +20 -10
- package/scripts/live-surface-check.mjs +124 -41
- package/scripts/markdown-link-check.mjs +1 -1
- package/scripts/merge-shape-check.mjs +242 -0
- package/scripts/mint-names-in-force.mjs +5 -5
- package/scripts/mint-offered-territories.mjs +72 -0
- package/scripts/mint-public-residue.mjs +2 -2
- package/scripts/mint-reference-strip-backlog.mjs +2 -2
- package/scripts/mint-suite-census.mjs +75 -2
- package/scripts/mint-writing-standard-backlog.mjs +2 -2
- package/scripts/purge-runs.mjs +7 -7
- package/scripts/reconcile-runs.mjs +2 -2
- package/scripts/release-approve-parked.mjs +20 -2
- package/scripts/release-await-cut.mjs +120 -1
- package/scripts/release-note-required.mjs +76 -8
- package/scripts/report-header-render-check.mjs +164 -0
- package/shared/brand.mjs +27 -0
- package/shared/connect-clients.mjs +39 -11
- package/shared/env-aliases.mjs +1 -1
- package/shared/identifier-scan.mjs +65 -9
- package/shared/identifier-sentinels.mjs +22 -0
- package/shared/names-in-force.mjs +3 -1
- package/shared/offered-territories.json +738 -0
- package/shared/pre-rename-spellings.mjs +53 -0
- package/shared/reference-guard-classes.mjs +40 -2
- package/shared/stdio-connect.mjs +39 -4
- package/shared/tree-commit.mjs +48 -0
- /package/driver/skills/{prelim-register → clearance-register}/providers/euipo.md +0 -0
- /package/driver/skills/{prelim-register → clearance-register}/providers/free-tier.md +0 -0
- /package/driver/skills/{prelim-register → clearance-register}/providers/uspto-local.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/field-doctrine-pharma.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/firm-wide-reasoning.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/report-prose.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework-demo.manifest.json +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework-demo.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework-triage.manifest.json +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework-triage.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework.manifest.json +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/risk-framework.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/template-formatting.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/templates/email/generic.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/templates/search-request-form.html +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/worked-examples-demo.md +0 -0
- /package/driver/skills/{prelim-search → clearance-search}/worked-examples.md +0 -0
package/driver/progress.mjs
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
2
|
// Copyright 2026 Cordillera Sàrl. Additional terms under section 7 of the AGPL-3.0 apply — see ADDITIONAL-TERMS.md
|
|
3
|
-
// progress.mjs — live run status for the
|
|
3
|
+
// progress.mjs — live run status for the clearance-search driver.
|
|
4
4
|
//
|
|
5
5
|
// Two artifacts, both written by the driver into the FORWARDING agent's own workspace
|
|
6
6
|
// (so the agent's sandboxed read tool can see them — agents can't exec, so on-demand status is a
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// blindly incremented — so the resumable pipeline can re-drive a run without corrupting either file.
|
|
15
15
|
|
|
16
16
|
import { readFileSync, writeFileSync, renameSync, unlinkSync, readdirSync, statSync, existsSync } from "node:fs";
|
|
17
|
-
import { join, dirname } from "node:path";
|
|
17
|
+
import { join, dirname } from "node:path"; import { STUDIO_SEGMENT_RE } from "../shared/pre-rename-spellings.mjs";
|
|
18
18
|
import { DRIVER_DIR } from "../shared/driver-dir.mjs"; //
|
|
19
19
|
import { config } from "./driver.config.mjs";
|
|
20
20
|
import { batchMarkName } from "./mark-name.mjs";
|
|
@@ -29,7 +29,7 @@ import { engineCommit, engineCommitSource } from "./engine-build.mjs"; // —
|
|
|
29
29
|
// driver's execution units (fan-out register axes, skeptic-escalation re-runs, corrective re-synthesis,
|
|
30
30
|
// two refutation passes) onto a clean forward-only sequence, so the displayed step never jumps backward.
|
|
31
31
|
export const DISPLAY_STEPS = [
|
|
32
|
-
"Framing the matter", // 1 matter-frame,
|
|
32
|
+
"Framing the matter", // 1 matter-frame, clearance-variants
|
|
33
33
|
"Register sweeps", // 2 common-law + register-unit:* (fan-out + escalation re-runs collapse here)
|
|
34
34
|
"Placement & digest", // 3 placement-inquiry, register-digest (+ re-digest)
|
|
35
35
|
"Skeptic review", // 4 skeptic
|
|
@@ -48,7 +48,7 @@ export const DISPLAY_STEPS = [
|
|
|
48
48
|
// UNLABELLED GAP on the stepper the client watches — the run looks stalled while it is working. Three
|
|
49
49
|
// stages were sitting in that state (blind-frame, frame-diff, doubt-closure); each now says so by name.
|
|
50
50
|
export const STAGE_TO_STEP = {
|
|
51
|
-
"matter-frame": 0, "
|
|
51
|
+
"matter-frame": 0, "clearance-variants": 0,
|
|
52
52
|
"common-law": 1, "common-law-half": 1, "register-unit": 1,
|
|
53
53
|
"placement-inquiry": 2, "register-digest": 2,
|
|
54
54
|
skeptic: 3,
|
|
@@ -398,11 +398,21 @@ export function seedRunStatus(ctx, { resume = false } = {}) {
|
|
|
398
398
|
}, null, { critical: true }); // door B — the clearance lane's identity seed, same rule
|
|
399
399
|
}
|
|
400
400
|
|
|
401
|
-
//
|
|
401
|
+
// Record that the run is at `rawStageKey`: advance the displayed step where the stage has one, and say
|
|
402
|
+
// what the run is doing either way, then refresh STATUS.md.
|
|
403
|
+
//
|
|
404
|
+
// AN UNMAPPED STAGE MOVES NO STEP AND IS STILL SOMETHING THE RUN IS DOING. This returned early for one,
|
|
405
|
+
// so it wrote no `lastStage` at all — and `lastStage` is what status-snapshot publishes as "what the run
|
|
406
|
+
// is actually doing". Three stages have no display step ON PURPOSE (STAGE_NO_STEP names each and why),
|
|
407
|
+
// so for the whole of any of them every surface went on naming the PREVIOUS stage, which is the same
|
|
408
|
+
// defect as a stale step wearing a different field. The step fields are still withheld — that part of
|
|
409
|
+
// the early return was right, and an unmapped stage must never touch the displayed step.
|
|
402
410
|
export function recordTransition(ctx, rawStageKey) {
|
|
403
411
|
const step = stepForStage(rawStageKey);
|
|
404
|
-
|
|
405
|
-
|
|
412
|
+
writeRunStatus(ctx, {
|
|
413
|
+
...(step ? { stepIndex: step.index, stepLabel: step.label, stepN: step.n, stepTotal: step.total } : {}),
|
|
414
|
+
lastStage: rawStageKey,
|
|
415
|
+
});
|
|
406
416
|
rollupStatus(ctx?.run?.studioRoot);
|
|
407
417
|
}
|
|
408
418
|
|
|
@@ -424,7 +434,7 @@ function findStatusFiles(root, depth, acc) {
|
|
|
424
434
|
}
|
|
425
435
|
|
|
426
436
|
function agentFromStudioRoot(studioRoot) {
|
|
427
|
-
const m = new RegExp(`${config.workspacePrefixRe}([^/]+)/studio
|
|
437
|
+
const m = new RegExp(`${config.workspacePrefixRe}([^/]+)/studio/${STUDIO_SEGMENT_RE}/?$`).exec(studioRoot ?? "");
|
|
428
438
|
return m ? m[1] : "";
|
|
429
439
|
}
|
|
430
440
|
|
|
@@ -14,9 +14,9 @@
|
|
|
14
14
|
//
|
|
15
15
|
// This module is read-only and defensive: a missing/unreadable ledger or a torn last line (two gateway
|
|
16
16
|
// turns appending concurrently) returns zeros / skips that line — it never throws. The driver calls it at
|
|
17
|
-
// publish time to attribute THIS run's calls by the session-key prefix `
|
|
17
|
+
// publish time to attribute THIS run's calls by the session-key prefix `clearance-<slug>-<codename>-`.
|
|
18
18
|
|
|
19
|
-
import { readFileSync, existsSync } from "node:fs";
|
|
19
|
+
import { readFileSync, existsSync } from "node:fs"; import { runPrefixSpellings } from "../shared/pre-rename-spellings.mjs";
|
|
20
20
|
// Aliased: three functions below take a parameter literally named `ledgerPath`, and an unaliased
|
|
21
21
|
// import would be shadowed by it inside its own default-value expression (a TDZ ReferenceError
|
|
22
22
|
// at the first call, not at load).
|
|
@@ -46,7 +46,7 @@ export const DEFAULT_LEDGER_PATH = resolveLedgerPath("call");
|
|
|
46
46
|
export const KINDS = ["search", "record_fetch", "image", "phoneme", "batch_screen", "enumerate", "execute_plan", "propose_supplemental"];
|
|
47
47
|
|
|
48
48
|
// The gateway namespaces the driver's --session-key as `agent:<agentId>:<key>` before it reaches the
|
|
49
|
-
// plugin (confirmed on the first live run: sessionKey = `agent:clawdi:
|
|
49
|
+
// plugin (confirmed on the first live run: sessionKey = `agent:clawdi:clearance-<slug>-<codename>-…`). Strip
|
|
50
50
|
// that leading `agent:<id>:` namespace so the run prefix anchors at the real start of the caller's key —
|
|
51
51
|
// otherwise a bare startsWith("clearotron-…") matches nothing.
|
|
52
52
|
function stripGatewayNs(s) {
|
|
@@ -54,11 +54,11 @@ function stripGatewayNs(s) {
|
|
|
54
54
|
}
|
|
55
55
|
|
|
56
56
|
// Does this ledger row belong to the run identified by `runPrefix`? The driver's --session-key is
|
|
57
|
-
// `
|
|
57
|
+
// `clearance-<slug>-<codename>-<stage><axis>` (+ optional `-rerunN`), so a prefix match catches every stage +
|
|
58
58
|
// axis + retry of the run. We check sessionKey (carries the key) and, defensively, sessionId.
|
|
59
59
|
function rowMatchesRun(row, runPrefix) {
|
|
60
|
-
return stripGatewayNs(row.sessionKey).startsWith(
|
|
61
|
-
|| stripGatewayNs(row.sessionId).startsWith(
|
|
60
|
+
return runPrefixSpellings(runPrefix).some((rp) => stripGatewayNs(row.sessionKey).startsWith(rp)
|
|
61
|
+
|| stripGatewayNs(row.sessionId).startsWith(rp)); // either spelling: a run resumed across the rename
|
|
62
62
|
}
|
|
63
63
|
|
|
64
64
|
// ── band-truth gate (2026-07-14, teal-foundry): count the ledger rows attributed to ONE unit lane ──────
|
|
@@ -146,7 +146,7 @@ function emptyTally() {
|
|
|
146
146
|
/**
|
|
147
147
|
* Tally every ledger line whose gateway id starts with `runPrefix`.
|
|
148
148
|
* @param {string} ledgerPath path to the JSONL ledger
|
|
149
|
-
* @param {string} runPrefix e.g. `
|
|
149
|
+
* @param {string} runPrefix e.g. `clearance-acme-bluejay-` (note the trailing hyphen)
|
|
150
150
|
* @returns {object} the tally (see emptyTally) — never throws
|
|
151
151
|
*/
|
|
152
152
|
export function tallyRegisterCalls(ledgerPath = DEFAULT_LEDGER_PATH, runPrefix) {
|
|
@@ -208,7 +208,7 @@ export function tallyRegisterCalls(ledgerPath = DEFAULT_LEDGER_PATH, runPrefix)
|
|
|
208
208
|
* (missing/unreadable ledger or a torn last line ⇒ skips that line, never throws). A cache_hit record_fetch
|
|
209
209
|
* still counts — the URI WAS fetched this run, which is exactly what the gate asks.
|
|
210
210
|
* @param {string} ledgerPath path to the JSONL ledger
|
|
211
|
-
* @param {string} runPrefix e.g. `
|
|
211
|
+
* @param {string} runPrefix e.g. `clearance-acme-bluejay-` (note the trailing hyphen)
|
|
212
212
|
* @returns {Set<string>} the set of fetched record URIs
|
|
213
213
|
*/
|
|
214
214
|
export function fetchedRecordUris(ledgerPath = DEFAULT_LEDGER_PATH, runPrefix) {
|
package/driver/publish/index.mjs
CHANGED
|
@@ -16,7 +16,7 @@ import { buildAudit } from './xlsx.mjs';
|
|
|
16
16
|
import { parseFindingsJson, parseFindingsJsonLenient, deriveDisplayVerdict, joinFindingToBlock, CLIENT_TIER_BY_COMPOSITE, projectCoverageJudgment } from '../findings-model.mjs';
|
|
17
17
|
import { readStore, requiredAbsent, nonClosingAbsences } from './publish-inputs.mjs'; // — and why an absence did not close
|
|
18
18
|
import { clearanceReportData } from './report-data.mjs';
|
|
19
|
-
import { searchDepthRecord } from './search-depth.mjs'; // how much was read to reach the answer, as counts and tokens
|
|
19
|
+
import { searchDepthRecord, planTerritoriesOf } from './search-depth.mjs'; import { bandRecords } from '../named-band.mjs'; // how much was read to reach the answer, as counts and tokens
|
|
20
20
|
import { parseFrameworkManifest } from '../framework.mjs';
|
|
21
21
|
import { rollupTokens, servedModels } from '../tokens.mjs';
|
|
22
22
|
import { reportIdentityFor, productCoverageNote, isRegisterOnly } from '../search-policy.mjs';
|
|
@@ -817,6 +817,61 @@ export async function publishReport({ runId, codename, reportMd, auditMd, findin
|
|
|
817
817
|
reasons: droppedConditions.map((c) => c.note) });
|
|
818
818
|
} catch { /* the workbook row is the record that matters; this line is the second copy */ }
|
|
819
819
|
}
|
|
820
|
+
// ── THE CHECKS THE RUN DECIDED ON AND DID NOT MAKE ─────────────────────────────────────────────
|
|
821
|
+
//
|
|
822
|
+
// The recall net mints a probe per remembered conflict and a probe per owner behind one, then
|
|
823
|
+
// dispatches at most five owner probes. The excess is recorded in the run's own receipt and nothing
|
|
824
|
+
// downstream carried it to a reader, so a search that decided on nineteen ownership checks, made five
|
|
825
|
+
// and said nothing about the other fourteen read as a search that made the checks it wanted.
|
|
826
|
+
//
|
|
827
|
+
// The cap is not the defect: it is deliberate and the rows it drops are ranked material-first, so the
|
|
828
|
+
// five that run are the five that matter most. What was missing is the disclosure, and it lands where
|
|
829
|
+
// the 2026-09-17 ruling put the same class of fact — an ordinary row on the workbook's gaps sheet and
|
|
830
|
+
// one line in the run record. Nothing reaches the report page: the approved boards draw a forward
|
|
831
|
+
// decision and an open question back to the client, and a check nobody made is neither.
|
|
832
|
+
//
|
|
833
|
+
// THE WORDS ARE THE RECEIPT'S OWN. The party and the probe id are read from it verbatim; "over the
|
|
834
|
+
// cap" and "never dispatched" are the vocabulary the ask ledger already ships for these same rows.
|
|
835
|
+
// Nothing here composes a sentence, and the note carries no seam, so the gaps sheet's own splitter
|
|
836
|
+
// leaves "What was done" empty — which is the fact: nothing was done.
|
|
837
|
+
// READ THROUGH THE DECLARED HELPER, three states and not two. An absent receipt is a run whose recall
|
|
838
|
+
// net minted nothing — env-gated off, or a matter with no remembered conflict — and there is nothing
|
|
839
|
+
// to disclose. A DAMAGED one is a different fact: the probes may have overflowed and this publish
|
|
840
|
+
// cannot tell, so it says so in the run record instead of shipping the same empty sheet an
|
|
841
|
+
// everything-dispatched run ships. Collapsing those two is the defect publish-inputs.mjs exists for.
|
|
842
|
+
//
|
|
843
|
+
// NOT PUSHED ONTO `inputsAbsent`. That list rides meta.json's clientGate record, whose own note two
|
|
844
|
+
// hundred lines down is that it stays undefined when every store was found so a complete run's meta
|
|
845
|
+
// is byte-identical to one written before the list existed. Most runs have no recall receipt, so
|
|
846
|
+
// adding this store to it would change the meta of every archived run on re-render for a store that
|
|
847
|
+
// feeds a workbook row and not the gate -- the same reason the other presentation-only stores are
|
|
848
|
+
// not on it either.
|
|
849
|
+
// `runDir ?? dirname(reportMd)`, the same base its neighbours take and not a defensive flourish:
|
|
850
|
+
// publishReport's `runDir` is OPTIONAL, and a bare `readStore(runDir, …)` throws TypeError on an
|
|
851
|
+
// undefined base rather than reporting an absent store. Caught in CI by the graceful-stop runner arm,
|
|
852
|
+
// which publishes without one: the throw left publishReport at `published → null` and the runner
|
|
853
|
+
// exited 1. Reading from the report's own directory is also the right answer for a republish.
|
|
854
|
+
const recallStore = readStore(runDir ?? dirname(reportMd), '_driver/register-recall.json');
|
|
855
|
+
const undispatchedProbes = (recallStore.value?.overflow ?? [])
|
|
856
|
+
.filter((o) => o && (o.term || o.qid))
|
|
857
|
+
.map((o) => ({
|
|
858
|
+
area: String(o.term ?? o.qid),
|
|
859
|
+
state: 'not-searched',
|
|
860
|
+
note: `over the cap, never dispatched (${String(o.qid ?? 'no probe id recorded')})`,
|
|
861
|
+
}));
|
|
862
|
+
if (recallStore.state === 'damaged' && runDir) {
|
|
863
|
+
try {
|
|
864
|
+
runLog(runDir, { event: 'probe-over-cap-unreadable', store: recallStore.name, error: recallStore.error });
|
|
865
|
+
} catch { /* best-effort, as below */ }
|
|
866
|
+
}
|
|
867
|
+
// Best-effort, as above: a publish never fails for want of a log line.
|
|
868
|
+
if (undispatchedProbes.length && runDir) {
|
|
869
|
+
try {
|
|
870
|
+
runLog(runDir, { event: 'probe-over-cap-undispatched', n: undispatchedProbes.length,
|
|
871
|
+
parties: undispatchedProbes.map((p) => p.area) });
|
|
872
|
+
} catch { /* the workbook row is the record that matters; this line is the second copy */ }
|
|
873
|
+
}
|
|
874
|
+
|
|
820
875
|
// doc 50 — the run's FROZEN framework manifest (band vocabulary). Present on band-doctrine runs;
|
|
821
876
|
// absent on every archived run (they render byte-identically on the legacy paths).
|
|
822
877
|
let framework = null;
|
|
@@ -845,11 +900,25 @@ export async function publishReport({ runId, codename, reportMd, auditMd, findin
|
|
|
845
900
|
// two must not render the same way. Null on archived runs ⇒ render keeps the prose fallback.
|
|
846
901
|
let scopeBasis = null;
|
|
847
902
|
let searchedJurisdictions = [];
|
|
903
|
+
// WHAT WAS SEARCHED IS THE PLAN'S ANSWER, NOT THE ARCHIVE'S, and it is carried separately from the
|
|
904
|
+
// header's list below because the two are used for different things and one of them is deliberately
|
|
905
|
+
// emptied. The coverage presentation asks which territories the run searched and which it could not
|
|
906
|
+
// reach; the plan compiles both — entries carry the regions it will query, `deferred_coverage` carries
|
|
907
|
+
// the ones this provider does not cover with the reason each. A register that archives no records
|
|
908
|
+
// still has both, which is the whole point: keyed off the record store, a provider that keeps nothing
|
|
909
|
+
// looked like a provider nobody asked.
|
|
910
|
+
//
|
|
911
|
+
// THREE-VALUED like the record listing above it: null means there is no plan to read, so the run
|
|
912
|
+
// cannot say — an archived or legacy run — and it must not be confused with a plan that named nothing.
|
|
913
|
+
let planTerritories = null;
|
|
848
914
|
try {
|
|
849
915
|
const plan = JSON.parse(readFileSync(driverDir(runDir ?? dirname(reportMd), 'register-plan.json'), 'utf8'));
|
|
850
916
|
if (plan?.scope_basis === 'worldwide') scopeBasis = 'worldwide';
|
|
851
917
|
searchedJurisdictions = [...new Set((plan?.entries ?? []).flatMap((e) => Array.isArray(e.regions) ? e.regions : []))];
|
|
852
918
|
if (!searchedJurisdictions.length && Array.isArray(plan?.regions)) searchedJurisdictions = [...new Set(plan.regions)];
|
|
919
|
+
// Derived here, BEFORE the worldwide clearing below — that clearing exists for the header and would
|
|
920
|
+
// otherwise empty this on exactly the runs that cover the most ground.
|
|
921
|
+
planTerritories = planTerritoriesOf(plan);
|
|
853
922
|
} catch { /* no plan sidecar — try the instructed scope */ }
|
|
854
923
|
if (!searchedJurisdictions.length) {
|
|
855
924
|
try {
|
|
@@ -1065,7 +1134,7 @@ export async function publishReport({ runId, codename, reportMd, auditMd, findin
|
|
|
1065
1134
|
// the same rule (the workbook's own BANNED gate had already started firing on the raw detail —
|
|
1066
1135
|
// advisory, so CI stayed green). reviewReceipts.lint keeps its raw detail for the internal
|
|
1067
1136
|
// readers above (fetchState reads registry-record-coverage's URIs out of it).
|
|
1068
|
-
counts = await buildAudit({ droppedConditions, findings, coverage, contextNotes, coverageJudgment, markAssessment, corrections: correctionsDoc, fetchState, verdict: verdictInfo, jurisdiction, commonLawJoinedTerms, registerOnly, clientGate, lintFailures: deliveryFlagLines(reviewReceipts.lint), productName, registerPublishesRecordPages: runOrigins == null ? null : runOrigins.length > 0, recordLinks: officeLinks?.byUri ?? null }, auditParsed, join(poolRunDir, auditFile), fm.title, fm);
|
|
1137
|
+
counts = await buildAudit({ droppedConditions, undispatchedProbes, findings, coverage, contextNotes, coverageJudgment, markAssessment, corrections: correctionsDoc, fetchState, verdict: verdictInfo, jurisdiction, commonLawJoinedTerms, registerOnly, clientGate, lintFailures: deliveryFlagLines(reviewReceipts.lint), productName, registerPublishesRecordPages: runOrigins == null ? null : runOrigins.length > 0, recordLinks: officeLinks?.byUri ?? null }, auditParsed, join(poolRunDir, auditFile), fm.title, fm);
|
|
1069
1138
|
grpRead(join(poolRunDir, auditFile), 0o640);
|
|
1070
1139
|
if (counts?.gateViolations?.length) console.warn(`[audit-workbook] advisory: ${counts.gateViolations.join(' | ')}`);
|
|
1071
1140
|
} catch (e) {
|
|
@@ -1098,6 +1167,35 @@ export async function publishReport({ runId, codename, reportMd, auditMd, findin
|
|
|
1098
1167
|
// pattern — a run with no grid or no case-law layer still gets its register counts, and the absent
|
|
1099
1168
|
// ones report themselves as zero or `not-in-scope` rather than as a gap nobody can see.
|
|
1100
1169
|
let searchDepth = null;
|
|
1170
|
+
// HOW DEEP THE LOCAL-LANGUAGE INVESTIGATION WENT, and it is derived by the one author of that
|
|
1171
|
+
// question rather than re-read here. The import is LAZY and gated on the run's own sidecar existing,
|
|
1172
|
+
// which keeps the property the pipeline's own fold keeps: a plain clearance never loads the jx
|
|
1173
|
+
// machinery at all. No sidecar means the component never ran, which the fold reports as not-in-scope.
|
|
1174
|
+
let laneDepthVerdicts = null;
|
|
1175
|
+
try {
|
|
1176
|
+
const jxSidecar = driverDir(runDir ?? dirname(reportMd), 'jx-lanes.json');
|
|
1177
|
+
if (existsSync(jxSidecar)) {
|
|
1178
|
+
const sidecar = JSON.parse(readFileSync(jxSidecar, 'utf8'));
|
|
1179
|
+
const { laneDepthOfRun } = await import('../jx.mjs');
|
|
1180
|
+
let units = null;
|
|
1181
|
+
try { units = JSON.parse(readFileSync(driverDir(runDir ?? dirname(reportMd), 'jx/units.json'), 'utf8')); } catch { /* the statement handles an absent units file */ }
|
|
1182
|
+
// THE RUN ALREADY ANSWERED THIS, AND THE STAMPED ANSWER IS THE ONE THAT COUNTS.
|
|
1183
|
+
//
|
|
1184
|
+
// `deriveJxSliceStatement` writes `fold.depth` at delivery, from the run's OWN environment, for the
|
|
1185
|
+
// reason the seam exists at all: the arms are environment, so a verdict derived later can only speak
|
|
1186
|
+
// for the box it is derived on. Publishing re-derived it here — a second author for a fact the run
|
|
1187
|
+
// had already stated — and the two disagreed on a delivered client report.
|
|
1188
|
+
//
|
|
1189
|
+
// The disagreement was not subtle. This call passed `deriveJxSliceStatement`'s whole return,
|
|
1190
|
+
// `{executes, slices}`, where the function reads `slices.candidates` — so the lookup found nothing,
|
|
1191
|
+
// every lane's `ran` came back null whatever it had really done, and a full-country JP run that
|
|
1192
|
+
// searched in Japanese printed "Local-language investigation · Not run this run". The stamped
|
|
1193
|
+
// verdict on the same run's sidecar said `ran: "candidates"`, correctly.
|
|
1194
|
+
//
|
|
1195
|
+
// So: read what the run stated, and derive only for a run delivered before the stamp existed.
|
|
1196
|
+
laneDepthVerdicts = laneDepthOfRun({ sidecar, units });
|
|
1197
|
+
}
|
|
1198
|
+
} catch { /* an unreadable sidecar reports as not-in-scope rather than failing a publish */ }
|
|
1101
1199
|
try {
|
|
1102
1200
|
const runBase = runDir ?? dirname(reportMd);
|
|
1103
1201
|
const recDir = join(runBase, '_records');
|
|
@@ -1106,15 +1204,22 @@ export async function publishReport({ runId, codename, reportMd, auditMd, findin
|
|
|
1106
1204
|
searchDepth = searchDepthRecord({
|
|
1107
1205
|
auditMd: (auditMd && existsSync(auditMd)) ? rdText(auditMd) : '',
|
|
1108
1206
|
recordIndex: recordsByUri ?? {},
|
|
1109
|
-
|
|
1207
|
+
// null, NOT []: a run whose provider archives no records has no `_records/` at all, and an empty
|
|
1208
|
+
// array is a register that was searched and returned nothing. They are different facts and the
|
|
1209
|
+
// page says different things about them, so the distinction this line already computes is kept
|
|
1210
|
+
// rather than thrown away one character later.
|
|
1211
|
+
recordFileNames: existsSync(recDir) ? readdirSync(recDir) : null,
|
|
1212
|
+
// …and where there is no archive, what the register returned: the band's own record ids.
|
|
1213
|
+
bandRecordIds: existsSync(recDir) ? null : (() => { try { const b = rdJson(join(runBase, 'register-named-band.json')); return b ? bandRecords(b).map((r) => r?.record_id).filter(Boolean) : null; } catch { return null; } })(),
|
|
1110
1214
|
commonLawGrid: rdJson(join(runBase, 'common-law-grid.json')),
|
|
1111
1215
|
caseLawText: rdText(join(dirname(reportMd), 'case-law-findings.md')),
|
|
1112
1216
|
registerPlan: rdJson(driverDir(runBase, 'register-plan.json')),
|
|
1217
|
+
laneDepthVerdicts,
|
|
1113
1218
|
});
|
|
1114
1219
|
writeRO('search-depth.json', JSON.stringify(searchDepth, null, 2));
|
|
1115
1220
|
} catch { /* the depth record is additive — a publish never fails for want of it */ }
|
|
1116
1221
|
|
|
1117
|
-
writeRO('report.html', renderHtml(parsed, findings, coverage, { demoData, servedModels: served, productName, depthNote, scopeBasis, auditFile: auditFile || undefined, runId, delivery: deliv, recordsByUri, contextNotes, coverageJudgment: coverageJudgmentDisplay, markAssessment, fourAnswers, homeHref: '../index.html', nav: reportNav, chromeHref: '../assets/chrome.css', issued, asOf, verdictInfo, framework, searchedJurisdictions, caseLawByOrdinal, caseLawNotice, enforcerSignals, recordOrigin, recordOrigins: runOrigins, recordCitation: runProviderConf?.recordCitation ?? null, recordLinks: officeLinks?.byUri ?? null, providerLabel, seniorRights, findingsSchemaVersion, searchDepth }));
|
|
1222
|
+
writeRO('report.html', renderHtml(parsed, findings, coverage, { demoData, servedModels: served, productName, depthNote, scopeBasis, auditFile: auditFile || undefined, runId, delivery: deliv, recordsByUri, contextNotes, coverageJudgment: coverageJudgmentDisplay, markAssessment, fourAnswers, homeHref: '../index.html', nav: reportNav, chromeHref: '../assets/chrome.css', issued, asOf, verdictInfo, framework, searchedJurisdictions, planTerritories, caseLawByOrdinal, caseLawNotice, enforcerSignals, recordOrigin, recordOrigins: runOrigins, recordCitation: runProviderConf?.recordCitation ?? null, recordLinks: officeLinks?.byUri ?? null, providerLabel, seniorRights, findingsSchemaVersion, searchDepth }));
|
|
1118
1223
|
// ONE report (spec 2026-07-30 §5): report.client.html is no longer written. The knockout lane's own
|
|
1119
1224
|
// collapse note is the precedent: "two renderings of one run is how the wrong link gets sent". The
|
|
1120
1225
|
// client host serves the same report.html through the portal's readReport() (cleaning built in) — its
|
|
@@ -1486,7 +1591,7 @@ export function composeEmailBody(reportMdPath, url, auditFile, productName = nul
|
|
|
1486
1591
|
const FONT = "font-family:Calibri,'Segoe UI',Arial,sans-serif";
|
|
1487
1592
|
|
|
1488
1593
|
// 5-tier reporting-template risk scheme — the whole left "NAME / RISK RATING" cell takes the bg colour.
|
|
1489
|
-
// Matches skills/
|
|
1594
|
+
// Matches skills/clearance-search/templates/search-request-form.html + knockout-searches/template-formatting.md.
|
|
1490
1595
|
// `txt` = a readable text-colour version of the tier (for the EXECUTIVE SUMMARY risk phrase, where a bright
|
|
1491
1596
|
// fill like yellow/red is unreadable as text). Order matters: VERY HIGH before HIGH.
|
|
1492
1597
|
export const RISK_TIERS = [
|
|
@@ -7,14 +7,14 @@
|
|
|
7
7
|
// self-reported trail), meta.json batch stamps, and the email composer. Deliberately separate from publishReport (single-clearance-shaped); shares the exported
|
|
8
8
|
// palette (TONE_TIER, resolved against the run's FROZEN framework) so driver and interactive output
|
|
9
9
|
// can never drift and a knockout never reads the clearance composer's module state.
|
|
10
|
-
import { readFileSync, writeFileSync, mkdirSync, chmodSync, existsSync } from 'node:fs';
|
|
10
|
+
import { readFileSync, writeFileSync, copyFileSync, mkdirSync, chmodSync, existsSync } from 'node:fs';
|
|
11
11
|
import { join } from 'node:path';
|
|
12
12
|
import { driverDir, ensureDriverDir } from '../../shared/driver-dir.mjs'; //
|
|
13
13
|
import { riskTier, TONE_TIER, regenIndex, regenSurfaces, auditRouteFor, markReportRouteFor, reportRouteFor } from './index.mjs';
|
|
14
14
|
import { runKnockoutLint, deliveryFlagLines } from '../predelivery-lint.mjs';
|
|
15
15
|
import { note } from '../log.mjs';
|
|
16
16
|
import { addSheet } from './xlsx.mjs';
|
|
17
|
-
import { COUNT_PREDICATES, COUNT_BASIS, countsForMark, countLine, countedMarks, variantFormsLine } from '../register-count.mjs';
|
|
17
|
+
import { COUNT_PREDICATES, COUNT_BASIS, countsForMark, countLine, countedMarks, variantFormsLine, disclosedFloor, moreThan } from '../register-count.mjs';
|
|
18
18
|
import { RECORD_BASIS, recordsForMark, recordsLine, listedMarks, normalizeRegisterRecordLinks } from '../register-records.mjs';
|
|
19
19
|
import { reportIdentityFor, productCoverageNote, kebab } from '../search-policy.mjs';
|
|
20
20
|
import { batchMarkName } from '../mark-name.mjs';
|
|
@@ -143,7 +143,8 @@ export async function buildKnockoutWorkbook(findings, receipts, outPath, registe
|
|
|
143
143
|
if (noteRows.length) addSheet(wb, 'Working Notes', ['Mark', 'Type', 'Note'], noteRows);
|
|
144
144
|
|
|
145
145
|
if (registerCounts?.marks?.length) {
|
|
146
|
-
|
|
146
|
+
// A floor the register disclosed is its own figure, printed as the report prints it.
|
|
147
|
+
const cell = (c) => (Number.isFinite(c?.total) ? c.total : disclosedFloor(c) !== null ? moreThan(disclosedFloor(c)) : 'not available');
|
|
147
148
|
const countRows = (findings.marks ?? []).map((m) => {
|
|
148
149
|
const e = countsForMark(registerCounts, m.name);
|
|
149
150
|
const row = {
|
|
@@ -161,7 +162,7 @@ export async function buildKnockoutWorkbook(findings, receipts, outPath, registe
|
|
|
161
162
|
row['Basis'] = COUNT_BASIS;
|
|
162
163
|
// The verbatim provider reason for anything missing — the auditable half of "not available".
|
|
163
164
|
row['Notes'] = COUNT_PREDICATES
|
|
164
|
-
.filter((p) => !Number.isFinite(e?.counts?.[p.key]?.total))
|
|
165
|
+
.filter((p) => !Number.isFinite(e?.counts?.[p.key]?.total) && disclosedFloor(e?.counts?.[p.key]) === null)
|
|
165
166
|
.map((p) => `${p.label}: ${e?.counts?.[p.key]?.unavailable ?? 'no count recorded'}`)
|
|
166
167
|
.join(' · ');
|
|
167
168
|
return row;
|
|
@@ -335,6 +336,30 @@ export async function publishKnockout({ runId, codename, runDir, findings, plan,
|
|
|
335
336
|
const poolRunDir = join(poolRoot, runId);
|
|
336
337
|
mkdirSync(poolRunDir, { recursive: true });
|
|
337
338
|
const writeRO = (name, data) => { const p = join(poolRunDir, name); writeFileSync(p, data); grpRead(p, 0o640); };
|
|
339
|
+
|
|
340
|
+
// ── THE RUN'S WORKING RECORD TRAVELS WITH THE REPORT ──────────────────────────────────────────────
|
|
341
|
+
//
|
|
342
|
+
// The delivered directory held the report, its data and the workbook, and nothing the engine itself
|
|
343
|
+
// wrote. That is what the acceptance read needs to tell a line the ENGINE wrote from a line the
|
|
344
|
+
// RENDERER invented: without it the check can still place a line as report text or as a sanctioned
|
|
345
|
+
// label, so it passes — and cannot fail in the way it exists to fail. A clearance run's record is
|
|
346
|
+
// `audit.md` and `findings.json`; this lane's own names are these two, and they are copied rather
|
|
347
|
+
// than rebuilt so the pool carries the bytes the stages wrote.
|
|
348
|
+
//
|
|
349
|
+
// AN ABSENT ONE IS SAID, NOT SKIPPED. A run whose stage never wrote its assessment is a different
|
|
350
|
+
// thing from a run that wrote one, and a publisher that copies what it finds and says nothing about
|
|
351
|
+
// what it does not makes the two look identical to the next reader — which is the whole defect this
|
|
352
|
+
// closes, one directory along.
|
|
353
|
+
for (const name of ['knockout-findings.json', 'knockout-assessment.md']) {
|
|
354
|
+
const src = join(runDir, name);
|
|
355
|
+
if (existsSync(src)) {
|
|
356
|
+
const dest = join(poolRunDir, name);
|
|
357
|
+
copyFileSync(src, dest);
|
|
358
|
+
grpRead(dest, 0o640);
|
|
359
|
+
} else {
|
|
360
|
+
note(`knockout working record: ${name} is not in the run directory, so the delivered report has no engine text to be checked against`);
|
|
361
|
+
}
|
|
362
|
+
}
|
|
338
363
|
const issued = (() => {
|
|
339
364
|
try {
|
|
340
365
|
const d = new Date();
|
|
@@ -116,7 +116,7 @@ function linkHome(poolDir) {
|
|
|
116
116
|
|
|
117
117
|
// Locate an archived run's WORKSPACE dir (the publishReport input set: raw report.md, findings.json,
|
|
118
118
|
// _driver/ sidecars) across every agent workspace. Layout (pipeline archive step):
|
|
119
|
-
// <workspaceRoot>/<workspacePrefix><agent>/studio/
|
|
119
|
+
// <workspaceRoot>/<workspacePrefix><agent>/studio/clearance-search/archive/<month|legacy-*>/<matter-slug>/<date-codename>/
|
|
120
120
|
// where `<matter-slug>-<date-codename>` === runId. Returns every match (ambiguity is the caller's error).
|
|
121
121
|
// The roots route through driver.config.mjs's workspace helpers (never inline the `workspace-` literal —
|
|
122
122
|
// a deployment picks its own prefix via CLEAROTRON_WORKSPACE_PREFIX / CLEAROTRON_WORK_DIR).
|
|
@@ -17,7 +17,7 @@
|
|
|
17
17
|
// contract the publication gate held before it was retired into tests at — pass, fail, and COULD
|
|
18
18
|
// NOT RUN, never folded into two — carried to the seam where a run becomes a client artifact.
|
|
19
19
|
//
|
|
20
|
-
// Modelled on mcp-server/lib/coverage.mjs
|
|
20
|
+
// Modelled on assertValidatorCoverage() in mcp-server/lib/coverage.mjs (lines 65-75): a closed partition asserted
|
|
21
21
|
// at LOAD, so a new store cannot be added without consciously choosing its gating. The failure that
|
|
22
22
|
// mechanism exists to prevent is the — a hand-copied mirror that silently stopped covering an
|
|
23
23
|
// artifact and still answered "complete: yes" about a file it never checked.
|
|
@@ -57,10 +57,26 @@ export const PUBLISH_INPUTS = {
|
|
|
57
57
|
"_driver/verdict.json": "optional",
|
|
58
58
|
// doc 50 — the frozen band vocabulary. Present on band-doctrine runs only, by design.
|
|
59
59
|
"_driver/framework.json": "optional",
|
|
60
|
+
// The recall net's receipt. Its overflow[] carries the probes the run minted and did not dispatch,
|
|
61
|
+
// one row each on the workbook's gaps sheet. `optional`, and the reason is the recall net's own
|
|
62
|
+
// switches: the lane is env-gated for rollback and mints nothing when a matter has no remembered
|
|
63
|
+
// conflict, so an absent receipt is a run that had nothing to record rather than one that lost it.
|
|
64
|
+
// `required` would close the gate on every knockout and every matter with a clean history.
|
|
65
|
+
"_driver/register-recall.json": "optional",
|
|
60
66
|
// T6 (D4) — the frozen register plan; the render falls back to the coverage prose without it.
|
|
61
67
|
"_driver/register-plan.json": "optional",
|
|
68
|
+
// The band the register returned, read ONLY where the run has no `_records/` archive, to count what was
|
|
69
|
+
// read and where. Optional: a knockout, a legacy run and a register-less run have none, and an absent
|
|
70
|
+
// band leaves the count as "cannot say" exactly as before.
|
|
71
|
+
"register-named-band.json": "optional",
|
|
62
72
|
// The instructed scope, read only as the register plan's fallback for the searched-jurisdiction set.
|
|
63
73
|
"_driver/instructed-scope.json": "optional",
|
|
74
|
+
// The frozen local-language lane decision, and the units the fold wrote. Read to derive how deep that
|
|
75
|
+
// investigation went against what the matter configured — through the engine's own asked-versus-ran
|
|
76
|
+
// reader, not by re-deciding it here. Optional and genuinely so: a plain clearance never runs the
|
|
77
|
+
// component and carries neither file, which is the state that folds to not-in-scope.
|
|
78
|
+
"_driver/jx-lanes.json": "optional",
|
|
79
|
+
"_driver/jx/units.json": "optional",
|
|
64
80
|
// T7 (E5) — the grounded case-law profiles. A run with no case-law layer legitimately has none.
|
|
65
81
|
"case-law-findings.md": "optional",
|
|
66
82
|
// T7 (E6) — Corsearch enforcement telemetry; presentation-only, absent ⇒ no lines.
|
|
@@ -104,7 +120,7 @@ export const CALLER_SUPPLIED = {
|
|
|
104
120
|
};
|
|
105
121
|
|
|
106
122
|
/**
|
|
107
|
-
* Load-time gate, on
|
|
123
|
+
* Load-time gate, modelled on assertValidatorCoverage() in mcp-server/lib/coverage.mjs (lines 64-73).
|
|
108
124
|
*
|
|
109
125
|
* Asserts the table is a well-formed closed partition: every gating is one of the two words, no store
|
|
110
126
|
* is declared in more than one table, and no declaration is blank. It deliberately does NOT scan
|