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
package/driver/register-plan.mjs
CHANGED
|
@@ -573,7 +573,194 @@ export function mintSupplementalQid({ prefix, term, used }) {
|
|
|
573
573
|
* driver/register-capabilities.mjs). Omitted ⇒ the pre-phase-3 corsearch-shaped
|
|
574
574
|
* behaviour, byte-identical (no entry gains a key, no jurisdiction is translated).
|
|
575
575
|
*/
|
|
576
|
-
|
|
576
|
+
/**
|
|
577
|
+
* The receipt that ARMS the house-element exclusion, and the only thing that may.
|
|
578
|
+
*
|
|
579
|
+
* A SEPARATE FILE FROM THE FRAME'S PROPOSAL, deliberately, and for the reason the digest's accounting
|
|
580
|
+
* stamp is separate from its facts: the proposal is what a model said, the receipt is what the register
|
|
581
|
+
* answered, and a reader who cannot tell those apart cannot tell a judgement from evidence. Absent means
|
|
582
|
+
* the question was never asked, which is the same as unverified and excludes nothing.
|
|
583
|
+
*/
|
|
584
|
+
export const HOUSE_ELEMENT_RECEIPT = "house-element.json";
|
|
585
|
+
|
|
586
|
+
/**
|
|
587
|
+
* Verify that the client actually owns the proposed house element, on the register, by owner.
|
|
588
|
+
*
|
|
589
|
+
* IT SITS BESIDE THE TRANSFORM IT GATES, for the reason `accountingArmed` sits beside the refusal it
|
|
590
|
+
* arms: the gate and the thing gated go stale together or not at all, and a reader meeting one finds the
|
|
591
|
+
* other. Nothing else may arm this exclusion.
|
|
592
|
+
*
|
|
593
|
+
* FAIL-CLOSED ON EVERY PATH, and that is the whole design. The frame PROPOSED this element from its
|
|
594
|
+
* reading of the matter; acting on the proposal alone would drop an element from a client's search on a
|
|
595
|
+
* model's assertion, and an element nobody swept is a clean report over unswept ground — the one defect
|
|
596
|
+
* that reaches a client as a confident wrong answer rather than as a visible failure. So every way of
|
|
597
|
+
* not knowing lands in the same place: not verified, no exclusion, the element searched in full, and a
|
|
598
|
+
* reason on the receipt saying which way it was. An outage, an unknown client name, a lookup that threw,
|
|
599
|
+
* a dead registration, a registration in some other class — none of them excludes anything.
|
|
600
|
+
*
|
|
601
|
+
* `lookup` is injected, exactly as `runOwnerChecks` takes its `exec` and `countRegisterHits` its
|
|
602
|
+
* `counter`: the fixture path that makes this product testable at no cost covers this call too, and a
|
|
603
|
+
* test never reaches a provider. NEVER THROWS and never rejects.
|
|
604
|
+
*
|
|
605
|
+
* @param owners the client's own names — the profile's trading names and the matter's customer. Empty
|
|
606
|
+
* is a real answer and it means NOT VERIFIED: with no name to match an owner against,
|
|
607
|
+
* "the client owns it" cannot be established by anything this function can see.
|
|
608
|
+
* @returns the receipt, always. `verified: true` is the only value that may arm an exclusion.
|
|
609
|
+
*/
|
|
610
|
+
export async function verifyHouseElementOwnership({
|
|
611
|
+
element, classes = [], owners = [], lookup, now = () => new Date().toISOString(),
|
|
612
|
+
}) {
|
|
613
|
+
const el = String(element ?? "").trim();
|
|
614
|
+
const wanted = (Array.isArray(classes) ? classes : []).map(String).map((c) => c.trim()).filter(Boolean);
|
|
615
|
+
const names = (Array.isArray(owners) ? owners : []).map((o) => String(o ?? "").trim()).filter(Boolean);
|
|
616
|
+
const receipt = (verified, reason, records = []) =>
|
|
617
|
+
({ verified, element: el, owners_checked: names, classes: wanted, records, reason, ts: now() });
|
|
618
|
+
|
|
619
|
+
if (!el) return receipt(false, "house_element_absent: nothing was proposed");
|
|
620
|
+
if (!names.length)
|
|
621
|
+
return receipt(false, "client_owner_unknown: this run holds no trading name for the client, so an owner on the register cannot be matched to it");
|
|
622
|
+
if (!wanted.length)
|
|
623
|
+
return receipt(false, "instructed_classes_absent: ownership is only decisive in the classes the matter is instructed in");
|
|
624
|
+
if (typeof lookup !== "function")
|
|
625
|
+
return receipt(false, "lookup_unavailable: no register lookup was wired, so ownership was never asked");
|
|
626
|
+
|
|
627
|
+
let rows = [];
|
|
628
|
+
try {
|
|
629
|
+
const r = await lookup({ element: el, owners: names, classes: wanted });
|
|
630
|
+
if (!r?.ok) return receipt(false, `lookup_did_not_answer: ${String(r?.reason ?? "no reason given").slice(0, 200)}`);
|
|
631
|
+
rows = Array.isArray(r.records) ? r.records : [];
|
|
632
|
+
} catch (e) {
|
|
633
|
+
return receipt(false, `lookup_threw: ${String(e?.message ?? e).slice(0, 200)}`);
|
|
634
|
+
}
|
|
635
|
+
|
|
636
|
+
// A MATCH IS ALL THREE AT ONCE — the client's own name, alive, in an instructed class. Checking them
|
|
637
|
+
// separately would let a dead registration in class 9 and a live one in class 25 held by someone else
|
|
638
|
+
// combine into an ownership nobody has.
|
|
639
|
+
const norm = (v) => String(v ?? "").toLowerCase().replace(/[^a-z0-9]+/g, " ").trim();
|
|
640
|
+
const ours = names.map(norm).filter(Boolean);
|
|
641
|
+
const live = (st) => { const t = norm(st); return Boolean(t) && !/(dead|expired|cancell?ed|withdrawn|refused|lapsed|abandoned)/.test(t); };
|
|
642
|
+
const matched = rows.filter((r) => {
|
|
643
|
+
const owner = norm(r?.owner_name);
|
|
644
|
+
if (!owner || !ours.some((o) => owner === o || owner.includes(o) || o.includes(owner))) return false;
|
|
645
|
+
if (!live(r?.status)) return false;
|
|
646
|
+
const rc = (Array.isArray(r?.classes) ? r.classes : []).map(String).map((c) => c.trim());
|
|
647
|
+
return rc.some((c) => wanted.includes(c));
|
|
648
|
+
});
|
|
649
|
+
|
|
650
|
+
if (!matched.length)
|
|
651
|
+
return receipt(false, `no_live_owned_registration: ${rows.length} record(s) came back and none is a live registration held by this client in an instructed class`, []);
|
|
652
|
+
// The records are the EVIDENCE the report states the exclusion on, so they ride the receipt.
|
|
653
|
+
return receipt(true, `verified: ${matched.length} live registration(s) held by this client in an instructed class`,
|
|
654
|
+
matched.map((r) => ({ record_id: String(r?.record_id ?? r?.uri ?? ""), owner_name: String(r?.owner_name ?? ""),
|
|
655
|
+
status: String(r?.status ?? ""), classes: (Array.isArray(r?.classes) ? r.classes : []).map(String) })));
|
|
656
|
+
}
|
|
657
|
+
|
|
658
|
+
/**
|
|
659
|
+
* The manifest with the client's own house element taken out of the conflict analysis. PURE.
|
|
660
|
+
*
|
|
661
|
+
* THE DEFECT (production run, 2026-09-16). The mark was the client's own famous house mark followed by
|
|
662
|
+
* a tagline. `dominant_element` was the house element, so the machine-built one-letter mutation forms
|
|
663
|
+
* were built from it — and because those mutations are ordinary short words they pull every mark
|
|
664
|
+
* containing them across four registers. Two such queries put 1,154 records into a 2,146-record band,
|
|
665
|
+
* 54% of it, reachable from nothing else. The reviewing lawyer's method for the same matter was three
|
|
666
|
+
* searches: the whole phrase, the shorter phrase, the last word alone.
|
|
667
|
+
*
|
|
668
|
+
* WHAT IS NOT DONE HERE, AND IT IS THE POINT. This does not decide that the client owns the element —
|
|
669
|
+
* it is called only where the driver has already verified ownership on the register by owner and written
|
|
670
|
+
* its receipt. A `house` argument that arrived from a model's assertion would be an element nobody
|
|
671
|
+
* searched because a model said it was safe.
|
|
672
|
+
*
|
|
673
|
+
* THE WHOLE PHRASE SURVIVES, and that is the lawyer's method rather than a softening of it. `mark` is
|
|
674
|
+
* untouched, so the exact search for the full phrase still runs. What stops is treating the house
|
|
675
|
+
* element as an AXIS — its mutations, its forms, its own dominant-element sweep — because that is where
|
|
676
|
+
* the band came from, not from the one exact query.
|
|
677
|
+
*
|
|
678
|
+
* Returns `{ manifest, confirmation, refused }`. `refused` non-null means the exclusion was NOT applied
|
|
679
|
+
* and the returned manifest is the input: the caller plans as it would have with no receipt at all.
|
|
680
|
+
*/
|
|
681
|
+
export function excludeHouseElement(manifest, house) {
|
|
682
|
+
const element = String(house?.element ?? "").trim();
|
|
683
|
+
const remainder = String(house?.remainder ?? "").trim();
|
|
684
|
+
const keep = (why) => ({ manifest, confirmation: null, refused: why });
|
|
685
|
+
if (!element || !remainder) return keep("house_element_incomplete");
|
|
686
|
+
|
|
687
|
+
const words = remainder.split(/\s+/).filter(Boolean);
|
|
688
|
+
// ── THE FLOOR IS THE JOIN TO THIS MANIFEST'S OWN MARK ───────────────────────────────────────────
|
|
689
|
+
//
|
|
690
|
+
// The direction that reaches a client is naming too MUCH as the house element: ownership verifies
|
|
691
|
+
// perfectly, nothing distinctive is left, and no count downstream catches it because "queries on the
|
|
692
|
+
// house element: 0" is satisfied by a plan holding no queries at all. The frame refuses the shapes it
|
|
693
|
+
// can see — an empty remainder, a remainder equal to the element, an element containing it.
|
|
694
|
+
//
|
|
695
|
+
// WHAT THE FRAME CANNOT SEE IS THIS MARK. The proposal is made against the matter, the exclusion is
|
|
696
|
+
// applied against a compiled manifest, and nothing until here has joined the two. A remainder that is
|
|
697
|
+
// not part of the mark being planned means the receipt and the manifest are describing different
|
|
698
|
+
// things — a re-frame, a second ratified form, a resumed run whose manifest moved — and cutting the
|
|
699
|
+
// dominant element down to a word that is not in the mark would leave the plan searching something
|
|
700
|
+
// the client never applied for. So the exclusion applies only where both halves are in the mark.
|
|
701
|
+
const lcMark = String(manifest?.mark ?? "").toLowerCase();
|
|
702
|
+
if (!lcMark) return keep("house_element_no_mark_to_join");
|
|
703
|
+
if (!lcMark.includes(remainder.toLowerCase())) return keep("house_element_remainder_not_in_mark");
|
|
704
|
+
if (!lcMark.includes(element.toLowerCase())) return keep("house_element_not_in_mark");
|
|
705
|
+
|
|
706
|
+
const lcEl = element.toLowerCase();
|
|
707
|
+
const lcWords = words.map((w) => w.toLowerCase());
|
|
708
|
+
// A word of the remainder, by word boundary rather than by containment: `includes` would count the
|
|
709
|
+
// remainder word "on" inside an unrelated mutation and keep a query this exists to drop.
|
|
710
|
+
const namesRemainder = (v) => {
|
|
711
|
+
const t = String(v ?? "").toLowerCase();
|
|
712
|
+
return lcWords.some((w) => new RegExp(`(^|[^\\p{L}\\p{N}])${w.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}([^\\p{L}\\p{N}]|$)`, "u").test(t));
|
|
713
|
+
};
|
|
714
|
+
|
|
715
|
+
// THE DOMINANT WORD OF THE REMAINDER: longest, ties broken by the LAST of them. The lawyer's own
|
|
716
|
+
// phrasing was "the last word alone", and on the matter that produced this the two agree; longest is
|
|
717
|
+
// the better rule where they do not, because a trailing article is last and carries nothing.
|
|
718
|
+
let dominant = words[0];
|
|
719
|
+
for (const w of words) if (w.length >= dominant.length) dominant = w;
|
|
720
|
+
|
|
721
|
+
const variants = (Array.isArray(manifest?.variants) ? manifest.variants : []).filter((v) => namesRemainder(v?.value));
|
|
722
|
+
const next = {
|
|
723
|
+
...manifest,
|
|
724
|
+
// UNTOUCHED: the exact search for the full phrase is the first of the lawyer's three.
|
|
725
|
+
mark: manifest?.mark,
|
|
726
|
+
dominant_element: dominant,
|
|
727
|
+
elements: (Array.isArray(manifest?.elements) ? manifest.elements : []).filter((e) => String(e?.value ?? "").trim().toLowerCase() !== lcEl),
|
|
728
|
+
// The shorter phrase is the second of the three, and it has to be PUSHED: with `dominant_element`
|
|
729
|
+
// now a single word and `mark` the full phrase, nothing else in the compile would search the
|
|
730
|
+
// remainder as a phrase on its own.
|
|
731
|
+
// `exact-phrase` because that is what it is, and because the category vocabulary is CLOSED —
|
|
732
|
+
// `parseVariantManifestModel` refuses anything outside it, so a descriptive category invented here
|
|
733
|
+
// would fail the manifest on its way into the compile.
|
|
734
|
+
variants: [{ category: "exact-phrase", value: remainder,
|
|
735
|
+
rationale: "the distinctive remainder once the client's own registered element is set aside" },
|
|
736
|
+
...variants],
|
|
737
|
+
};
|
|
738
|
+
// REQUIREMENT 3: the element is still checked ONCE, as a confirmation of the client's own live
|
|
739
|
+
// registrations rather than as a conflict search — so the exclusion is evidenced on the report by a
|
|
740
|
+
// query that ran, not by a sentence saying one would have.
|
|
741
|
+
const confirmation = { axis: "incumbent-class", predicate: "owner", term: element,
|
|
742
|
+
expected_kind: "enumerate", provenance: "mark", house_element_confirmation: true };
|
|
743
|
+
return { manifest: next, confirmation, refused: null };
|
|
744
|
+
}
|
|
745
|
+
|
|
746
|
+
export function compileRegisterPlan({ manifest, job, form = null, skillVersion = "", capabilities = null, unavailableOffices = [], houseElement = null }) {
|
|
747
|
+
// ── AN EXCLUDED ELEMENT'S FORM BAND MUST BE UNREACHABLE, NOT MERELY UNASKED-FOR ─────────────────
|
|
748
|
+
//
|
|
749
|
+
// THE DEFECT THIS CLOSES, found by following the seam rather than by a failing arm. `bandFor` falls
|
|
750
|
+
// back to `elements[0]` when it cannot find the element it was asked for. The house-element exclusion
|
|
751
|
+
// changes `dominant_element` to the remainder's dominant word — a word the form neighbourhood, derived
|
|
752
|
+
// earlier from the original manifest, may carry no band for. The lookup would then MISS and the
|
|
753
|
+
// fallback would hand back the first element's band, which is the house element's: the exclusion would
|
|
754
|
+
// appear to work, `dominant_element` would read correctly on the plan, and the one-letter mutation
|
|
755
|
+
// floor of the very element being excluded would compile anyway. Silently, and it is the whole flood.
|
|
756
|
+
//
|
|
757
|
+
// So the element is removed from the form document here, where every `bandFor` call in this compile
|
|
758
|
+
// reads it. Unreachable beats un-asked-for: a fallback cannot select what is not there.
|
|
759
|
+
if (houseElement && form?.elements) {
|
|
760
|
+
const lcHouse = String(houseElement).trim().toLowerCase();
|
|
761
|
+
const kept = form.elements.filter((e) => String(e?.element ?? "").trim().toLowerCase() !== lcHouse);
|
|
762
|
+
form = { ...form, elements: kept };
|
|
763
|
+
}
|
|
577
764
|
const classes = (job?.classes ?? []).map(String).filter(Boolean);
|
|
578
765
|
if (!classes.length) throw new Error("register_plan_classes_missing: a plan is always class-scoped — compile with the matter's in-scope Nice classes");
|
|
579
766
|
const caps = capabilities ?? null;
|
|
@@ -792,7 +979,7 @@ export function compileRegisterPlan({ manifest, job, form = null, skillVersion =
|
|
|
792
979
|
// visible from the definition rather than inferred from an absence.
|
|
793
980
|
const STRIPPED_CATEGORIES = new Set(["phonetic", "transliteration", "visual"]);
|
|
794
981
|
// — THE DOCTRINE'S OWN DISPATCH TABLE, NOW BINDABLE. The universal-categories table
|
|
795
|
-
// (
|
|
982
|
+
// (clearance-variants SKILL.md) states the mode per tag: `exact-element` sweeps default, `plural-root`
|
|
796
983
|
// is a root (the contains match is its whole purpose), and `formative-family` is "never exact-only".
|
|
797
984
|
// Until the enum accepted these tags the mandate bound to nothing — measured: three root-shaped
|
|
798
985
|
// strings dispatched exact, 4/2/4 records, the family they exist to reach retrieved zero times.
|
|
@@ -15,7 +15,7 @@
|
|
|
15
15
|
// ordering, and renewal/expiry cycle arithmetic (renewals fall at year 10/20/… from registration — the
|
|
16
16
|
// check that catches "registration 2013 … renewed 2025" from the document alone).
|
|
17
17
|
|
|
18
|
-
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, openSync, readSync, closeSync } from "node:fs";
|
|
18
|
+
import { readFileSync, writeFileSync, mkdirSync, existsSync, readdirSync, openSync, readSync, closeSync } from "node:fs"; import { runPrefixSpellings } from "../shared/pre-rename-spellings.mjs";
|
|
19
19
|
import { join } from "node:path";
|
|
20
20
|
import { driverDir, ensureDriverDir } from "../shared/driver-dir.mjs"; // — one definition of where `_driver/` is
|
|
21
21
|
import { ledgerPath, runRecordLogPath, ledgerDeprecationNotice, retiredGlobalRecordLogNotice }
|
|
@@ -34,8 +34,8 @@ function stripGatewayNs(s) {
|
|
|
34
34
|
return typeof s === "string" ? s.replace(/^agent:[^:]+:/, "") : "";
|
|
35
35
|
}
|
|
36
36
|
function rowMatchesRun(row, runPrefix) {
|
|
37
|
-
return stripGatewayNs(row.sessionKey).startsWith(
|
|
38
|
-
|| stripGatewayNs(row.sessionId ?? "").startsWith(
|
|
37
|
+
return runPrefixSpellings(runPrefix).some((rp) => stripGatewayNs(row.sessionKey).startsWith(rp)
|
|
38
|
+
|| stripGatewayNs(row.sessionId ?? "").startsWith(rp)); // either spelling: a run resumed across the rename
|
|
39
39
|
}
|
|
40
40
|
|
|
41
41
|
|
|
@@ -586,7 +586,7 @@ export const REPAIR_COMPOSERS = [
|
|
|
586
586
|
//
|
|
587
587
|
// `samplesForStage` IS REQUIRED, and for the reason `*:lint-repair`'s note above gives rather than the
|
|
588
588
|
// one it looks like: a `stage: "*"` composer is walked for EVERY recording stage, so the fixed sample's
|
|
589
|
-
// `record_frame_diff` is read as an order handed to blind-frame, matter-frame,
|
|
589
|
+
// `record_frame_diff` is read as an order handed to blind-frame, matter-frame, clearance-variants,
|
|
590
590
|
// report-overview, report-card and doubt-closure — six ordered-but-not-granted findings that are
|
|
591
591
|
// artifacts of the SAMPLE, not of the tree. At dispatch the tool is always the walking stage's own,
|
|
592
592
|
// because it is derived from `out`. Removing this hook reproduces all six.
|
|
@@ -116,7 +116,7 @@ export function failingTarget(lastFail, files = []) {
|
|
|
116
116
|
if (!named) return null;
|
|
117
117
|
const list = (Array.isArray(files) ? files : [files]).filter(Boolean).map(String);
|
|
118
118
|
if (list.length <= 1) return list[0] ?? null;
|
|
119
|
-
// longest suffix match wins: "x/register-findings.md" identifies ".../
|
|
119
|
+
// longest suffix match wins: "x/register-findings.md" identifies ".../clearance-search/x/register-findings.md"
|
|
120
120
|
const hit = list.find((f) => f === named || f.endsWith(`/${named}`) || named.endsWith(`/${f}`));
|
|
121
121
|
return hit ?? null;
|
|
122
122
|
}
|
|
@@ -22,14 +22,14 @@
|
|
|
22
22
|
// an INTENDED fix (then --update on the merged result).
|
|
23
23
|
//
|
|
24
24
|
// Env: CLEAROTRON_REPLAY_ROOTS colon-separated corpus roots
|
|
25
|
-
// (default: <workspaceRoot>/workspace-*/studio/
|
|
26
|
-
// CLEAROTRON_REPLAY_SNAPSHOT snapshot path (default: ~/.
|
|
25
|
+
// (default: <workspaceRoot>/workspace-*/studio/clearance-search — live slugs + archive/)
|
|
26
|
+
// CLEAROTRON_REPLAY_SNAPSHOT snapshot path (default: ~/.clearance-replay-snapshot.json)
|
|
27
27
|
|
|
28
28
|
import "../shared/env-local.mjs"; // — FIRST: the CLEAROTRON_* translation must land before any
|
|
29
29
|
// module-top capture below it evaluates. A call in this file's BODY
|
|
30
30
|
// would run too late — that was the repair that left this open.
|
|
31
31
|
import { readFileSync, writeFileSync, readdirSync, existsSync, statSync } from "node:fs";
|
|
32
|
-
import { join, basename } from "node:path";
|
|
32
|
+
import { join, basename } from "node:path"; import { studioDirFor } from "../shared/pre-rename-spellings.mjs";
|
|
33
33
|
import { DRIVER_DIR, driverDir } from "../shared/driver-dir.mjs"; //
|
|
34
34
|
import { homedir } from "node:os";
|
|
35
35
|
import { validators } from "./verify.mjs";
|
|
@@ -62,7 +62,7 @@ function looksLikeRunDir(p) {
|
|
|
62
62
|
return n.includes(DRIVER_DIR) || n.some((f) => FILE_CHECKS[f]);
|
|
63
63
|
}
|
|
64
64
|
|
|
65
|
-
// Corpus roots → sorted run dirs. Layout per root (a workspace's studio/
|
|
65
|
+
// Corpus roots → sorted run dirs. Layout per root (a workspace's studio/clearance-search):
|
|
66
66
|
// <slug>/<date>-<codename>/ (live slugs)
|
|
67
67
|
// archive/<YYYY-MM>/<slug>/<date>-<codename>/ (archived)
|
|
68
68
|
export function discoverRuns(roots) {
|
|
@@ -219,12 +219,12 @@ function main() {
|
|
|
219
219
|
const args = new Set(process.argv.slice(2));
|
|
220
220
|
// ON-DISK NAME, NOT A PRODUCT NAME: an install that never set the variable already has this file, so
|
|
221
221
|
// renaming the default points the reader at one that does not exist. Ruling.
|
|
222
|
-
const snapshotPath = process.env.CLEAROTRON_REPLAY_SNAPSHOT || join(homedir(), ".prelim-replay-snapshot.json");
|
|
222
|
+
const snapshotPath = process.env.CLEAROTRON_REPLAY_SNAPSHOT || [join(homedir(), ".prelim-replay-snapshot.json"), join(homedir(), ".clearance-replay-snapshot.json")].find((f, i) => i === 1 || existsSync(f)); // the file the install already has wins
|
|
223
223
|
const roots = process.env.CLEAROTRON_REPLAY_ROOTS
|
|
224
224
|
? process.env.CLEAROTRON_REPLAY_ROOTS.split(":").filter(Boolean)
|
|
225
225
|
: names(config.workspaceRoot)
|
|
226
226
|
.filter((d) => config.agentIdFromWorkspaceName(d) != null)
|
|
227
|
-
.map((d) => join(config.workspaceRoot, d
|
|
227
|
+
.map((d) => studioDirFor(join(config.workspaceRoot, d)));
|
|
228
228
|
|
|
229
229
|
const runDirs = discoverRuns(roots);
|
|
230
230
|
if (!runDirs.length) {
|
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
// Copyright 2026 Cordillera Sàrl. Additional terms under section 7 of the AGPL-3.0 apply — see ADDITIONAL-TERMS.md
|
|
3
3
|
// report-overview-record.mjs — the recording transport for the report shell.
|
|
4
4
|
//
|
|
5
|
-
// Conversion 4, after blind-frame, skeptic, frame-diff, matter-frame and
|
|
5
|
+
// Conversion 4, after blind-frame, skeptic, frame-diff, matter-frame and clearance-variants
|
|
6
6
|
//. It is the FIRST conversion whose artifact a client reads. report-overview.md is not an internal
|
|
7
7
|
// input that a later stage consumes — it is the front-matter and the Actions section of the delivered
|
|
8
8
|
// report, so a render defect here reaches a lawyer's desk rather than a test log.
|
|
@@ -181,7 +181,7 @@ export function renderReportOverview(model, identity) {
|
|
|
181
181
|
const id = identity ?? {};
|
|
182
182
|
const fm = [
|
|
183
183
|
"---",
|
|
184
|
-
"type:
|
|
184
|
+
"type: clearance-clearance",
|
|
185
185
|
id.matter ? `matter: ${id.matter}` : "",
|
|
186
186
|
id.title ? `title: ${id.title}` : "",
|
|
187
187
|
id.client ? `client: ${id.client}` : "",
|
|
@@ -81,8 +81,8 @@ export const RESULT_NOUN_FIELDS = Object.freeze([
|
|
|
81
81
|
+ "is the fact the disclosure downstream depends on" },
|
|
82
82
|
{ file: "driver/pipeline.mjs", noun: "settled", sites: 5, atWriteSite: 4, verdict: "result",
|
|
83
83
|
why: "counts off the union and the doubt ledger" },
|
|
84
|
-
{ file: "driver/pipeline.mjs", noun: "verified", sites:
|
|
85
|
-
why: "`rows.filter(r => r.verified).length`, where each row's flag is `srRecords.has(senior.uri)` — a lookup, not a call" },
|
|
84
|
+
{ file: "driver/pipeline.mjs", noun: "verified", sites: 4, atWriteSite: 2, verdict: "result",
|
|
85
|
+
why: "two sites, both measured states rather than calls returning. (1) `rows.filter(r => r.verified).length`, where each row's flag is `srRecords.has(senior.uri)` — a lookup, not a call. (2) the house-element ownership row's `verified: receipt.verified`, which the verifier sets only where a returned register record matched the client's own name AND was live AND carried an instructed class; the lookup CALL returning is a separate field on the same receipt (`reason`), and an unanswered lookup writes this false" },
|
|
86
86
|
{ file: "driver/repairs.mjs", noun: "closed", sites: 2, atWriteSite: 1, verdict: "result",
|
|
87
87
|
why: "`effect.closed`, measured against `effect.asked`" },
|
|
88
88
|
{ file: "driver/repairs.mjs", noun: "outcome", sites: 1, atWriteSite: 1, verdict: "result",
|
package/driver/run-economics.mjs
CHANGED
|
@@ -14,11 +14,14 @@
|
|
|
14
14
|
// the provider's four separately-priced token kinds.)
|
|
15
15
|
//
|
|
16
16
|
// ── WHAT A "DISPATCH" IS ──────────────────────────────────────────────────────────────────────────
|
|
17
|
-
// One model invocation: one row in `_driver/<stage>.jsonl`
|
|
18
|
-
// per ATTEMPT, so retries are separate dispatches and retry
|
|
19
|
-
// direct-API jx lanes bypass the gateway and write
|
|
20
|
-
// {model, usage} shape; they are dispatches too, under stage
|
|
21
|
-
// (run events, not dispatches) — same file selection as
|
|
17
|
+
// One model invocation: one row in `_driver/<stage>.jsonl` that tokens.mjs's `isAttemptRow` counts as
|
|
18
|
+
// a provider attempt (gateway.mjs writes one per ATTEMPT, so retries are separate dispatches and retry
|
|
19
|
+
// waste is counted, not averaged away). The direct-API jx lanes bypass the gateway and write
|
|
20
|
+
// `_driver/jx-completions.jsonl` in the same {model, usage} shape; they are dispatches too, under stage
|
|
21
|
+
// `jx-completions`. `run.jsonl` is skipped (run events, not dispatches) — same file selection as
|
|
22
|
+
// tokens.mjs, deliberately, and the SAME ROW TEST as tokens.mjs, imported rather than copied: a jx row
|
|
23
|
+
// whose turn ran and named no model carries no `model`, only `modelActual: null`, and a census that still
|
|
24
|
+
// asked for a `model` counted no dispatch and no tokens for a turn the token rollup counted with both.
|
|
22
25
|
//
|
|
23
26
|
// ── ZERO SEMANTICS: THE THING THIS MODULE EXISTS TO GET RIGHT ─────────────────────────────────────
|
|
24
27
|
// tokens.mjs sums `usage` with `u.output || 0`, so a dispatch whose usage is null contributes zero AND
|
|
@@ -32,7 +35,7 @@
|
|
|
32
35
|
// measured — the dispatch journalled a usage object (from the provider's own result envelope)
|
|
33
36
|
// streamed — usage present but RECONSTRUCTED from the stream (`signals.usageStreamed`), because the
|
|
34
37
|
// turn died before its result event. A real measurement, a weaker one, counted apart.
|
|
35
|
-
// unmeasured — the row is a dispatch
|
|
38
|
+
// unmeasured — the row is a dispatch and carries no usage at all. THE KILLED TURNS.
|
|
36
39
|
// `tokensComplete` is false whenever `unmeasured > 0`, at run level and per stage, and
|
|
37
40
|
// `unmeasuredDispatches[]` names which ones so a reader can see what the total is missing.
|
|
38
41
|
//
|
|
@@ -92,14 +95,18 @@
|
|
|
92
95
|
// so a per-record basis is the only honest shape. `tokens.mjs` keys its rollup on the requested alias
|
|
93
96
|
// for the same reason and is likewise untouched.
|
|
94
97
|
//
|
|
95
|
-
// Pure by contract: `runEconomics()` reads the run dir and nothing else — no env, no config, no network
|
|
96
|
-
//
|
|
98
|
+
// Pure by contract: `runEconomics()` reads the run dir and nothing else — no env, no config, no network.
|
|
99
|
+
// Its only driver import is the attempt-row test it shares with tokens.mjs; the log and status writers
|
|
100
|
+
// serve `stampRunEconomics()`, the only part that writes.
|
|
97
101
|
|
|
98
102
|
import { readdirSync, readFileSync, writeFileSync } from "node:fs";
|
|
99
103
|
import { join } from "node:path";
|
|
100
104
|
import { driverDir } from "../shared/driver-dir.mjs"; //
|
|
101
105
|
import { runLog, note } from "./log.mjs";
|
|
102
106
|
import { writeRunStatus } from "./progress.mjs";
|
|
107
|
+
// tokens.mjs imports this module too (isCodeSide, stampRunEconomics). The cycle is safe because each side
|
|
108
|
+
// reads the other's bindings only inside functions, never while the module is loading.
|
|
109
|
+
import { isAttemptRow } from "./tokens.mjs";
|
|
103
110
|
|
|
104
111
|
/**
|
|
105
112
|
* The provider's separately-priced token kinds, in the driver's own `usage` vocabulary (gateway.mjs /
|
|
@@ -178,7 +185,23 @@ function billingKeyOf(rec) {
|
|
|
178
185
|
// is rather than dragged into "unknown" beside genuinely unstamped legacy rows.
|
|
179
186
|
const engine = isCodeSide(rec) ? "code" : String(rec.engine ?? "unknown");
|
|
180
187
|
const authMode = isCodeSide(rec) ? "not-provider-billed" : String(rec.authMode ?? "unknown");
|
|
181
|
-
|
|
188
|
+
// A TURN THAT NAMED NO MODEL (a jx row with `modelActual: null` and no `model`) is keyed the way the
|
|
189
|
+
// token rollup keys it (modelKey in tokens.mjs): `<engine>/no-model-reported`, a name that says the
|
|
190
|
+
// model is missing. Read through the old `?? "unknown"` it landed beside legacy rows nobody stamped,
|
|
191
|
+
// and its byBilling bucket named a different model from the rollup's byModel for the same turn.
|
|
192
|
+
//
|
|
193
|
+
// A COPY OF modelKey's RULE, NOT A SHARED ONE: tokens.mjs does not export it. So the test for "no stamp"
|
|
194
|
+
// is modelKey's own, a non-empty string `modelUsed`, and not `modelUsed == null`: under that looser test
|
|
195
|
+
// a row stamped `modelUsed: ""` keyed its bucket as the empty string while the rollup keyed the same
|
|
196
|
+
// turn `<engine>/no-model-reported`. Two copies of one rule drifting apart is how the census and the
|
|
197
|
+
// rollup came to disagree about what an attempt is, so the tests hold these two copies to each other on
|
|
198
|
+
// the rows the engine writes. They still part on a row no writer produces: no model and no engine, or
|
|
199
|
+
// engine `anthropic-agent`. This key names the missing model there, while modelKey resolves the absent
|
|
200
|
+
// model through the catalog before it asks whether one exists, and buckets the row as `undefined`.
|
|
201
|
+
const stamped = typeof rec.modelUsed === "string" && rec.modelUsed;
|
|
202
|
+
const model = !stamped && typeof rec.model !== "string"
|
|
203
|
+
? `${typeof rec.engine === "string" && rec.engine ? rec.engine : "unknown"}/no-model-reported`
|
|
204
|
+
: String(rec.modelUsed ?? rec.model ?? "unknown");
|
|
182
205
|
return { engine, authMode, model, key: `${engine}|${authMode}|${model}` };
|
|
183
206
|
}
|
|
184
207
|
|
|
@@ -223,11 +246,19 @@ function foldBilling(bucketMap, rec, cls) {
|
|
|
223
246
|
// future engine whose name began that way, which is how a vendor claim becomes a guess. An engine this
|
|
224
247
|
// table does not know is reported BY NAME and blocks the single-vendor claim, because "I do not know who
|
|
225
248
|
// billed this" and "one vendor" are different answers and only one of them is safe to print.
|
|
249
|
+
//
|
|
250
|
+
// THE NATIVE-LANGUAGE ROWS STAMP THE VENDOR ITSELF. jxBillingStamp (jx-lanes.mjs) writes as the row's
|
|
251
|
+
// engine the provider the engine door resolved, `anthropic` or `openai` (engine/auth.mjs), so those two
|
|
252
|
+
// names are engines this table must place. Without them an Anthropic-only run with a native-language turn
|
|
253
|
+
// named `anthropic` as an engine that bills to no vendor, in the same sentence that named anthropic as its
|
|
254
|
+
// one vendor. Two exact names, still a closed table.
|
|
226
255
|
export const ENGINE_VENDORS = Object.freeze({
|
|
227
256
|
"anthropic-agent": "anthropic",
|
|
228
257
|
"anthropic-direct": "anthropic",
|
|
229
258
|
"anthropic-completions": "anthropic",
|
|
230
259
|
"openai-agent": "openai",
|
|
260
|
+
"anthropic": "anthropic",
|
|
261
|
+
"openai": "openai",
|
|
231
262
|
});
|
|
232
263
|
/** The vendor an engine bills to, or null when the table does not name one. */
|
|
233
264
|
export const vendorOf = (engine) => ENGINE_VENDORS[String(engine ?? "")] ?? null;
|
|
@@ -469,7 +500,7 @@ export function runEconomics(runDir, { now = null, bytesPerOutputToken = BYTES_P
|
|
|
469
500
|
let sawDeclaredNoOutput = false;
|
|
470
501
|
|
|
471
502
|
for (const rec of rows) {
|
|
472
|
-
if (!rec
|
|
503
|
+
if (!isAttemptRow(rec)) continue; // only provider attempts are dispatches (tokens.mjs, isAttemptRow)
|
|
473
504
|
const cls = classesOf(rec.usage);
|
|
474
505
|
const streamed = cls != null && rec.signals?.usageStreamed === true;
|
|
475
506
|
|