clearotron 0.3.2-beta.7 → 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 +58 -26
- package/CONTRIBUTING.md +8 -8
- package/INSTALL.md +148 -81
- package/README.md +3 -3
- package/SECURITY.md +3 -3
- package/bin/brandowner.mjs +3 -3
- package/bin/framework-preflight.mjs +1 -1
- package/bin/onboard.mjs +637 -216
- package/bin/start.mjs +151 -27
- package/bin/update.mjs +58 -11
- 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 +32 -14
- package/docs/architecture/05-config-governance.md +23 -8
- 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 +124 -0
- package/driver/README.md +3 -3
- package/driver/band-size.mjs +59 -0
- 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/config-inventory.mjs +112 -9
- package/driver/consumption-ledger.mjs +2 -2
- package/driver/contract-arm2-baseline.json +2 -5
- package/driver/contract-dictation-registry.mjs +19 -19
- package/driver/contract-e3-backlog.mjs +43 -43
- package/driver/contract-e3-baseline.json +14 -14
- package/driver/contract-vocabulary.mjs +68 -27
- 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/door-gates.mjs +41 -7
- package/driver/doubt-ledger.mjs +2 -2
- package/driver/drainer-identity.mjs +34 -8
- package/driver/driver.config.mjs +367 -104
- package/driver/engine/CONTRACT.md +10 -3
- package/driver/engine/README.md +2 -2
- package/driver/engine/anthropic-agent.mjs +77 -21
- package/driver/engine/auth.mjs +129 -10
- package/driver/engine/jx-turn.mjs +7 -6
- 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 +18 -5
- package/driver/engine/openai-agent.mjs +4 -2
- package/driver/engine/probe.mjs +110 -23
- package/driver/enqueue-schema.mjs +6 -2
- package/driver/findings-model.mjs +6 -3
- package/driver/flag-snapshot.mjs +34 -8
- package/driver/form-neighbourhood.mjs +54 -7
- package/driver/framework.mjs +4 -4
- package/driver/gateway.mjs +36 -24
- package/driver/jx-lanes.mjs +23 -4
- package/driver/jx-units.mjs +7 -4
- package/driver/jx.mjs +34 -4
- package/driver/knockout-review-record.mjs +56 -4
- package/driver/known-conflicts.mjs +1 -1
- package/driver/matter-frame-record.mjs +90 -1
- package/driver/named-band.mjs +1 -1
- 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 +396 -81
- package/driver/placement-form.mjs +77 -1
- package/driver/placement-model.mjs +1 -1
- package/driver/portal-config-view.mjs +30 -1
- package/driver/portal-report.mjs +107 -6
- package/driver/portal-service.mjs +80 -14
- package/driver/portal-upstream.mjs +1 -1
- package/driver/predelivery-lint.mjs +12 -2
- 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 +154 -8
- package/driver/publish/knockout.mjs +39 -5
- package/driver/publish/pool-admin.mjs +1 -1
- package/driver/publish/publish-inputs.mjs +18 -2
- package/driver/publish/render-knockout.mjs +184 -31
- package/driver/publish/render.mjs +323 -93
- package/driver/publish/report-data.mjs +4 -1
- package/driver/publish/report-topbar.mjs +58 -0
- package/driver/publish/search-depth.mjs +133 -4
- package/driver/publish/templates/report.css +78 -4
- package/driver/publish/xlsx.mjs +20 -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-availability.mjs +2 -2
- package/driver/register-count.mjs +50 -5
- package/driver/register-coverage.mjs +161 -1
- package/driver/register-digest-record.mjs +236 -11
- package/driver/register-grant-vocabulary.mjs +1 -1
- package/driver/register-plan.mjs +189 -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/result-noun-fields.mjs +2 -2
- package/driver/run-economics.mjs +41 -10
- package/driver/run-requirements.mjs +173 -9
- package/driver/runner.mjs +5 -5
- 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 +65 -61
- package/driver/status-snapshot.mjs +2 -2
- package/driver/suite-census.json +340 -136
- package/driver/surface-exit-verdict.mjs +58 -0
- package/driver/systemd/README.md +9 -6
- package/driver/systemd/clearotron-worker.service +1 -1
- package/driver/terminal-clamp.mjs +109 -1
- package/driver/tokens.mjs +169 -3
- package/driver/unit-environment.mjs +42 -15
- package/driver/unit-inventory.mjs +19 -2
- 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 +94 -6
- package/driver/whatif-queue.mjs +1 -1
- package/driver/wordlists/en.txt +63906 -0
- package/mcp-server/CHANGELOG.md +8 -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 +18 -1
- package/package.json +12 -11
- package/portal-ui/dist/assets/{index-CVOIvdhc.css → index-CtvwLCti.css} +207 -3
- package/portal-ui/dist/assets/{index-5UyqAyNM.js → index-EVaSo5-g.js} +1580 -527
- 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/jx/README.md +2 -1
- package/providers/jx/src/turn-envelope.mjs +8 -3
- package/providers/oauth-mcp-bridge/CHANGELOG.md +8 -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/README.md +1 -1
- 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 +8 -6
- 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 +39 -6
- package/scripts/env-classify.mjs +20 -2
- package/scripts/freeze-example-run.mjs +61 -18
- package/scripts/generated-files-are-current.mjs +69 -4
- 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/scripts/settings-render-check.mjs +75 -2
- package/scripts/test-full.mjs +96 -3
- package/scripts/test-run.mjs +10 -0
- package/shared/brand.mjs +27 -0
- package/shared/connect-clients.mjs +39 -11
- package/shared/deployment-box.mjs +7 -2
- package/shared/driver-dir.mjs +1 -1
- 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 +4 -2
- 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
|
@@ -44,6 +44,9 @@
|
|
|
44
44
|
// BLOCKING — without these nothing runs at all, in any product. The register and its credential (the
|
|
45
45
|
// driver throws by name at the first stage), the engine and the binary it drives, and the pool the
|
|
46
46
|
// report is written into. This is the set whose absence produced the outcome at the top of this file.
|
|
47
|
+
// And what the billing word needs, because the run door refuses without it before any turn: under
|
|
48
|
+
// `cloud` the switch of the cloud it pays through (or the gateway address), under `api-key` the
|
|
49
|
+
// engine's key; and the billing word itself, whenever the run door refuses the way it is set.
|
|
47
50
|
//
|
|
48
51
|
// NARROWING — `PERPLEXITY_API_KEY`. Its absence does NOT crash a run and does not deliver a false
|
|
49
52
|
// notice: the three clearance searches carry the common-law grid and cannot switch it off, so they
|
|
@@ -71,13 +74,16 @@
|
|
|
71
74
|
// Refusing here is refusing over OUR bug, and there is nothing for a reader to go and set.
|
|
72
75
|
//
|
|
73
76
|
// at:"order" the value an OPERATOR supplies — the register, its credential, the engine and the
|
|
74
|
-
// binary it drives. Absent, the install is not finished.
|
|
75
|
-
// AT ORDER TIME, before a stage dispatches and before
|
|
77
|
+
// binary it drives, and what the billing word needs (above). Absent, the install is not finished.
|
|
78
|
+
// The doors come up and every run is refused AT ORDER TIME, before a stage dispatches and before
|
|
79
|
+
// anything is spent, naming what is missing.
|
|
76
80
|
//
|
|
77
81
|
// The axis is on the ROW, not in the caller, for the reason the whole module exists: a start that
|
|
78
82
|
// decides for itself which names are its own and a runner that decides separately are two opinions
|
|
79
83
|
// about one list, and they drift in the direction where the second asks for less.
|
|
80
84
|
|
|
85
|
+
import { BILLING_MODES, CLOUD_SWITCH, CLOUD_SETTINGS, CLOUD_CREDENTIAL_CHECK, billingMode, cloudsSwitchedOn, resolveAuthMode } from "./engine/auth.mjs"; // — the billing row asks the one resolver which ways an engine can be paid for, and the cloud rows read its own lists
|
|
86
|
+
|
|
81
87
|
/** The pool a report is written into. Named once; the supervisor already writes it. */
|
|
82
88
|
export const POOL_ENV = "CLEAROTRON_REPORTS_DIR";
|
|
83
89
|
/** The register selection, and the engine selection. */
|
|
@@ -85,6 +91,9 @@ export const REGISTER_ENV = "CLEAROTRON_DATABASE";
|
|
|
85
91
|
export const ENGINE_ENV = "CLEAROTRON_AI";
|
|
86
92
|
/** Narrowing, never blocking — see the header. */
|
|
87
93
|
export const RESEARCH_ENV = "PERPLEXITY_API_KEY";
|
|
94
|
+
/** The settings that decide WHICH cloud a Claude turn goes to: each cloud's switch, and the gateway address.
|
|
95
|
+
* Any one of them answers the cloud billing word; start compares all of them against the services' file. */
|
|
96
|
+
export const CLOUD_ROUTES = Object.freeze([...Object.values(CLOUD_SWITCH), "ANTHROPIC_BASE_URL"]);
|
|
88
97
|
|
|
89
98
|
/** WHEN a blocking value is asked for. `START` is what `clearotron start` writes itself; `ORDER` is what
|
|
90
99
|
* an operator configures, and its absence refuses a RUN rather than an install. See the header. */
|
|
@@ -93,6 +102,35 @@ export const ORDER = "order";
|
|
|
93
102
|
|
|
94
103
|
const val = (env, name) => String(env?.[name] ?? "").trim();
|
|
95
104
|
|
|
105
|
+
// THE WAYS AN ENGINE CAN BE PAID FOR, AS THE BILLING ROW SAYS THEM. The row named all three for every engine,
|
|
106
|
+
// and Codex refuses a cloud account (engine/auth.mjs, resolveAuthMode), so a Codex install was offered a way
|
|
107
|
+
// to pay its run door refuses.
|
|
108
|
+
const PAY_WORDS = Object.freeze({ subscription: "subscription", "api-key": "API key", cloud: "cloud account" });
|
|
109
|
+
|
|
110
|
+
/**
|
|
111
|
+
* The billing words `engineId` can be paid by, in BILLING_MODES order: each one asked of the resolver, the
|
|
112
|
+
* one authority on it, with what that word needs set (the engine's key for `api-key`, a cloud's switch for
|
|
113
|
+
* `cloud`), so a word the resolver accepts only when its settings are present is not read as refused. An
|
|
114
|
+
* import of a leaf with no imports of its own, so this module stays pure.
|
|
115
|
+
*/
|
|
116
|
+
export function payWays(engineId, engine = {}) {
|
|
117
|
+
return BILLING_MODES.filter((mode) => {
|
|
118
|
+
const env = { [engine.authEnv ?? "CLEAROTRON_AI_BILLING"]: mode,
|
|
119
|
+
...(mode === "api-key" && engine.apiKeyEnv ? { [engine.apiKeyEnv]: "set" } : {}),
|
|
120
|
+
...(mode === "cloud" ? { [CLOUD_SWITCH.vertex]: "1" } : {}) };
|
|
121
|
+
try { resolveAuthMode({ engineName: engineId, env }); return true; } catch { return false; }
|
|
122
|
+
});
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* A billing refusal from the run door, in words that carry no value. Every refusal names settings and the
|
|
127
|
+
* billing word, except the one for a word that is not a billing mode, which quotes the word as it is set;
|
|
128
|
+
* that quote is replaced by the setting's name. PURE. Any other message comes back unchanged.
|
|
129
|
+
*/
|
|
130
|
+
export function billingRefusalWords(message) {
|
|
131
|
+
return String(message ?? "").replace(/^([A-Z][A-Z0-9_]*)=[\s\S]*? is not a billing mode/, "$1 is set to a word that is not a billing mode");
|
|
132
|
+
}
|
|
133
|
+
|
|
96
134
|
/**
|
|
97
135
|
* Every environment name this box's configuration says a clearance needs, with the reason each one is
|
|
98
136
|
* there and whether its absence blocks or narrows.
|
|
@@ -101,9 +139,9 @@ const val = (env, name) => String(env?.[name] ?? "").trim();
|
|
|
101
139
|
* environment in hand: the composer has the supervisor's, and the guard has the one it just wrote into
|
|
102
140
|
* the unit file. A function that read the ambient environment would answer about neither.
|
|
103
141
|
*
|
|
104
|
-
* PURE.
|
|
142
|
+
* PURE, apart from the engine resolver a caller passes in `tables.resolveEngine` (see the engine row).
|
|
105
143
|
*/
|
|
106
|
-
export function runRequirements(env = {}, { registers = [], engines = {}, defaultEngine = null } = {}) {
|
|
144
|
+
export function runRequirements(env = {}, { registers = [], engines = {}, defaultEngine = null, resolveEngine = null } = {}) {
|
|
107
145
|
const out = [];
|
|
108
146
|
const push = (name, blocking, why, at = ORDER) =>
|
|
109
147
|
out.push({ name, blocking, why, at, present: Boolean(val(env, name)) });
|
|
@@ -128,11 +166,135 @@ export function runRequirements(env = {}, { registers = [], engines = {}, defaul
|
|
|
128
166
|
|
|
129
167
|
// ── THE ENGINE, AND THE BINARY IT DRIVES ─────────────────────────────────────────────────────────
|
|
130
168
|
push(ENGINE_ENV, true, "which reasoning engine runs the stages");
|
|
131
|
-
const
|
|
132
|
-
|
|
169
|
+
const engineId = val(env, ENGINE_ENV) || defaultEngine || "";
|
|
170
|
+
const engine = (engines ?? {})[engineId];
|
|
171
|
+
if (engine?.env) {
|
|
133
172
|
push(engine.env, true, `the path to the ${engine.vendor} CLI this engine drives — a stage cannot dispatch without it`);
|
|
134
|
-
|
|
135
|
-
|
|
173
|
+
// FOUND IS WHAT COUNTS, NOT SET. A program on this environment's PATH, or the copy installed with
|
|
174
|
+
// Clearotron, needs no path written anywhere, and asking only whether the variable was set refused an
|
|
175
|
+
// install whose engine the run door would have started. The resolver arrives through the tables, like
|
|
176
|
+
// everything else this module knows about the install; a caller that passes no resolver gets the
|
|
177
|
+
// variable's own answer, which is all it can see. The module's one import is engine/auth.mjs, for the
|
|
178
|
+
// billing row below: a driver leaf with no imports of its own, pure, and the one authority on which
|
|
179
|
+
// billing words an engine takes, so importing it keeps this module pure and reaches nothing in `bin/`.
|
|
180
|
+
const row = out[out.length - 1];
|
|
181
|
+
if (!row.present && typeof resolveEngine === "function") {
|
|
182
|
+
try { row.present = Boolean(resolveEngine(engineId, { env })?.resolved); } catch { /* not found is not present */ }
|
|
183
|
+
}
|
|
184
|
+
}
|
|
185
|
+
if (engine?.authEnv) {
|
|
186
|
+
const ways = payWays(engineId, engine);
|
|
187
|
+
const said = ways.map((m) => PAY_WORDS[m]);
|
|
188
|
+
push(engine.authEnv, false, `how the engine is paid for — ${said.length > 1 ? `${said.slice(0, -1).join(", ")} or ${said.at(-1)}` : said[0]}; `
|
|
189
|
+
+ `unset means the subscription, and the adapter refuses before spending if the ${ways.includes("cloud") ? "key or cloud account" : "key"} it names is absent`);
|
|
190
|
+
const billingRow = out[out.length - 1];
|
|
191
|
+
const billingRows = out.length;
|
|
192
|
+
const word = billingMode(env);
|
|
193
|
+
const refused = "without it the engine refuses every search before spending";
|
|
194
|
+
// ── THE KEY, WHEN THE WORD IS `api-key` ──────────────────────────────────────────────────────────
|
|
195
|
+
//
|
|
196
|
+
// The same defect as the cloud below, one billing word over: the word travelled to the services' file
|
|
197
|
+
// and the key did not, so the run door refused every search after intake ("api-key but
|
|
198
|
+
// ANTHROPIC_API_KEY is not set"), while start and doctor reported nothing. Blocking at order time, like
|
|
199
|
+
// the switch: an operator's value, and the run door's own refusal without it.
|
|
200
|
+
if (word === "api-key" && engine.apiKeyEnv && ways.includes("api-key"))
|
|
201
|
+
push(engine.apiKeyEnv, true, `the key ${engine.authEnv}=api-key bills every turn to — ${refused}`);
|
|
202
|
+
// ── THE LONG-LIVED SIGN-IN, WHEN THE WORD IS THE SUBSCRIPTION ────────────────────────────────────
|
|
203
|
+
//
|
|
204
|
+
// Setup captures it on a machine that cannot complete a sign-in, which is the server a background
|
|
205
|
+
// install runs on, and the program reads it from its environment. Carried when set and never asked
|
|
206
|
+
// for: a machine signed in through the program itself needs none.
|
|
207
|
+
const tokenEnv = engine.headless?.tokenEnv;
|
|
208
|
+
if (word === "subscription" && tokenEnv && val(env, tokenEnv))
|
|
209
|
+
push(tokenEnv, false, `the long-lived sign-in the ${engine.vendor} CLI uses for the subscription on a machine it cannot sign in on; carried as set, and not asked for`);
|
|
210
|
+
// ── A CLOUD ACCOUNT, AND THE SETTINGS THAT REACH IT ──────────────────────────────────────────────
|
|
211
|
+
//
|
|
212
|
+
// The billing word travelled and the cloud did not. A machine set up to pay through a cloud account and
|
|
213
|
+
// started as background services wrote CLEAROTRON_AI_BILLING=cloud into the services' file and none of
|
|
214
|
+
// the cloud's own settings, so the run door refused every search: "none of CLAUDE_CODE_USE_VERTEX, …
|
|
215
|
+
// is set". Measured 2026-09-15 on a Microsoft Foundry configuration.
|
|
216
|
+
//
|
|
217
|
+
// ONLY WHEN THE WORD IS `cloud` AND THE ENGINE TAKES ONE — the resolver's answer through `payWays`, not
|
|
218
|
+
// a list of engines here. Under subscription or api-key nothing of a cloud is carried: a switch left on
|
|
219
|
+
// in a shell and written into the services' file sends Claude to that cloud, and the run door refuses
|
|
220
|
+
// a switch beside either word. Codex refuses a cloud account outright, so its rows do not change.
|
|
221
|
+
if (word === "cloud" && ways.includes("cloud")) {
|
|
222
|
+
const on = cloudsSwitchedOn(env);
|
|
223
|
+
const payWord = `${engine.authEnv}=cloud`;
|
|
224
|
+
// BLOCKING, AT ORDER TIME. Without the switch the run door refuses (engine/auth.mjs), so this is the
|
|
225
|
+
// same class as the engine and its program: an operator's value, whose absence refuses a run at
|
|
226
|
+
// intake and never a start.
|
|
227
|
+
for (const c of on)
|
|
228
|
+
push(CLOUD_SWITCH[c], true, `sends Claude to ${CLOUD_CREDENTIAL_CHECK[c].who}, the account ${payWord} pays through — ${refused}`);
|
|
229
|
+
if (!on.length && val(env, "ANTHROPIC_BASE_URL"))
|
|
230
|
+
push("ANTHROPIC_BASE_URL", true, `the gateway ${payWord} pays through — ${refused}`);
|
|
231
|
+
if (!on.length && !val(env, "ANTHROPIC_BASE_URL")) {
|
|
232
|
+
// NOTHING SAYS WHICH CLOUD, and a row has one name. Any one of four settings satisfies it. The row is
|
|
233
|
+
// NAMED by one real setting, Google's switch, the representative `payWays` above already uses for "a
|
|
234
|
+
// cloud account", because every reader of a row treats its name as a variable: start and doctor print
|
|
235
|
+
// it, the runner logs it, and a composite name read as four more items in doctor's list of what is
|
|
236
|
+
// missing. The REASON names all four, with whose each is. And the row carries all four as `anyOf`,
|
|
237
|
+
// which `runRequiredNames` hands out as names to read: doctor fills its view of the services by name,
|
|
238
|
+
// and reading only Google's switch reported an Azure machine's switch, held by its services, as
|
|
239
|
+
// missing.
|
|
240
|
+
//
|
|
241
|
+
// A SWITCH THAT IS SET AND NOT ON IS SAID, because it reads as on to a person and as off to the
|
|
242
|
+
// program: `CLAUDE_CODE_USE_FOUNDRY=0` on an Azure machine was answered with Google's switch
|
|
243
|
+
// and nothing else, and the reader was left to work out why the one they set did not count.
|
|
244
|
+
// And it is not an alternative to read or carry: `anyOf` is handed to the composer too, and the
|
|
245
|
+
// set-and-off line would travel through it.
|
|
246
|
+
const off = Object.values(CLOUD_SWITCH).filter((k) => val(env, k));
|
|
247
|
+
out.push({ name: CLOUD_SWITCH.vertex, anyOf: CLOUD_ROUTES.filter((k) => !off.includes(k)), blocking: true, at: ORDER, present: false,
|
|
248
|
+
why: `${payWord} pays through a cloud account and nothing names which one — set `
|
|
249
|
+
+ `${["vertex", "foundry", "bedrock"].map((c) => `${CLOUD_SWITCH[c]}=1 for ${CLOUD_CREDENTIAL_CHECK[c].who}`).join(", ")}, `
|
|
250
|
+
+ `or ANTHROPIC_BASE_URL for a gateway`
|
|
251
|
+
+ (off.length ? ` (${off.join(" and ")} ${off.length > 1 ? "are" : "is"} set, but not on: a switch is on at 1, true, yes or on)` : "")
|
|
252
|
+
+ `; ${refused}` });
|
|
253
|
+
}
|
|
254
|
+
// EVERY OTHER CLOUD SETTING THAT IS SET, CARRIED AND NEVER ASKED FOR. Which of them a cloud needs
|
|
255
|
+
// depends on how the machine signs in to it — an Azure key or the Azure sign-in, an AWS profile, an
|
|
256
|
+
// instance role or keys, gcloud or a key file — and the model pins are optional everywhere. The run
|
|
257
|
+
// door asks for none of them; the cloud refuses a missing one at the first turn. So a row is made
|
|
258
|
+
// only for a name that is set: present by construction, it can never be reported missing or refuse
|
|
259
|
+
// anything, and exists so the composer carries it. Names on CLOUD_SETTINGS only — never the rest of
|
|
260
|
+
// the environment, which would put every secret in a shell into the services' file.
|
|
261
|
+
//
|
|
262
|
+
// NEVER A SWITCH. One that is on is a row above. One that is set and not on switches nothing, and
|
|
263
|
+
// carried into the services' file it held that line there, where the add-only merge kept a later
|
|
264
|
+
// `=1` out of it for good.
|
|
265
|
+
const who = on.length === 1 ? CLOUD_CREDENTIAL_CHECK[on[0]].who : !on.length && val(env, "ANTHROPIC_BASE_URL") ? "the gateway" : "the cloud account";
|
|
266
|
+
const named = new Set(out.flatMap((r) => r.anyOf ?? [r.name]));
|
|
267
|
+
for (const k of CLOUD_SETTINGS)
|
|
268
|
+
if (!named.has(k) && !Object.values(CLOUD_SWITCH).includes(k) && val(env, k))
|
|
269
|
+
push(k, false, `one of the settings Claude reads to reach and pay ${who}; carried as set, and not asked for, because which ones a cloud needs depends on how this machine signs in to it`);
|
|
270
|
+
}
|
|
271
|
+
// ── AND ANY OTHER WAY THE RUN DOOR REFUSES HOW THIS IS PAID FOR ──────────────────────────────────
|
|
272
|
+
//
|
|
273
|
+
// The rows above name what is MISSING. The run door also refuses what is set wrongly: two clouds
|
|
274
|
+
// switched on, a switch left on beside `subscription` or `api-key`, a word that is not a billing mode,
|
|
275
|
+
// a cloud account on Codex. Each passed start's guard, the order wall and doctor, and the run was then
|
|
276
|
+
// refused after intake. Measured 2026-09-15: a services' file holding Azure's switch from one start
|
|
277
|
+
// and Google's from the next read clean everywhere and refused every search.
|
|
278
|
+
//
|
|
279
|
+
// ASKED OF THE RUN DOOR ITSELF, `resolveAuthMode`, over the environment being judged, and its refusal
|
|
280
|
+
// is the reason: one authority, so this check can be neither stricter nor laxer than the door. Its
|
|
281
|
+
// words carry names and the billing word, never a key. The row that turns blocking is the billing
|
|
282
|
+
// word's own, so no new name is handed to the composer: a switch beside `subscription` stays out of
|
|
283
|
+
// the services' file, as the cloud rows above intend. `present` on a row means SATISFIED, which is
|
|
284
|
+
// what every reader of it asks; for this row, set is not enough. When a row above already names what
|
|
285
|
+
// is missing, that row is the answer and this adds nothing, because the door's refusal is the same one.
|
|
286
|
+
//
|
|
287
|
+
// ONE OF THE DOOR'S REFUSALS QUOTES A VALUE: the word that is not a billing mode. A key pasted into the
|
|
288
|
+
// billing word by mistake is that value, and this reason is printed by start, by doctor and in the
|
|
289
|
+
// runner's log. So it is said by name, through billingRefusalWords below.
|
|
290
|
+
if (!out.slice(billingRows).some((r) => r.blocking && !r.present)) {
|
|
291
|
+
try { resolveAuthMode({ engineName: engineId, env }); } catch (e) {
|
|
292
|
+
if (e?.billingRefusal)
|
|
293
|
+
Object.assign(billingRow, { blocking: true, present: false, at: ORDER,
|
|
294
|
+
why: `every search is refused before spending, over how this machine is set to pay: ${billingRefusalWords(e.message)}` });
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
}
|
|
136
298
|
|
|
137
299
|
push(RESEARCH_ENV, false, "the three clearance searches carry the common-law grid and refuse at preflight without it; a Knockout search still runs and discloses the half it skipped");
|
|
138
300
|
|
|
@@ -147,7 +309,9 @@ export function runRequirements(env = {}, { registers = [], engines = {}, defaul
|
|
|
147
309
|
* a box whose operator configured them, which is the same shape of defect one size down.
|
|
148
310
|
*/
|
|
149
311
|
export function runRequiredNames(env = {}, tables = {}) {
|
|
150
|
-
|
|
312
|
+
// A row any one of several settings satisfies names them in `anyOf`, and each is a name to carry or read.
|
|
313
|
+
// Each name once: a name handed out twice is read twice and reported twice.
|
|
314
|
+
return [...new Set(runRequirements(env, tables).flatMap((r) => r.anyOf ?? [r.name]))];
|
|
151
315
|
}
|
|
152
316
|
|
|
153
317
|
/**
|
package/driver/runner.mjs
CHANGED
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
import "../shared/env-local.mjs"; // side effect: apply <repo>/.env when THIS file is the CLI entry (never on library import)
|
|
22
22
|
import "./engine/mcp/http-dispatcher.mjs"; // side effect: raise undici headersTimeout (code-side fetches)
|
|
23
23
|
import { readdirSync, renameSync, existsSync, mkdirSync, readFileSync, writeFileSync, appendFileSync, rmSync, statSync } from "node:fs";
|
|
24
|
-
import { join, dirname, basename } from "node:path";
|
|
24
|
+
import { join, dirname, basename } from "node:path"; import { studioDirFor } from "../shared/pre-rename-spellings.mjs";
|
|
25
25
|
import { driverDir, ensureDriverDir } from "../shared/driver-dir.mjs"; // — one definition of where `_driver/` is
|
|
26
26
|
// The queue's filename vocabulary, in ONE place — the rule, extended by to the prose-sidecar and
|
|
27
27
|
// claim-sidecar names, because a harness check retyped four of them from memory and false-alarmed on the
|
|
@@ -774,15 +774,15 @@ async function backstopFailureNotice({ res, job, agentId, base, codename, studio
|
|
|
774
774
|
// starts, so nothing is spent and nothing is promised.
|
|
775
775
|
|
|
776
776
|
let __runTables = null;
|
|
777
|
-
async function runTables() {
|
|
777
|
+
export async function runTables() { // exported for the requirement-check wiring test
|
|
778
778
|
// AT CALL TIME, never a static import. `driver/run-requirements.mjs`'s header states the reason and it
|
|
779
779
|
// is load-bearing: the register SELECTION table lives in `bin/onboard.mjs`, a CLI entry point, and a
|
|
780
780
|
// static import from `driver/` would point the driver at `bin/` — the cycle that makes `clearotron
|
|
781
781
|
// doctor` exit 13 after printing most of a report.
|
|
782
782
|
if (!__runTables) {
|
|
783
783
|
const { PROVIDERS } = await import("../bin/onboard.mjs");
|
|
784
|
-
const { ENGINE_BINARIES, DEFAULT_ENGINE_ID } = await import("./driver.config.mjs");
|
|
785
|
-
__runTables = { registers: PROVIDERS, engines: ENGINE_BINARIES, defaultEngine: DEFAULT_ENGINE_ID };
|
|
784
|
+
const { ENGINE_BINARIES, DEFAULT_ENGINE_ID, resolveEngineProgram } = await import("./driver.config.mjs");
|
|
785
|
+
__runTables = { registers: PROVIDERS, engines: ENGINE_BINARIES, defaultEngine: DEFAULT_ENGINE_ID, resolveEngine: resolveEngineProgram };
|
|
786
786
|
}
|
|
787
787
|
return __runTables;
|
|
788
788
|
}
|
|
@@ -1548,7 +1548,7 @@ export function agentStudioRoots() {
|
|
|
1548
1548
|
try {
|
|
1549
1549
|
for (const name of readdirSync(config.workspaceRoot)) {
|
|
1550
1550
|
if (config.agentIdFromWorkspaceName(name) == null) continue;
|
|
1551
|
-
const s = join(config.workspaceRoot, name
|
|
1551
|
+
const s = studioDirFor(join(config.workspaceRoot, name));
|
|
1552
1552
|
if (existsSync(s)) out.push(s);
|
|
1553
1553
|
}
|
|
1554
1554
|
} catch { /* workspaceRoot absent in some envs */ }
|
package/driver/scope-facts.mjs
CHANGED
|
@@ -26,6 +26,7 @@
|
|
|
26
26
|
// keys only on the neutral plan/band vocabulary, never a vendor name or vendor-shaped field.
|
|
27
27
|
|
|
28
28
|
import { classTokensFromScopeText } from "./coverage-ledger.mjs";
|
|
29
|
+
import { capabilitiesFor } from "./register-capabilities.mjs";
|
|
29
30
|
|
|
30
31
|
const clsStr = (c) => String(c ?? "").trim();
|
|
31
32
|
|
|
@@ -144,10 +145,11 @@ export function deriveScopeFacts({ instructedScope = null, plan = null, planExec
|
|
|
144
145
|
|
|
145
146
|
const searched_jurisdictions = Array.isArray(plan?.regions) ? plan.regions.map(String) : [];
|
|
146
147
|
const scope_basis = plan?.scope_basis === "worldwide" ? "worldwide" : null;
|
|
148
|
+
const register_service = registerServiceOf(plan);
|
|
147
149
|
|
|
148
150
|
const classes_line = instructedClasses.length ? instructedClasses.join(", ") : null;
|
|
149
151
|
const coverage_line = instructedClasses.length && entries.length
|
|
150
|
-
? buildCoverageLine(instructedClasses, per_class, { searched_jurisdictions, scope_basis, instructedScope })
|
|
152
|
+
? buildCoverageLine(instructedClasses, per_class, { searched_jurisdictions, scope_basis, instructedScope, register_service })
|
|
151
153
|
: null;
|
|
152
154
|
|
|
153
155
|
return {
|
|
@@ -184,7 +186,7 @@ export function deriveScopeFacts({ instructedScope = null, plan = null, planExec
|
|
|
184
186
|
// clause is word-for-word the same are stated ONCE, over the classes they are about. Nothing is pooled
|
|
185
187
|
// and no number moves: the grouping is on the rendered text, so two classes only share a line when the
|
|
186
188
|
// line they would each have printed is already the same string.
|
|
187
|
-
function buildCoverageLine(classes, per_class, { searched_jurisdictions = [], scope_basis = null, instructedScope = null } = {}) {
|
|
189
|
+
function buildCoverageLine(classes, per_class, { searched_jurisdictions = [], scope_basis = null, instructedScope = null, register_service = null } = {}) {
|
|
188
190
|
const sorted = [...classes].sort((a, b) => (Number(a) || 0) - (Number(b) || 0));
|
|
189
191
|
const byBody = new Map();
|
|
190
192
|
for (const c of sorted) {
|
|
@@ -197,7 +199,7 @@ function buildCoverageLine(classes, per_class, { searched_jurisdictions = [], sc
|
|
|
197
199
|
// bodies A, B, A renders "Classes 5 and 32: A; Class 9: B", not A twice.
|
|
198
200
|
const groups = [...byBody.entries()].map(([body, classes]) => ({ body, classes }));
|
|
199
201
|
const head = (cs) => (cs.length === 1 ? `Class ${cs[0]}` : `Classes ${joinAnd(cs.map(String))}`);
|
|
200
|
-
return `${groups.map((g) => `${head(g.classes)}: ${g.body}`).join("; ")}${jurisdictionTail({ searched_jurisdictions, scope_basis, instructedScope })}`;
|
|
202
|
+
return `${groups.map((g) => `${head(g.classes)}: ${g.body}`).join("; ")}${jurisdictionTail({ searched_jurisdictions, scope_basis, instructedScope, register_service })}`;
|
|
201
203
|
}
|
|
202
204
|
|
|
203
205
|
// One plain-language clause per class, routed on the COUNTS (state-agnostic), not on the state label.
|
|
@@ -263,11 +265,24 @@ const joinAnd = (parts) => (parts.length <= 1 ? parts.join("") : `${parts.slice(
|
|
|
263
265
|
// EU US CH WO doesn't make sense". A worldwide-scoped plan (scope_basis, or a worldwide token riding
|
|
264
266
|
// an instructed list) collapses the tail to the one word; only a genuinely named list is listed.
|
|
265
267
|
const WORLDWIDE_TOKEN_RE = /^(worldwide|world|ww|global)$/i;
|
|
266
|
-
|
|
268
|
+
|
|
269
|
+
// WORLDWIDE NAMES THE SERVICE THAT SEARCHED IT (owner, 2026-09-18). "Worldwide" is eleven registers on one
|
|
270
|
+
// installation and 186 on another, and a reader can only tell which by being told the service. The name is
|
|
271
|
+
// the provider's own label from its capabilities contract — the same name the order form shows — read off
|
|
272
|
+
// the provider the frozen plan records. A plan that records none, or one this build does not know, keeps
|
|
273
|
+
// the bare word: a guessed name would be worse than none. Never a list of offices, never a count.
|
|
274
|
+
function registerServiceOf(plan) {
|
|
275
|
+
if (!plan?.provider) return null;
|
|
276
|
+
try { return capabilitiesFor(plan.provider).label ?? null; } catch { return null; }
|
|
277
|
+
}
|
|
278
|
+
|
|
279
|
+
function jurisdictionTail({ searched_jurisdictions = [], scope_basis = null, instructedScope = null, register_service = null } = {}) {
|
|
267
280
|
const list = (searched_jurisdictions.length
|
|
268
281
|
? searched_jurisdictions
|
|
269
282
|
: (Array.isArray(instructedScope?.jurisdictions) ? instructedScope.jurisdictions : [])).map((j) => String(j).trim()).filter((j) => j);
|
|
270
|
-
if (scope_basis === "worldwide" || list.some((j) => WORLDWIDE_TOKEN_RE.test(j)))
|
|
283
|
+
if (scope_basis === "worldwide" || list.some((j) => WORLDWIDE_TOKEN_RE.test(j))) {
|
|
284
|
+
return ` · registers: worldwide${register_service ? ` (${register_service})` : ""}`;
|
|
285
|
+
}
|
|
271
286
|
if (!list.length) return "";
|
|
272
287
|
// 2–3-letter office codes display uppercase ("us" reads as a pronoun, "US" as a jurisdiction);
|
|
273
288
|
// longer names pass through untouched.
|
package/driver/scope-ledger.mjs
CHANGED
|
@@ -1,12 +1,12 @@
|
|
|
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
|
-
// scope-ledger.mjs — the machine mirror of the
|
|
3
|
+
// scope-ledger.mjs — the machine mirror of the clearance-variants `### Scope ledger` (the frame-omission
|
|
4
4
|
// design: the blind-pass framing fix, approved into this file — the code is its own record).
|
|
5
5
|
//
|
|
6
|
-
//
|
|
6
|
+
// clearance-variants emits a prose `### Scope ledger` table — one row per variant / field / source the
|
|
7
7
|
// run CONSIDERED and DROPPED (or applied), each carrying the observation that should REOPEN it. The
|
|
8
8
|
// driver CODE-DERIVES scope-ledger.json from that prose (renderScopeLedgerJson, called after
|
|
9
|
-
//
|
|
9
|
+
// clearance-variants validates) so the JSON is authored by the driver, never the model, and matches the
|
|
10
10
|
// prose BY CONSTRUCTION — exactly the coverage-ledger.mjs pattern. The blind frame-diff reads the
|
|
11
11
|
// dropped set + reopen triggers to decide which omissions to escalate.
|
|
12
12
|
//
|
|
@@ -115,7 +115,7 @@ export function parseScopeLedgerJson(raw) {
|
|
|
115
115
|
|
|
116
116
|
/**
|
|
117
117
|
* CODE-DERIVE the JSON scope ledger FROM the prose `### Scope ledger` table (the driver calls this after
|
|
118
|
-
*
|
|
118
|
+
* clearance-variants validates; never-killed at the call site). PURE. Throws `scope_ledger_unparseable`
|
|
119
119
|
* when the prose carries a Scope ledger heading but yields no classifiable row, so the caller's catch
|
|
120
120
|
* routes to the manifest-prose-fallback path (the run still delivers). Returns a JSON ARRAY string that
|
|
121
121
|
* round-trips through parseScopeLedgerJson.
|
|
@@ -136,7 +136,7 @@ export function renderScopeLedgerJson(md) {
|
|
|
136
136
|
* MARKDOWN TABLE back out of a prose manifest a model had typed, on fixed column positions. A machine
|
|
137
137
|
* artifact the downstream depends on, recovered from a table.
|
|
138
138
|
*
|
|
139
|
-
* `
|
|
139
|
+
* `record_clearance_variants` now receives those rows TYPED, so the driver can serialise them directly. Both
|
|
140
140
|
* paths call THIS function, which is what makes the recorded and the archived ledger byte-identical for
|
|
141
141
|
* the same rows by construction rather than by an assertion someone has to maintain — and it is why the
|
|
142
142
|
* prose parse can stay for archives (the anchor rule: a new way in, never a replacement) without becoming
|
package/driver/search-policy.mjs
CHANGED
|
@@ -15,10 +15,10 @@
|
|
|
15
15
|
// ── THE DEPTH LADDER IS GONE ─────────────────────────────────────────────────────────────────────────────────
|
|
16
16
|
//
|
|
17
17
|
// There used to be a second vocabulary here: a closed set of LEVELS (`knockout`, `knockout-register`,
|
|
18
|
-
// `
|
|
18
|
+
// `clearance`, `clearance-jx`) with a display face reading "Depth 1"…"Depth 5", ordered by effort. A client
|
|
19
19
|
// bought a level; the thing they were sold — a knockout, a worldwide preliminary, a single-country deep
|
|
20
20
|
// dive — was a different word that appeared on no wire. The two disagreed in the one place it mattered:
|
|
21
|
-
// `
|
|
21
|
+
// `clearance` named THREE products depending on where it pointed, and the composer's own footer had to
|
|
22
22
|
// invent labels ("Deep dive — United States", "Full clearance") for distinctions "the registry has no
|
|
23
23
|
// word for". The registry now has the word. The level menu, its numbering, its ordering and the
|
|
24
24
|
// `searchLevel` wire field are DELETED — not deprecated, not hidden behind the product name.
|
|
@@ -61,7 +61,7 @@ export const ORDERABLE_PRODUCTS = PRODUCT_IDS;
|
|
|
61
61
|
//
|
|
62
62
|
// This is the reading that squares "no old mechanism survives its replacement" with an archive: the
|
|
63
63
|
// delete rule governs the ORDERABLE path, and naming a run that already happened is not that path.
|
|
64
|
-
export const RETIRED_PRODUCTS = ["knockout", "knockout-register", "
|
|
64
|
+
export const RETIRED_PRODUCTS = ["knockout", "knockout-register", "clearance-register-only", "clearance", "clearance-jx"];
|
|
65
65
|
|
|
66
66
|
/** Saved searches dropped by the last `loadRecipes` because their base is a retired product. Rewritten on
|
|
67
67
|
* every load, so it describes the CURRENT store and never accumulates. Read it to tell a reader why a
|
|
@@ -304,9 +304,9 @@ export const RETIRED_POLICIES = {
|
|
|
304
304
|
// Retired 2026-08-06: the clearance shape with its unregistered-use half removed. That basis
|
|
305
305
|
// still composes into riskStatement, and the workbook's own gate still inverts on it, because an
|
|
306
306
|
// archived run of it must not re-render claiming a sweep that never ran.
|
|
307
|
-
"
|
|
308
|
-
"
|
|
309
|
-
"
|
|
307
|
+
"clearance-register-only": { product: "clearance-register-only", stageLabel: "Depth 3", pipeline: "clearance", report: { template: "clearance", identity: "Preliminary clearance — register only" }, components: { registerProbe: false, jxLanes: false, commonLawGrid: false } },
|
|
308
|
+
"clearance": { product: "clearance", stageLabel: "Depth 4", pipeline: "clearance", report: { template: "clearance", identity: "Preliminary clearance" }, components: { registerProbe: false, jxLanes: false, commonLawGrid: true } },
|
|
309
|
+
"clearance-jx": { product: "clearance-jx", stageLabel: "Depth 5", pipeline: "clearance", report: { template: "clearance", identity: "Preliminary clearance with jurisdiction deep-dive" }, components: { registerProbe: false, jxLanes: true, commonLawGrid: true } },
|
|
310
310
|
};
|
|
311
311
|
|
|
312
312
|
/** The report identity to print on a run's document: `{ template, identity, stageLabel, banner }`.
|
|
@@ -464,13 +464,23 @@ export function isRegisterOnly(policy) {
|
|
|
464
464
|
* search asked for. Asking it "does this exist" is asking the wrong question — the orderability test is
|
|
465
465
|
* always `ORDERABLE_PRODUCTS.includes(...)`, and a typo must fail closed against that positive list. */
|
|
466
466
|
export function policyFor(product) {
|
|
467
|
-
const k =
|
|
467
|
+
const k = productKeyAsRenamed(product);
|
|
468
468
|
return PRODUCT_POLICIES[k] ?? RETIRED_POLICIES[k] ?? null;
|
|
469
469
|
}
|
|
470
|
+
// THREE RETIRED KEYS WERE RENAMED WITH THE IDENTIFIER, and records written before it still carry the old
|
|
471
|
+
// spelling: an archived run's product, a saved search's base in a client's store. Read the old key as the
|
|
472
|
+
// new wherever a key is looked up, or an archived report loses its name and a store's saved searches fail
|
|
473
|
+
// to load over a base this build still knows as retired.
|
|
474
|
+
const PRE_RENAME_PRODUCT_KEYS = Object.freeze({ "prelim": "clearance", "prelim-register-only": "clearance-register-only", "prelim-jx": "clearance-jx" });
|
|
475
|
+
/** A product key, lower-cased, with a pre-rename spelling read as its current one. PURE. */
|
|
476
|
+
export function productKeyAsRenamed(product) {
|
|
477
|
+
const k = String(product ?? "").trim().toLowerCase();
|
|
478
|
+
return PRE_RENAME_PRODUCT_KEYS[k] ?? k;
|
|
479
|
+
}
|
|
470
480
|
|
|
471
481
|
// What THIS build can actually execute. A resolution onto machinery a build does not carry must CLARIFY
|
|
472
482
|
// at admission (never silently run the wrong-priced product — a knockout request running as a $40
|
|
473
|
-
//
|
|
483
|
+
// clearance, or a 1.5 request running as a plain Stage 1, is the exact silent-substitution this file exists
|
|
474
484
|
// to forbid). These flags are flipped in CODE as each lane lands, never from the environment: there are
|
|
475
485
|
// no runtime env kill switches — see the note below.
|
|
476
486
|
// EXPORTED because availability is now asked about in two places, not one. The runner asks "may this
|
|
@@ -498,7 +508,7 @@ export const BUILT = { knockout: true, jxLanes: true, registerProbe: true };
|
|
|
498
508
|
// They were not merely dead, they were actively harmful, and in exactly the way the old comment here
|
|
499
509
|
// predicted: a caller outside the engine's environment reads every switch as unset, and unset was
|
|
500
510
|
// indistinguishable from off. The portal was given a snapshot to work around it. The ops-MCP was not,
|
|
501
|
-
// so `describe_options` and `plan_run` told clients that knockout, knockout-register and
|
|
511
|
+
// so `describe_options` and `plan_run` told clients that knockout, knockout-register and clearance-jx were
|
|
502
512
|
// "Not switched on for this account yet" while the engine would have run all three (2026-07-27). That is
|
|
503
513
|
// the second time this service has lied for want of an environment variable — the first was the profiles
|
|
504
514
|
// dir answering with a demo roster. Deleting the switch is the fix that cannot recur; plumbing a
|
|
@@ -830,7 +840,7 @@ export const CAPABILITY_SKIPPED_CAUSE = {
|
|
|
830
840
|
* now honoured wherever it resolves, and built machinery is never "off".
|
|
831
841
|
*
|
|
832
842
|
* The RESOLVED policy is measured, never `policyFor(resolved.level)`: a recipe can turn jxLanes on over a
|
|
833
|
-
* base of `
|
|
843
|
+
* base of `clearance`, and the base registry entry would answer "available" for a resolution that is not.
|
|
834
844
|
* Delegating the built arms to productAvailability is what makes the null-equivalence with
|
|
835
845
|
* gateResolvedPolicy structural rather than a coincidence two edits can break (search-policy.test.mjs
|
|
836
846
|
* pins it over the whole matrix).
|
|
@@ -1115,7 +1125,7 @@ export function loadRecipes({ dir = process.env.CLEAROTRON_RECIPES_DIR || null,
|
|
|
1115
1125
|
// NOT SILENT, because unlike a retired extra this one is observable: a saved search disappears. The
|
|
1116
1126
|
// skip is recorded so a caller can say why rather than leaving a lawyer's saved search gone with no
|
|
1117
1127
|
// reason. Everything else still throws — a config error is still load-blocking.
|
|
1118
|
-
if (RETIRED_PRODUCTS.includes(
|
|
1128
|
+
if (RETIRED_PRODUCTS.includes(productKeyAsRenamed(r?.base))) {
|
|
1119
1129
|
retiredBaseSkips.push({ customer, slug, base: String(r.base), why: "base names a retired search — the saved search cannot be ordered" });
|
|
1120
1130
|
continue;
|
|
1121
1131
|
}
|
|
@@ -1216,7 +1226,7 @@ export function checkMarkBudget(job, policy) {
|
|
|
1216
1226
|
// THE SCOPE ITSELF.
|
|
1217
1227
|
//
|
|
1218
1228
|
// THAT LAST RUNG IS THE OFFERING, AND IT IS NOT A CONSTANT. The Generic default used to be the literal
|
|
1219
|
-
// level `
|
|
1229
|
+
// level `clearance`, which named three different products depending on where it pointed. A clearance that
|
|
1220
1230
|
// names no product IS whichever product its territories make it — `productFor(pipeline, scope)`, the one
|
|
1221
1231
|
// function that answers that question anywhere — so the default is derived, not picked. The caller hands
|
|
1222
1232
|
// in the RESOLVED scope (`territories`) because resolving it needs the profile and the project overlay,
|
package/driver/skills/README.md
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
**Every file here is prompt payload served to the model at runtime.** A stage is one engine turn told to read named
|
|
4
4
|
files and follow them exactly, so an edit for brevity or tone changes what a clearance concludes —
|
|
5
|
-
`
|
|
5
|
+
`clearance-search/synthesis-rules.md` is 1,025 lines of program. Never touch this tree in a documentation or tidy-up
|
|
6
6
|
task, and never let a mass edit reach it — the [ADR-0005](../../docs/decisions/0005-comments-carry-reasoning.md)
|
|
7
7
|
comment sweep is required to exclude this tree, and that script does not exist yet, so nothing enforces it but you.
|
|
8
8
|
|
|
@@ -20,21 +20,21 @@ One directory per skill. `SKILL.md` is the entry point — YAML frontmatter (`na
|
|
|
20
20
|
procedure. Every other file beside it is a reference the SKILL.md or the stage message names explicitly, with six
|
|
21
21
|
exceptions: the four `risk-framework*.manifest.json` sidecars, whose paths the code DERIVES from the deck
|
|
22
22
|
(`manifestPathFor()`, `../framework.mjs`) so validators and the renderer read band vocabulary without parsing prose;
|
|
23
|
-
`
|
|
23
|
+
`clearance-search/risk-framework-triage.md`, the knockout lane's default ladder, named in code only
|
|
24
24
|
(`../pipeline-knockout.mjs`) because the knockout stage message hands the seat the frozen `_driver/framework.json`
|
|
25
|
-
instead; and `
|
|
25
|
+
instead; and `clearance-search/templates/search-request-form.html`, named only in `../publish/index.mjs`.
|
|
26
26
|
|
|
27
27
|
| Skill | What the stage does |
|
|
28
28
|
|---|---|
|
|
29
29
|
| `matter-frame` | Phase 0. Writes the matter's commercial context — sector, customer base, channels of trade, jurisdictions that materially matter, off-field sectors, watchlist seeds — before any search runs. `watchlist-reference.md` is enrichment, not authority. |
|
|
30
|
-
| `
|
|
30
|
+
| `clearance-variants` | Classifies the mark into one of six archetypes, derives a risk theory from that, emits the variant manifest both execution skills read. Non-Latin scripts: `transliteration-scripts.md`. |
|
|
31
31
|
| `blind-frame`, `frame-diff` | Re-derives the threat model from the raw instruction alone, deliberately starved of the matter frame, then diffs that model against what the run actually scoped and emits reopen directives the driver acts on. Something has to test the frame instead of reasoning inside it. |
|
|
32
|
-
| `
|
|
33
|
-
| `
|
|
32
|
+
| `clearance-common-law` | The marketplace / web / social sweep, as structured Perplexity research over the platform list the stage dictates. Prompt templates: `perplexity-prompts.md`. |
|
|
33
|
+
| `clearance-register` | Register execution in the two modes a spawn selects: `unit.md` (the funnel — enumerate one axis to completion, decide nothing) and `digest.md` (judgment over the merged band). Plus `register-recipes.md`, `status-rules.md`, `stealth-filer-indicators.md`, `providers/`. |
|
|
34
34
|
| `placement-inquiry` | Applies commercial relevance per candidate — headline-candidate / sheet-2 / watchlist-annex / out-of-scope-filtered — before any tiering runs. |
|
|
35
35
|
| `case-law-citation` | Grounds risk-relevant findings in precedent fetched in-session, never from memory. One thin adapter per source in `sources/`; `evals.md` defines what working means. |
|
|
36
36
|
| `narrative-refutation` | Reads the finished narrative against the underlying findings files and returns CLEAR / CONDITIONAL / BLOCKING with itemised flags. Delivery is gated on the verdict. |
|
|
37
|
-
| `
|
|
37
|
+
| `clearance-search` | The doctrine the synthesis and delivery stages are held to: `synthesis-rules.md`, the `risk-framework*.md` ladders with their `.manifest.json` band vocabularies, `delivery-contract.md`, `report-prose.md`, `worked-examples.md`, `phase2-execution.md`, `field-doctrine-pharma.md`. |
|
|
38
38
|
| `knockout-frame`, `knockout-assess` | Stages A and C of the knockout doctrine — a broad kill/no-kill triage screen over several candidate names at once (Stage B is a code-side research sweep). Not a clearance. |
|
|
39
39
|
|
|
40
40
|
## What reads it
|
|
@@ -53,25 +53,25 @@ instead; and `prelim-search/templates/search-request-form.html`, named only in `
|
|
|
53
53
|
|
|
54
54
|
## The provider boundary
|
|
55
55
|
|
|
56
|
-
Operator syntax and vendor tool tokens like `corsearch_*` live only in `
|
|
56
|
+
Operator syntax and vendor tool tokens like `corsearch_*` live only in `clearance-register/providers/`; the spine names
|
|
57
57
|
the `register_*` tools, whichever vendor `CLEAROTRON_DATABASE` selects.
|
|
58
58
|
`../test/provider-neutral-prose.test.mjs` holds three of that boundary's lines: no vendor-prefixed tool token
|
|
59
59
|
outside `providers/` and the `engine/mcp/<provider>-server.mjs` glue; no provider record host in a provider-agnostic
|
|
60
60
|
skill file (banned hosts are read FROM the provider docs, so a sixth provider is covered the day its doc lands);
|
|
61
61
|
every register server registering only `register_*`, under the MCP name `register`. Field paths are the open edge —
|
|
62
62
|
the matcher requires `<vendor>_` and cannot see camelCase, so no test fails on one, and
|
|
63
|
-
`
|
|
63
|
+
`clearance-register/status-rules.md` still instructs off Corsearch's own field names (`corsearchStatusCode`,
|
|
64
64
|
`onomaticsJurisdictionsStatuses`, the `owners[0].*` owner chain), plus one line of `register-recipes.md` off
|
|
65
|
-
`onomaticsOppositions[]`. `
|
|
65
|
+
`onomaticsOppositions[]`. `clearance-register/SKILL.md`, `unit.md`, `digest.md` and everything under `clearance-search/`
|
|
66
66
|
are clean. The authoring rules and the empirical-verification checklist a new provider doc must pass sit in
|
|
67
|
-
`
|
|
67
|
+
`clearance-register/providers/README.md`.
|
|
68
68
|
|
|
69
69
|
## Where to start
|
|
70
70
|
|
|
71
|
-
`
|
|
72
|
-
Then `
|
|
73
|
-
(`unit.md`, `digest.md`) and per-provider files (`providers/`). The other, `
|
|
71
|
+
`clearance-search/SKILL.md` — the orchestrator's own skill, and the one file describing the whole workflow end to end.
|
|
72
|
+
Then `clearance-register/SKILL.md`, the more elaborated of the two execution skills: a spine plus per-mode files
|
|
73
|
+
(`unit.md`, `digest.md`) and per-provider files (`providers/`). The other, `clearance-common-law`, is one spine plus
|
|
74
74
|
`perplexity-prompts.md`, its two grid modes (deterministic `grid_spec_path` dispatch vs. the legacy authored
|
|
75
75
|
program) being sections inside it — the per-mode split and `providers/` are register-only, not a shape every
|
|
76
|
-
execution skill shares. `
|
|
76
|
+
execution skill shares. `clearance-search/phase2-execution.md` is methodology, not sequencing — the pipeline is
|
|
77
77
|
sequenced in code, in `../stages.mjs` and `../pipeline.mjs`.
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: blind-frame
|
|
3
|
-
description: The frame-STARVED independent re-derivation for the v3 preliminary trademark search workflow. **Invoked exclusively by the `
|
|
3
|
+
description: The frame-STARVED independent re-derivation for the v3 preliminary trademark search workflow. **Invoked exclusively by the `clearance-search` orchestrator/driver**, in parallel with the gather sweeps — do not call directly. Reads ONLY the raw instruction (mark, goods, classes, territories, manner of use) and NOT the matter frame, then re-derives the threat model cold across four layers — element + neighbours both directions, field by goods-overlap, sources by real channel, ranking by goods-overlap — and emits a structured model the frame-diff stage diffs against what the run actually scoped. Its job is to test the frame, not to reason inside it.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
## Purpose
|
|
7
7
|
|
|
8
|
-
Every other stage in a clearotron run reasons *inside* a frame that was set early (at `matter-frame` / `
|
|
8
|
+
Every other stage in a clearotron run reasons *inside* a frame that was set early (at `matter-frame` / `clearance-variants`): the variants to chase, the field that counts as on-field, the sources worth searching. Verification then runs *on* that frame — nothing tests the frame itself. When the frame is mis-scoped, the whole run inherits the miss and the skeptic, reasoning from the same frame, certifies it.
|
|
9
9
|
|
|
10
10
|
You are the antidote. You are **deliberately starved of the frame**: you receive only the raw instruction, exactly as the requester wrote it, and you re-derive the threat model **cold**. Because you never see the run's conclusions, you cannot anchor to them. Your output is later **diffed** against what the run actually scoped — the gaps in that diff are the omissions the frame missed.
|
|
11
11
|
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
---
|
|
2
2
|
name: case-law-citation
|
|
3
|
-
description: Grounds risk-relevant trademark findings in cited case law and decisions instead of asserting them. Invoked by the
|
|
3
|
+
description: Grounds risk-relevant trademark findings in cited case law and decisions instead of asserting them. Invoked by the clearance-search orchestrator at Step 4.5 to profile an aggressive enforcer or test a likelihood-of-confusion question against precedent, and usable inline in chat for ad-hoc conflict/enforcement questions. Queries live legal sources — CourtListener (US federal incl. CAFC), EUR-Lex (EU CJEU + General Court judgments, verbatim), and Legal Data Hunter (108-country statutes and case law) — and returns, per finding, on-point authorities (case or decision, forum, date, one-line holding, stable identifier) with relevance notes, or an explicit no-precedent result. Cites only from documents fetched in the session, never from memory. Use when grounding watchlist or aggressive-enforcer hits, or when a trademark conflict, confusability, or enforcement-history question needs precedent.
|
|
4
4
|
---
|
|
5
5
|
|
|
6
6
|
# Case-law citation
|
|
@@ -40,17 +40,17 @@ Companion files (one level deep — read the one you need):
|
|
|
40
40
|
|
|
41
41
|
Still-future adapter (drops in here with no change to this file): `sources/euipo.md` (EUIPO
|
|
42
42
|
Boards-of-Appeal decisions — no free API today). EUIPO *register* lookups are a different layer
|
|
43
|
-
(`
|
|
43
|
+
(`clearance-register`), not this skill.
|
|
44
44
|
|
|
45
45
|
## Trigger
|
|
46
46
|
|
|
47
|
-
Called by `
|
|
47
|
+
Called by `clearance-search` after synthesis flags the risk-relevant findings (Step 4.5). Also triggers in
|
|
48
48
|
chat when the user raises a trademark conflict, likelihood-of-confusion, or enforcement-history question.
|
|
49
49
|
Gated and optional: if this skill is absent, clearotron skips Step 4.5 and delivers normally.
|
|
50
50
|
|
|
51
51
|
## Model
|
|
52
52
|
|
|
53
|
-
**Sonnet, eval-gated** when spawned from the `
|
|
53
|
+
**Sonnet, eval-gated** when spawned from the `clearance-search` Step 4.5 seam. The work is extraction
|
|
54
54
|
from documents fetched **this session** — the fetch-before-cite discipline (cite only what you
|
|
55
55
|
fetched; read the holding from the fetched text; refuse to invent when sources are empty), not model
|
|
56
56
|
recall, is what keeps citations honest. That makes it a Sonnet-tier task **provided** Sonnet reliably
|
|
@@ -65,7 +65,7 @@ successfully fetch and verify.
|
|
|
65
65
|
would close this gap; none is wired today.)
|
|
66
66
|
- **Not** the EUIPO administrative layer: **EUIPO Boards-of-Appeal decisions** have no free API and are
|
|
67
67
|
not searched — report that as a coverage gap. EUIPO *register* lookups are a different layer
|
|
68
|
-
(`
|
|
68
|
+
(`clearance-register`), not this skill.
|
|
69
69
|
|
|
70
70
|
## Citing convention
|
|
71
71
|
|