clearotron 0.2.4 → 0.3.0-beta.1
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 +13 -2
- package/CONTRIBUTING.md +1 -1
- package/INSTALL.md +62 -38
- package/bin/brandowner.mjs +18 -169
- package/bin/clearotron.mjs +3 -1
- package/bin/connect.mjs +28 -19
- package/bin/disconnect.mjs +3 -3
- package/bin/example.mjs +7 -7
- package/bin/framework-preflight.mjs +49 -0
- package/bin/grant.mjs +151 -93
- package/bin/onboard.mjs +220 -138
- package/bin/start.mjs +146 -137
- package/bin/stop.mjs +2 -2
- package/bin/update.mjs +1 -1
- package/build-info.json +2 -2
- package/docs/CLIENT-MCP.md +2 -2
- package/docs/E2E.md +12 -2
- package/docs/ONBOARDING.md +1 -1
- package/docs/PORTAL.md +14 -13
- package/docs/SECURITY.md +23 -24
- package/docs/architecture/04-configuration-reference.md +12 -5
- package/docs/architecture/05-config-governance.md +7 -7
- package/docs/architecture/07-quality-and-audit.md +1 -1
- package/docs/architecture/08-development-guide.md +5 -0
- package/docs/configuration.md +118 -0
- package/docs/decisions/0004-documentation-structure.md +2 -2
- package/docs/decisions/0006-what-the-public-repository-carries.md +2 -2
- package/driver/CHANGELOG.md +70 -0
- package/driver/ask-ledger.mjs +2 -2
- package/driver/cancel.mjs +27 -0
- package/driver/case-law-sources.mjs +3 -3
- package/driver/company-bundle.mjs +261 -0
- package/driver/compare.mjs +1 -1
- package/driver/compose-read.mjs +2 -2
- package/driver/config-inventory.mjs +2 -2
- package/driver/contract-audit.mjs +1 -1
- package/driver/contract-e3-backlog.mjs +1 -1
- package/driver/contract-vocabulary.mjs +1 -1
- package/driver/declination-call.mjs +1 -1
- package/driver/deliver-trigger.sh +3 -3
- package/driver/dev-portal.mjs +3 -1
- package/driver/digest-queue.mjs +1 -1
- package/driver/disposition-tool.mjs +1 -1
- package/driver/doc-constants.mjs +1 -1
- package/driver/drain-posture.mjs +2 -2
- package/driver/drainer-identity.mjs +1 -1
- package/driver/driver.config.mjs +44 -9
- package/driver/effective-scope.mjs +30 -1
- package/driver/effort-model.mjs +6 -6
- package/driver/engine/CONTRACT.md +2 -2
- package/driver/engine/anthropic-agent.mjs +11 -11
- package/driver/engine/jx-turn.mjs +1 -1
- package/driver/engine/mcp/gather-config.mjs +29 -4
- package/driver/engine/mcp/recording-server.mjs +73 -1
- package/driver/engine/openai-agent.mjs +1 -1
- package/driver/engine/probe.mjs +28 -4
- package/driver/enqueue-schema.mjs +23 -3
- package/driver/findings-model.mjs +2 -2
- package/driver/flag-snapshot.mjs +2 -2
- package/driver/floor-duty.mjs +2 -2
- package/driver/frame-diff-model.mjs +1 -1
- package/driver/framework-preflight.mjs +143 -0
- package/driver/gateway.mjs +9 -1
- package/driver/hit-list.mjs +1 -1
- package/driver/jx-lanes.mjs +1 -1
- package/driver/jx.mjs +1 -1
- package/driver/knockout-assess-record.mjs +1 -1
- package/driver/knockout-review-record.mjs +435 -0
- package/driver/order-probe.mjs +1 -1
- package/driver/outbox-backoff.mjs +2 -2
- package/driver/owner-use-check.mjs +2 -2
- package/driver/package.json +1 -1
- package/driver/pipeline-knockout.mjs +105 -8
- package/driver/pipeline.mjs +81 -30
- package/driver/plain-register.mjs +77 -3
- package/driver/portal-access.mjs +141 -74
- package/driver/portal-config-view.mjs +59 -70
- package/driver/portal-report.mjs +4 -4
- package/driver/portal-service.mjs +348 -141
- package/driver/portal-upstream.mjs +105 -17
- package/driver/predelivery-lint.mjs +43 -18
- package/driver/product-rows.mjs +1 -1
- package/driver/products.mjs +1 -1
- package/driver/profile-page.html +30 -5
- package/driver/profile-service.mjs +197 -26
- package/driver/profiles.mjs +48 -1
- package/driver/publish/index.mjs +31 -13
- package/driver/publish/knockout.mjs +9 -5
- package/driver/publish/office-record-links.mjs +189 -0
- package/driver/publish/parse.mjs +3 -3
- package/driver/publish/publish-inputs.mjs +26 -0
- package/driver/publish/render-knockout.mjs +42 -42
- package/driver/publish/render.mjs +29 -4
- package/driver/publish/report-data.mjs +2 -2
- package/driver/publish/seed-pool.mjs +1 -1
- package/driver/publish/templates/report.css +8 -8
- package/driver/publish/xlsx.mjs +49 -7
- package/driver/queue-watch-verdict.mjs +2 -2
- package/driver/recipe-service.mjs +1 -1
- package/driver/record-carry.mjs +1 -1
- package/driver/reference-score.mjs +1 -1
- package/driver/reference-strip-signatures.mjs +1 -1
- package/driver/register-availability.mjs +4 -3
- package/driver/register-count.mjs +3 -3
- package/driver/register-records.mjs +1 -1
- package/driver/repair-composers.mjs +1 -1
- package/driver/repairs.mjs +3 -3
- package/driver/replay-archive.mjs +1 -1
- package/driver/report-card-record.mjs +1 -1
- package/driver/result-noun-fields.mjs +5 -0
- package/driver/roster-verdict.mjs +48 -5
- package/driver/run-activity.mjs +1 -1
- package/driver/run-requirements.mjs +18 -5
- package/driver/runner.mjs +24 -15
- package/driver/search-policy.mjs +8 -8
- package/driver/senior-rights.mjs +1 -1
- package/driver/skills/prelim-search/delivery-contract.md +1 -1
- package/driver/skills/prelim-search/risk-framework-triage.md +10 -7
- package/driver/stages-knockout.mjs +72 -6
- package/driver/stages.mjs +10 -10
- package/driver/suite-census.json +238 -70
- package/driver/synthesis-record.mjs +2 -2
- package/driver/systemd/clearotron-client-mcp.service +3 -3
- package/driver/systemd/clearotron-deploy.service +2 -2
- package/driver/systemd/clearotron-mcp-face.service +1 -1
- package/driver/systemd/clearotron-portal.service +3 -3
- package/driver/systemd/clearotron-worker.service +5 -5
- package/driver/systemd/install-census.mjs +1 -1
- package/driver/systemd/render-units.mjs +9 -9
- package/driver/terminal-clamp.mjs +1 -1
- package/driver/trigger-cap.mjs +18 -2
- package/driver/unit-inventory.mjs +8 -8
- package/driver/usage-ledger.mjs +5 -3
- package/driver/verify-knockout.mjs +7 -7
- package/driver/verify.mjs +5 -5
- package/driver/whatif-memo-run.mjs +1 -1
- package/driver/whatif-queue.mjs +3 -3
- package/driver/whatif-worker.mjs +2 -2
- package/examples/README.md +1 -1
- package/examples/grants.example.json +25 -24
- package/mcp-server/CHANGELOG.md +10 -0
- package/mcp-server/http-server.mjs +3 -3
- package/mcp-server/key-socket.mjs +1 -1
- package/mcp-server/lib/audit-view.mjs +3 -3
- package/mcp-server/lib/brief.mjs +3 -3
- package/mcp-server/lib/driver.mjs +1 -1
- package/mcp-server/lib/events.mjs +1 -1
- package/mcp-server/lib/http-handler.mjs +2 -2
- package/mcp-server/lib/instructions.mjs +2 -2
- package/mcp-server/lib/knockout.mjs +1 -1
- package/mcp-server/lib/ops.mjs +6 -3
- package/mcp-server/lib/options.mjs +15 -4
- package/mcp-server/lib/plan.mjs +7 -6
- package/mcp-server/lib/runs.mjs +10 -0
- package/mcp-server/lib/whatif.mjs +5 -5
- package/mcp-server/package.json +1 -1
- package/mcp-server/packs/README.md +1 -1
- package/mcp-server/remote/client-mcp-apikey.service +1 -1
- package/mcp-server/remote/client-mcp.service +2 -2
- package/mcp-server/remote/trademark-artifacts-http.service +1 -1
- package/mcp-server/server.mjs +38 -24
- package/package.json +2 -2
- package/portal-ui/dist/assets/{index-KFAHMgdT.js → index-CWTHP0sH.js} +3471 -1901
- package/portal-ui/dist/assets/{index-1ziUJX1E.css → index-KpytsmNH.css} +79 -26
- package/portal-ui/dist/index.html +2 -2
- package/portal-ui/package.json +1 -1
- package/providers/_shared/lane-probe.mjs +9 -3
- package/providers/jx-subclass/lookup.mjs +1 -1
- package/providers/oauth-mcp-bridge/CHANGELOG.md +4 -0
- package/providers/oauth-mcp-bridge/package.json +1 -1
- package/providers/oauth-mcp-bridge/systemd/courtlistener-mcp.service +1 -1
- package/scripts/citation-drift-report.mjs +1 -1
- package/scripts/citation-line-check.mjs +2 -2
- package/scripts/drive-env-check.mjs +1 -1
- package/scripts/e2e.mjs +5 -5
- package/scripts/env-audit.mjs +13 -1
- package/scripts/headless-page.mjs +5 -5
- package/scripts/live-surface-check.mjs +26 -2
- package/scripts/mint-names-in-force.mjs +19 -5
- package/scripts/mint-reference-strip-backlog.mjs +1 -1
- package/scripts/mint-suite-census.mjs +37 -10
- package/scripts/pack-publishable.mjs +1 -1
- package/scripts/preinstall-node-check.mjs +1 -1
- package/scripts/release-await-cut.mjs +3 -3
- package/scripts/release-cut-decision.mjs +1 -1
- package/scripts/release-dist-tag.mjs +1 -1
- package/scripts/release-install-check.mjs +1 -1
- package/scripts/release-notes-lint.mjs +1 -1
- package/scripts/release-publish-guard.mjs +1 -1
- package/scripts/release-version-pr-checks.mjs +2 -2
- package/scripts/release-version.mjs +61 -5
- package/scripts/render-brand-banner.mjs +1 -1
- package/scripts/render-check.mjs +2 -2
- package/scripts/repo-writes.mjs +1 -1
- package/scripts/report-frame-check.mjs +1 -1
- package/scripts/report-screenshot.mjs +2 -2
- package/scripts/retire-bare-refs.mjs +1 -1
- package/scripts/revisit-render-check.mjs +1 -1
- package/scripts/score.mjs +1 -1
- package/scripts/strip-titles-and-attributions.mjs +389 -0
- package/scripts/strip-tracker-citations.mjs +122 -5
- package/scripts/test-run.mjs +4 -4
- package/scripts/third-party-notices.mjs +1 -1
- package/scripts/verify-publishable.mjs +1 -1
- package/shared/access-audience.mjs +2 -2
- package/shared/anon-overlay.mjs +1 -1
- package/shared/brand.mjs +15 -1
- package/shared/bundle-freshness.mjs +1 -1
- package/shared/bundle-rebuild.mjs +1 -1
- package/shared/checkout-move.mjs +2 -2
- package/shared/client-door.mjs +8 -8
- package/shared/connect-clients.mjs +7 -7
- package/shared/connector-signin-probe.mjs +1 -1
- package/shared/env-aliases.mjs +1 -1
- package/shared/env-local.mjs +5 -5
- package/shared/grants-edit.mjs +76 -0
- package/shared/install-auth.mjs +1 -1
- package/shared/listen.mjs +3 -3
- package/shared/mcp-challenge.mjs +1 -1
- package/shared/names-in-force.mjs +2 -1
- package/shared/onboarding-store.mjs +19 -2
- package/shared/reference-guard-classes.mjs +44 -2
- package/shared/register-selection.mjs +1 -1
- package/shared/scope.mjs +223 -56
- package/shared/secret-file.mjs +1 -1
- package/shared/server-units.mjs +1 -1
- package/shared/staff-domain.mjs +45 -78
- package/shared/summary-blocks.mjs +2 -2
- package/shared/systemd-failure.mjs +3 -3
- package/shared/tracked-files.mjs +1 -1
- package/shared/trigger-lane.mjs +1 -1
- package/shared/tty-style.mjs +1 -1
- package/shared/usage-block.mjs +1 -1
- package/shared/vacuous-pass.mjs +1 -1
- package/shared/verb-shim.mjs +1 -1
|
@@ -2441,7 +2441,7 @@ export function knockoutFindingViews(mark, { manifest = null } = {}) {
|
|
|
2441
2441
|
* Derived through knockoutFindingViews rather than re-deriving that fallback here — one projection
|
|
2442
2442
|
* decides what a finding's number is, and this cell cannot disagree with the sheet it points into.
|
|
2443
2443
|
*/
|
|
2444
|
-
// ── THE REVIEWER'S NOTES: WHAT EACH ONE IS ABOUT (
|
|
2444
|
+
// ── THE REVIEWER'S NOTES: WHAT EACH ONE IS ABOUT (A.4, rule 5) ──────────────────────────────────────
|
|
2445
2445
|
//
|
|
2446
2446
|
// The knockout page prints a note about the REQUEST at the top, above the conflicts, and a note about
|
|
2447
2447
|
// the NAME under that name's cards. A note saying the screen may have been scoped to the wrong market
|
|
@@ -2454,7 +2454,7 @@ export function knockoutFindingViews(mark, { manifest = null } = {}) {
|
|
|
2454
2454
|
//
|
|
2455
2455
|
// IT SORTS ON WHAT THE NOTE TALKS ABOUT, and the doctrine is written to match: a note about the request
|
|
2456
2456
|
// NAMES the request. The word set spans both vocabularies deliberately — "dispatch" and "instructed"
|
|
2457
|
-
// are what runs
|
|
2457
|
+
// are what earlier runs say, "the request" and "was asked" are what runs
|
|
2458
2458
|
// written after it say — so one reader serves the archive and the new doctrine at once.
|
|
2459
2459
|
export const REQUEST_NOTE_WORDS = /\b(?:dispatch|the request|the requester|instructed|was asked)\b/i;
|
|
2460
2460
|
|
package/driver/flag-snapshot.mjs
CHANGED
|
@@ -277,7 +277,7 @@ export function postureDelta(snapshot, expected = PRODUCTION_POSTURE) {
|
|
|
277
277
|
* hardcoded expectation. This answers how it differs from the box it is being read on, which is the
|
|
278
278
|
* question a staff member actually has in front of the capability page.
|
|
279
279
|
*
|
|
280
|
-
* WHY AN AGE IS NOT THE GUARD IT LOOKS LIKE
|
|
280
|
+
* WHY AN AGE IS NOT THE GUARD IT LOOKS LIKE. The snapshot's only writers were a run
|
|
281
281
|
* and the launcher, so a deployment being CONFIGURED — which runs nothing by definition — holds a
|
|
282
282
|
* capture that is stale for exactly as long as somebody is working on it, which is exactly when they
|
|
283
283
|
* are looking at the page. Found live: the register provider was moved from signa to clarivate and every
|
|
@@ -526,7 +526,7 @@ export function isStale(snapshot, { now, maxAgeMs = 24 * 60 * 60 * 1000 }) {
|
|
|
526
526
|
* the class of defect the snapshot exists to remove rather than to add to.
|
|
527
527
|
*
|
|
528
528
|
* Callable from the portal because the portal HAS the engine environment: one configuration per server
|
|
529
|
-
* box,
|
|
529
|
+
* box, ruling 2026-08-26, `EnvironmentFile=%h/.env` on every unit. Its result is for COMPARISON
|
|
530
530
|
* only — see portal-config-view.mjs on why the page still renders the capture and never this.
|
|
531
531
|
*/
|
|
532
532
|
export async function livePosture({ env = process.env } = {}) {
|
package/driver/floor-duty.mjs
CHANGED
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// WHY THIS READS THE FORM AND NOT THE PROSE. The obligation used to read "placed or explicitly reasoned
|
|
15
15
|
// away", and *reasoned away* is prose — unfalsifiable by machine. On the first of those runs 39 of the 45
|
|
16
16
|
// missing marks ARE discussed somewhere in the recommendations and 0 of the 45 record ids are named, so
|
|
17
|
-
// "discussed" and "reasoned away" cannot be told apart from the text. The
|
|
17
|
+
// "discussed" and "reasoned away" cannot be told apart from the text. The ruling of 2026-08-20
|
|
18
18
|
// settled it: the duty survives, narrowed to the must-check list, discharged by NAMING THE RECORD ID
|
|
19
19
|
// with a one-line ground. That is a row on a form, and a row is checkable.
|
|
20
20
|
//
|
|
@@ -65,7 +65,7 @@ export function armFloorDuty(runDir, { now = () => new Date().toISOString() } =
|
|
|
65
65
|
ensureDriverDir(String(runDir ?? ""));
|
|
66
66
|
writeFileSync(driverDir(String(runDir ?? ""), FLOOR_DUTY_STAMP), JSON.stringify({
|
|
67
67
|
_provenance: "the placement pass on this run ran under code that orders the floors by record id "
|
|
68
|
-
+ "(#1117), so its floor account may be held to that order
|
|
68
|
+
+ "(#1117), so its floor account may be held to that order",
|
|
69
69
|
ts: now(),
|
|
70
70
|
}, null, 2) + "\n");
|
|
71
71
|
return true;
|
|
@@ -436,7 +436,7 @@ export function effectiveInScope(hit, scopeJurisdictions = []) {
|
|
|
436
436
|
// does a SEARCHED token mechanically cover a scoped jurisdiction j? (exact, or an EU search covering EU-scope).
|
|
437
437
|
// A member scoped but only EU searched is NOT covered (national rights need a national search) → stays a gap.
|
|
438
438
|
//
|
|
439
|
-
// EXPORTED for the delivered-narrative coverage check (predelivery-lint.mjs
|
|
439
|
+
// EXPORTED for the delivered-narrative coverage check (predelivery-lint.mjs), which asks
|
|
440
440
|
// the same question of a different claimant: not "did the plan cover the scope" but "does the prose claim
|
|
441
441
|
// coverage of a territory the run did not search". One copy of the EU-reach rule, because two copies drifting
|
|
442
442
|
// apart would be a worse defect than the disagreement this is being exported to catch. Callers pass
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
|
+
// Copyright 2026 Cordillera Sàrl. Additional terms under section 7 of the AGPL-3.0 apply — see ADDITIONAL-TERMS.md
|
|
3
|
+
//
|
|
4
|
+
// THE FRAMEWORK PRE-FLIGHT — read a deck and its manifest and say what they declare, before either
|
|
5
|
+
// rates a real matter.
|
|
6
|
+
//
|
|
7
|
+
// A customer's counsel writes a risk framework as two files: a prose deck they can argue with, and a
|
|
8
|
+
// small manifest that gives code the band words. Until now the only way to find out whether the pair
|
|
9
|
+
// worked was to point a company at it and run a matter. Three things went wrong in that order, and each
|
|
10
|
+
// of them looked like something else:
|
|
11
|
+
//
|
|
12
|
+
// the manifest is refused — the run fails by a token, hours in, naming a JSON key
|
|
13
|
+
// the deck and the manifest differ — nothing fails; the profile screen renders the framework's title
|
|
14
|
+
// and its coloured band pills and silently omits the box saying what
|
|
15
|
+
// the bands mean, so the page looks built and is missing half of it
|
|
16
|
+
// the deck resolves somewhere else — nothing fails; another framework rates the matter
|
|
17
|
+
//
|
|
18
|
+
// IT REPORTS AND DOES NOT GRADE. The pre-flight says what the framework declares — the ladder in its own
|
|
19
|
+
// order, the entity the deck names, the shape it is, which file answered — and where the two files
|
|
20
|
+
// disagree it says which band and what the deck did not do. It has no opinion on whether the rubric is a
|
|
21
|
+
// good one: a rubric is a legal document, and a check that scored one would be a content lint in a
|
|
22
|
+
// different costume. Nothing here reads what a band MEANS, only that the deck defines it.
|
|
23
|
+
//
|
|
24
|
+
// AGREEMENT IS THE RENDERER'S OWN READ, not a second one. `bandMeaningRows` in profile-service.mjs is the
|
|
25
|
+
// walk the profile screen makes; this calls it. A pre-flight with its own matcher would pass a deck the
|
|
26
|
+
// page then drops, which is the defect it exists to remove rather than a smaller version of it.
|
|
27
|
+
|
|
28
|
+
import { readFileSync } from "node:fs";
|
|
29
|
+
import { config } from "./driver.config.mjs";
|
|
30
|
+
import { loadFrameworkManifest, manifestPathFor, DEFAULT_FRAMEWORK } from "./framework.mjs";
|
|
31
|
+
import { bandMeaningRows } from "./profile-service.mjs";
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* WHY A DECK SERVED FROM THE REPO IS WORTH A SENTENCE, and why only sometimes.
|
|
35
|
+
*
|
|
36
|
+
* Skill resolution is layered: the config store wins, the repo answers when the store is silent. That
|
|
37
|
+
* fallback is the migration design and every generic methodology file relies on it. For a FRAMEWORK it is
|
|
38
|
+
* different in kind, because the shipped tree carries decks under the same filenames customers use for
|
|
39
|
+
* their own — `risk-framework.md`, `risk-framework-demo.md`, `risk-framework-triage.md`, which are the
|
|
40
|
+
* three `files[]` re-includes after excluding `risk-framework-*`. Take a customer's deck out of the
|
|
41
|
+
* config store and the repo's copy answers: readable, valid, and not theirs. Nothing throws and the
|
|
42
|
+
* matter is rated under another company's rubric.
|
|
43
|
+
*
|
|
44
|
+
* So: reported when an overlay is configured and did not hold the file. Silent on a single-tree install,
|
|
45
|
+
* where there is no other copy for it to have been.
|
|
46
|
+
*/
|
|
47
|
+
export const SUBSTITUTION_SAY = (r) =>
|
|
48
|
+
`served from the shipped tree, not the configured store — the store has no ${r.rel}, so ${r.basePath} answered. `
|
|
49
|
+
+ `A customer deck removed from the store is replaced by the shipped file of the same name rather than missed.`;
|
|
50
|
+
|
|
51
|
+
const layerSay = {
|
|
52
|
+
overlay: "the configured store",
|
|
53
|
+
base: "the shipped tree (the configured store does not hold it)",
|
|
54
|
+
"base-only": "the shipped tree",
|
|
55
|
+
missing: "nowhere — neither the configured store nor the shipped tree holds it",
|
|
56
|
+
};
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* Everything the pre-flight found about one framework, as data. `ok` is the whole verdict; `refusals`
|
|
60
|
+
* says why when it is false, in sentences written for the person who wrote the files.
|
|
61
|
+
*
|
|
62
|
+
* `resolve` and `read` are injected so the lint can drive a planted tree — the CLI passes neither and
|
|
63
|
+
* gets the engine's own resolution, which is the point of the check.
|
|
64
|
+
*/
|
|
65
|
+
export function preflightFramework(fwPath, { resolve, read } = {}) {
|
|
66
|
+
const resolveOne = resolve ?? ((rel) => config.resolveSkillPathReport(rel));
|
|
67
|
+
const readOne = read ?? ((p) => readFileSync(p, "utf8"));
|
|
68
|
+
const manifestRel = manifestPathFor(fwPath);
|
|
69
|
+
const out = {
|
|
70
|
+
fwPath, manifestRel, ok: false, refusals: [], substitutions: [],
|
|
71
|
+
deck: null, manifest: null, declares: null, bands: null,
|
|
72
|
+
};
|
|
73
|
+
|
|
74
|
+
for (const [name, rel] of [["deck", fwPath], ["manifest", manifestRel]]) {
|
|
75
|
+
let r;
|
|
76
|
+
try { r = resolveOne(rel); }
|
|
77
|
+
catch (e) { out.refusals.push({ code: "skills_overlay_unreadable", say: String(e?.message ?? e) }); return out; }
|
|
78
|
+
out[name] = { rel, path: r.path, layer: r.layer, from: layerSay[r.layer] ?? r.layer };
|
|
79
|
+
if (r.layer === "base") out.substitutions.push({ ...r, say: SUBSTITUTION_SAY(r) });
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
let manifest;
|
|
83
|
+
try { manifest = loadFrameworkManifest((rel) => resolveOne(rel).path, fwPath); }
|
|
84
|
+
catch (e) {
|
|
85
|
+
out.refusals.push({ code: "manifest", say: String(e?.message ?? e) });
|
|
86
|
+
return out;
|
|
87
|
+
}
|
|
88
|
+
out.declares = {
|
|
89
|
+
key: manifest.framework_key, title: manifest.title, entity: manifest.entity_label,
|
|
90
|
+
sourceDeck: manifest.source_deck, shape: manifest.structure.kind,
|
|
91
|
+
ladder: manifest.bands.map((b) => ({ label: b.label, tone: b.tone })),
|
|
92
|
+
};
|
|
93
|
+
|
|
94
|
+
let deckText;
|
|
95
|
+
try { deckText = readOne(out.deck.path); }
|
|
96
|
+
catch (e) {
|
|
97
|
+
out.refusals.push({ code: "deck_unreadable", say: `the deck could not be read at ${out.deck.path}: ${String(e?.message ?? e)}` });
|
|
98
|
+
return out;
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
// The entity label has to appear in the prose, because the deck speaks about the company by name and a
|
|
102
|
+
// manifest naming a company the deck never mentions is a pair from two different frameworks.
|
|
103
|
+
if (!deckText.toLowerCase().includes(String(manifest.entity_label).toLowerCase()))
|
|
104
|
+
out.refusals.push({ code: "entity_absent",
|
|
105
|
+
say: `the manifest names "${manifest.entity_label}" as the company, and the deck prose never uses that name.` });
|
|
106
|
+
|
|
107
|
+
out.bands = bandMeaningRows(deckText, manifest) ?? [];
|
|
108
|
+
const missed = out.bands.filter((b) => b.miss);
|
|
109
|
+
if (missed.length)
|
|
110
|
+
out.refusals.push({ code: "bands_disagree",
|
|
111
|
+
say: `${missed.length} of ${out.bands.length} bands are named in the manifest and not defined in the deck. `
|
|
112
|
+
+ "The profile screen shows what the bands mean only when EVERY band is defined, so one miss empties the whole box.",
|
|
113
|
+
bands: missed.map((b) => ({ band: b.band, miss: b.miss })) });
|
|
114
|
+
|
|
115
|
+
out.ok = out.refusals.length === 0;
|
|
116
|
+
return out;
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** The report as lines for a reader. No colour, no verdict word the caller has not earned. */
|
|
120
|
+
export function formatPreflight(r) {
|
|
121
|
+
const L = [];
|
|
122
|
+
L.push(`Framework: ${r.fwPath}${r.fwPath === DEFAULT_FRAMEWORK ? " (this installation's default)" : ""}`);
|
|
123
|
+
if (r.deck) L.push(` deck ${r.deck.path}`, ` read from ${r.deck.from}`);
|
|
124
|
+
if (r.manifest) L.push(` manifest ${r.manifest.path}`, ` read from ${r.manifest.from}`);
|
|
125
|
+
if (r.declares) {
|
|
126
|
+
const d = r.declares;
|
|
127
|
+
L.push("", `It declares itself "${d.title}" (${d.key}), a ${d.shape}-shaped framework rating ${d.entity}.`,
|
|
128
|
+
`Transcribed from: ${d.sourceDeck}`, "", "The ladder, highest risk first:");
|
|
129
|
+
for (const [i, b] of d.ladder.entries()) L.push(` ${i + 1}. ${b.label.padEnd(16)} ${b.tone}`);
|
|
130
|
+
}
|
|
131
|
+
if (r.bands?.length) {
|
|
132
|
+
L.push("", "What the deck defines:");
|
|
133
|
+
for (const b of r.bands) L.push(b.miss ? ` ✗ ${b.band.padEnd(16)} ${b.miss}` : ` ✓ ${b.band.padEnd(16)} ${b.meaning}`);
|
|
134
|
+
}
|
|
135
|
+
for (const s of r.substitutions) L.push("", `WARNING ${s.rel}: ${s.say}`);
|
|
136
|
+
if (r.refusals.length) {
|
|
137
|
+
L.push("", "Not ready:");
|
|
138
|
+
for (const f of r.refusals) L.push(` ${f.say}`);
|
|
139
|
+
} else {
|
|
140
|
+
L.push("", "The deck and the manifest agree. Nothing was created and nothing was rated.");
|
|
141
|
+
}
|
|
142
|
+
return L.join("\n");
|
|
143
|
+
}
|
package/driver/gateway.mjs
CHANGED
|
@@ -65,6 +65,13 @@ export const TOOL_WRITTEN_ARTIFACTS = new Map([
|
|
|
65
65
|
refusals: (runDir) => frameRefusalsFor(runDir) }],
|
|
66
66
|
["knockout-frame.md", { tool: "record_knockout_frame", what: "the batch scope note",
|
|
67
67
|
refusals: (runDir) => frameRefusalsFor(runDir) }],
|
|
68
|
+
// The knockout lane's reviewing pass. ONE EXACT BASENAME, and it sits under `_driver/` rather than the
|
|
69
|
+
// run root because the artifact is the pass's account of what it rewrote, not a surface a client is
|
|
70
|
+
// shown — the lookup keys on the basename either way. The row is what makes a repair on this stage a
|
|
71
|
+
// CALL: the seat's grant carries no Write, so a ladder handing it the write form would order a
|
|
72
|
+
// hand-write it cannot perform, on a stage whose failure is deliberately quiet.
|
|
73
|
+
["knockout-review.json", { tool: "record_knockout_review", what: "your rewrites of the lines a reader sees first",
|
|
74
|
+
refusals: (runDir) => knockoutReviewRefusalsFor(runDir) }],
|
|
68
75
|
[BLIND_FRAME_MODEL_FILE, { tool: "record_blind_frame", what: "the threat model" }],
|
|
69
76
|
[SKEPTIC_FLAGS_FILE, { tool: "record_skeptic", what: "your flags and escalation decisions" }],
|
|
70
77
|
["senior-eye-review.md", { tool: "record_narrative_refutation", what: "your verdict and typed flags" }],
|
|
@@ -227,6 +234,7 @@ import { KNOCKOUT_ASSESS_CHUNK_RE, knockoutAssessChunkFile } from "./knockout-as
|
|
|
227
234
|
// The frame transport's refusal reader, so a missing plan or note can be told apart from a stage that
|
|
228
235
|
// never called its tool — the distinction, which its sibling stage still lacks.
|
|
229
236
|
import { frameRefusalsFor } from "./knockout-frame-record.mjs";
|
|
237
|
+
import { knockoutReviewRefusalsFor } from "./knockout-review-record.mjs";
|
|
230
238
|
import { profilesStoreDir } from "./profiles.mjs";
|
|
231
239
|
// The supplemental lane's single source (stages.mjs). The import is acyclic — stages.mjs reads
|
|
232
240
|
// verify/coverage-ledger/config/framework and never reaches back here.
|
|
@@ -577,7 +585,7 @@ export function selectEngine() {
|
|
|
577
585
|
// name: an enrolled-token veto is an enumerated check, and a fifth token forgetting to enrol is the
|
|
578
586
|
// move-blindness this repo has counted seven instances of.
|
|
579
587
|
//
|
|
580
|
-
// What died here with the form path (2026-08-17
|
|
588
|
+
// What died here with the form path (2026-08-17 ruling, delete-not-gate): reading the seat's file
|
|
581
589
|
// as a SUBMISSION, preserving it, refereeing whose bytes it held (the isDriverOwnBytes machinery) and
|
|
582
590
|
// rewriting the seat-facing copy. The seat writes no file, so there is no submission to read, no bytes
|
|
583
591
|
// to referee, and no seat-facing copy to keep in step. The capture duty moved to the receiver
|
package/driver/hit-list.mjs
CHANGED
|
@@ -178,7 +178,7 @@ export function slimLine(rec, reading = null) {
|
|
|
178
178
|
return line;
|
|
179
179
|
}
|
|
180
180
|
|
|
181
|
-
// ── CROWDS RIDE THE LIST AS A SIBLING ARRAY (Option A, ruled
|
|
181
|
+
// ── CROWDS RIDE THE LIST AS A SIBLING ARRAY (Option A, ruled 2026-08-31) ──────────────────────────
|
|
182
182
|
//
|
|
183
183
|
// WHY THEY HAVE TO RIDE AT ALL. The locked body's acceptance 4 is that no stage reads the fat band or
|
|
184
184
|
// an unpicked record. `band_lookup` answers from the list — but a lookup into a zone that was CROWDED
|
package/driver/jx-lanes.mjs
CHANGED
|
@@ -256,7 +256,7 @@ export { romanizationRefusal, romanizationSpellings };
|
|
|
256
256
|
// The jx model calls (fold completions, serp-judge, nativeread) are the ONLY dispatches in the driver
|
|
257
257
|
// that produce a per-token provider invoice. This comment used to name their executor as a
|
|
258
258
|
// `@anthropic-ai/sdk` Messages call; that has not been true since the lane lost its own destination
|
|
259
|
-
// and credential, and the SDK is no longer a dependency of this product at all
|
|
259
|
+
// and credential, and the SDK is no longer a dependency of this product at all.
|
|
260
260
|
// The lane goes wherever the run's resolved program goes — the CLI opens every connection, and no
|
|
261
261
|
// driver code calls a provider — so one run never mixes a subscription login with an API key. What
|
|
262
262
|
// survives unchanged is the BILLING distinction this block is about: these calls land on a per-token
|
package/driver/jx.mjs
CHANGED
|
@@ -164,7 +164,7 @@ export function deriveJxSliceStatement({ sidecar, units = null, env = process.en
|
|
|
164
164
|
// A reader concludes the deep lane ran. It does not follow: a profile set to `full` on an unarmed
|
|
165
165
|
// deployment and one set to `candidates` on an armed deployment execute identically.
|
|
166
166
|
//
|
|
167
|
-
//
|
|
167
|
+
// RULING (2026-08-17): flag, do not gate. "If we can't run deep dive on serpAPI we need to flag
|
|
168
168
|
// it." So `depth` is not deleted and it does not start gating anything — the run derives what the lane
|
|
169
169
|
// ACTUALLY got from the same durable record `executes` already comes from, and says so beside the ask.
|
|
170
170
|
// Gating would change what a run executes and therefore what it bills; a flag changes only what the
|
|
@@ -164,7 +164,7 @@ const DECLARED = Object.freeze({
|
|
|
164
164
|
|
|
165
165
|
/**
|
|
166
166
|
* Refuse an undeclared key by path. THE SHARED IMPLEMENTATION (preserve-merge.mjs), which six transports
|
|
167
|
-
* now use — reused rather than rebuilt, per this issue's own
|
|
167
|
+
* now use — reused rather than rebuilt, per this issue's own ruling.
|
|
168
168
|
*
|
|
169
169
|
* THE HAND-ROLLED FIRST CUT OF THIS WAS A PRODUCT-FAILURE RISK, and the shared module is why it did not
|
|
170
170
|
* ship. Mine refused undeclared keys at the TOP LEVEL as well as inside typed sub-objects. The shared one
|