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
package/driver/ask-ledger.mjs
CHANGED
|
@@ -118,7 +118,7 @@ export function returnedPhrase(info) {
|
|
|
118
118
|
}
|
|
119
119
|
|
|
120
120
|
/**
|
|
121
|
-
* — MAY AN EXECUTED RECALL PROBE DISCHARGE ON ITS OWN EXECUTION?
|
|
121
|
+
* — MAY AN EXECUTED RECALL PROBE DISCHARGE ON ITS OWN EXECUTION? Ruling 2026-08-19: "no".
|
|
122
122
|
*
|
|
123
123
|
* A recall ask discharges only when what came back is DEALT WITH. `kind:executed` alone discharges
|
|
124
124
|
* nothing — and the failure it hid is the one this issue was filed on: a HIGH-graded live US
|
|
@@ -597,7 +597,7 @@ export function deliveredMarks(findingsJsonText) {
|
|
|
597
597
|
const normMark = (s) => String(s ?? "").toLowerCase().replace(/[^a-z0-9]/g, "");
|
|
598
598
|
|
|
599
599
|
/**
|
|
600
|
-
*
|
|
600
|
+
* Ruling 2026-09-07, Option A, worded ABOUT THE MARK: a recall ask may close
|
|
601
601
|
* as immaterial only when the mark it went looking for is in the client's findings. Otherwise the mark
|
|
602
602
|
* is owed a finding and the ask stays open.
|
|
603
603
|
*
|
package/driver/cancel.mjs
CHANGED
|
@@ -56,6 +56,33 @@ export function requestCancel(runDir, { via = "unknown", by = undefined } = {})
|
|
|
56
56
|
return rec;
|
|
57
57
|
}
|
|
58
58
|
|
|
59
|
+
/**
|
|
60
|
+
* THE LAST READ BEFORE A REPORT IS PUBLISHED, on both lanes.
|
|
61
|
+
*
|
|
62
|
+
* The gateway's read is inside the attempt loop, before a turn is dispatched, and a dispatched turn
|
|
63
|
+
* always finishes. So the flag is consulted when a stage is about to spend and at no other moment —
|
|
64
|
+
* which means a stop pressed during the FINAL stage has no later boundary to be seen at, and the run
|
|
65
|
+
* publishes. Measured 2026-09-09 on a knockout: the stop was recorded, the last stage's turn ran to
|
|
66
|
+
* completion, and a full report was delivered 100 seconds later. Both the dialog and the API had told
|
|
67
|
+
* the operator twice that nothing would be delivered.
|
|
68
|
+
*
|
|
69
|
+
* ONE DEFINITION FOR BOTH LANES, because the promise is made in one set of words to every operator and
|
|
70
|
+
* two readings of "past the point of stopping" is how one lane keeps it and the other does not.
|
|
71
|
+
*
|
|
72
|
+
* IT GOES AFTER AN ALREADY-PUBLISHED SHORT-CIRCUIT, NEVER BEFORE. A run whose report is already out has
|
|
73
|
+
* delivered; refusing at that point would mark a delivered run cancelled and tell the operator the
|
|
74
|
+
* opposite of what happened, which is the same defect pointed the other way.
|
|
75
|
+
*
|
|
76
|
+
* This does not make boundary mode able to stop everything — a turn in flight still finishes and its
|
|
77
|
+
* spend is still spent. It makes the one promise the copy actually makes true: that nothing is
|
|
78
|
+
* delivered.
|
|
79
|
+
*/
|
|
80
|
+
export function assertNotCancelledBeforePublish(runDir, lane = "run") {
|
|
81
|
+
if (!isCancelled(runDir)) return null;
|
|
82
|
+
const rec = readCancel(runDir);
|
|
83
|
+
throw new RunCancelled(`publish (${lane})`, rec);
|
|
84
|
+
}
|
|
85
|
+
|
|
59
86
|
/**
|
|
60
87
|
* Thrown by the gateway when a run is asked to stop, and caught by both pipelines' run-level handlers.
|
|
61
88
|
*
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
// A FIX THAT MAKES THE GAP QUIETER WOULD BE WORSE THAN THE DEFECT. The report never claimed there was no
|
|
22
22
|
// adverse case law; every surface disclosed the gap. That discipline is untouched here — this changes
|
|
23
23
|
// the REASON attached to an honest gap, never whether the gap is stated.
|
|
24
|
-
// ── AND A SOURCE NOBODY COULD CHECK IS NOT A SOURCE WE DO NOT HAVE
|
|
24
|
+
// ── AND A SOURCE NOBODY COULD CHECK IS NOT A SOURCE WE DO NOT HAVE ──────────────────────────────────
|
|
25
25
|
//
|
|
26
26
|
// The credential reader has four states — absent, usable, unusable, unreadable — and the caller used to
|
|
27
27
|
// flatten them into one boolean before this composer saw them. Three of those states then shared one
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
// ground truth. A deployment with a `chmod 000` on one file would tell a client it does not have US
|
|
31
31
|
// federal case law. Nobody knows that. We know only that we could not look.
|
|
32
32
|
//
|
|
33
|
-
//
|
|
33
|
+
// RULING, 2026-09-05, on the three wordings put to him: report it as a source whose availability
|
|
34
34
|
// COULD NOT BE CONFIRMED — never as one this deployment does not have, and never as an outage — and stop
|
|
35
35
|
// there. Deliberately it does not say whether to treat the coverage as available: that is a second
|
|
36
36
|
// question, it applies to every product rather than this one source, and settling it as a side effect of
|
|
@@ -46,7 +46,7 @@
|
|
|
46
46
|
*
|
|
47
47
|
* `pipeline.mjs` built this shape inline and an arm rebuilt it by hand to drive the composer. Two copies
|
|
48
48
|
* of one mapping, and the arm's copy could not fail when the real one changed: adding `checked` here for
|
|
49
|
-
*
|
|
49
|
+
* The repair left the arm still composing the old three-field row and still passing. A test that
|
|
50
50
|
* restates the code it is checking agrees with itself.
|
|
51
51
|
*
|
|
52
52
|
* It is exported for that reason and not as a convenience: the arm and the pipeline must read the same
|
|
@@ -0,0 +1,261 @@
|
|
|
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
|
+
// company-bundle.mjs — what a valid company bundle is, and what the roster will accept.
|
|
4
|
+
//
|
|
5
|
+
// ── EXTRACTED FROM bin/brandowner.mjs WHEN THE SECOND DOOR ARRIVED ─────────────────────────────────
|
|
6
|
+
//
|
|
7
|
+
// The command line was the only way to create a company, so this logic lived beside it. The browser is
|
|
8
|
+
// now the second door, and the alternative to moving it was a second opinion about what a valid company
|
|
9
|
+
// is — which is the thing this codebase keeps paying to remove. There is one definition and both doors
|
|
10
|
+
// ask it.
|
|
11
|
+
//
|
|
12
|
+
// It lives here rather than in shared/ because every question below is answered by driver code — the
|
|
13
|
+
// profile validator, the framework loader, the skill-path resolver — and shared/ does not depend on
|
|
14
|
+
// driver/. And it lives outside bin/ for the reason shared/onboarding-store.mjs already states: a bin
|
|
15
|
+
// entry importing another bin entry is a dependency between two things a user types.
|
|
16
|
+
//
|
|
17
|
+
// ── WHAT IS HERE AND WHAT IS NOT ──────────────────────────────────────────────────────────────────
|
|
18
|
+
//
|
|
19
|
+
// Everything here is filesystem-and-roster pure: it composes a bundle and says whether the roster would
|
|
20
|
+
// accept it. What differs between the two doors stays with each — where the store is, how a context pack
|
|
21
|
+
// is read, who the author is (the command reads the OS username, the service takes it from the verified
|
|
22
|
+
// sign-in), and how the write is recorded.
|
|
23
|
+
//
|
|
24
|
+
// The refusal that matters most is the domain collision. The roster loader throws for the WHOLE roster
|
|
25
|
+
// when two companies claim one domain, so a bad create does not fail one company — it stops the
|
|
26
|
+
// deployment resolving ANY of them at the next start. That check is not reachable from the per-profile
|
|
27
|
+
// validator, so it has to be made here, against the proposed roster, before anything is written.
|
|
28
|
+
|
|
29
|
+
import { existsSync } from "node:fs";
|
|
30
|
+
import { join } from "node:path";
|
|
31
|
+
|
|
32
|
+
import { validateProfileEdit } from "./profiles.mjs";
|
|
33
|
+
import { DEFAULT_FRAMEWORK, loadFrameworkManifest } from "./framework.mjs";
|
|
34
|
+
import { config } from "./driver.config.mjs";
|
|
35
|
+
import { Refusal } from "../shared/onboarding-store.mjs";
|
|
36
|
+
|
|
37
|
+
// ── the framework, which is the point of the setup ─────────────────────────────────────────────────
|
|
38
|
+
/**
|
|
39
|
+
* Resolve the framework this brand owner will be rated under, as the ruling words it: the client's own
|
|
40
|
+
* when supplied, the Generic default otherwise, and always stated.
|
|
41
|
+
*
|
|
42
|
+
* ABSENT AND BROKEN ARE NOT THE SAME EVENT and the ruling separates them deliberately. Absent means the
|
|
43
|
+
* client has not given us their framework yet — onboarding proceeds under the Generic default, named.
|
|
44
|
+
* Broken means someone TRIED to select one and it does not resolve, and falling back there would rate a
|
|
45
|
+
* client's matters under a framework nobody chose while the output said everything was fine.
|
|
46
|
+
*
|
|
47
|
+
* The profile validator checks the SHAPE of this string only (`skills/prelim-search/<file>.md`, no
|
|
48
|
+
* escape) and never whether the file is there — so a shape-valid path to a document that does not exist
|
|
49
|
+
* validates cleanly and fails at rating time, which is the wrong place to find out.
|
|
50
|
+
*/
|
|
51
|
+
export function resolveFramework(requested, {
|
|
52
|
+
resolveSkill = (rel) => config.resolveSkillPath(rel),
|
|
53
|
+
loadManifest = loadFrameworkManifest,
|
|
54
|
+
} = {}) {
|
|
55
|
+
if (requested == null || String(requested).trim() === "")
|
|
56
|
+
return { path: DEFAULT_FRAMEWORK, source: "default" };
|
|
57
|
+
|
|
58
|
+
const path = String(requested).trim();
|
|
59
|
+
// Shape first, and by the profile validator's own rule rather than a second copy of it: a path that
|
|
60
|
+
// would be refused at load must be refused here, in the same words, before anything is written.
|
|
61
|
+
if (!/^skills\/prelim-search\/[^/]+\.md$/.test(path) || path.includes(".."))
|
|
62
|
+
throw new Refusal(
|
|
63
|
+
`--framework must name a document of the form "skills/prelim-search/<file>.md" (got ${JSON.stringify(path)}). `
|
|
64
|
+
+ `A profile selects a SHIPPED framework, never an arbitrary path.`);
|
|
65
|
+
|
|
66
|
+
// RESOLVED THE WAY THE RATING STAGE RESOLVES IT, never by joining the repo root. `skills/...` paths
|
|
67
|
+
// are relative to the DRIVER's skills directory, and a deployment may serve them from a doctrine
|
|
68
|
+
// overlay (CLEAROTRON_INSTRUCTIONS_DIR) instead. Joining the repo root would have looked in a
|
|
69
|
+
// directory that does not exist, refused every valid framework on every install, and — worse in the
|
|
70
|
+
// other direction — been blind to the overlay a deployment actually reads.
|
|
71
|
+
const deck = resolveSkill(path);
|
|
72
|
+
if (!existsSync(deck))
|
|
73
|
+
throw new Refusal(
|
|
74
|
+
`--framework names ${path}, and there is no such document on this install (resolved to ${deck}). `
|
|
75
|
+
+ `Refusing rather than rating this brand owner's matters under the Generic default: a framework `
|
|
76
|
+
+ `somebody chose and that does not resolve is a mistake, not an absence.`,
|
|
77
|
+
{ code: "framework_missing", path });
|
|
78
|
+
|
|
79
|
+
// The manifest is DERIVED from the deck path, never a separate knob — and it is what the validators,
|
|
80
|
+
// the renderer and the profile page read to know the framework's band vocabulary. A deck whose
|
|
81
|
+
// manifest will not load produces a customer whose page cannot state their own ladder.
|
|
82
|
+
//
|
|
83
|
+
// ASKED, NOT RE-CHECKED. An `existsSync` on the manifest here would be a second opinion about the
|
|
84
|
+
// same fact: `loadFrameworkManifest` already refuses a missing sidecar by name
|
|
85
|
+
// (`framework_manifest_missing:<path>`) and is the exact read the rating stage makes. Two checks
|
|
86
|
+
// means two messages to keep in step, and the one this file could write would be the one that goes
|
|
87
|
+
// stale.
|
|
88
|
+
let manifest;
|
|
89
|
+
try {
|
|
90
|
+
manifest = loadManifest(resolveSkill, path);
|
|
91
|
+
} catch (e) {
|
|
92
|
+
throw new Refusal(
|
|
93
|
+
`--framework names ${path}, and the framework will not load: ${String(e?.message ?? e)}. `
|
|
94
|
+
+ `Refusing rather than falling back: a framework somebody chose and that does not resolve is a `
|
|
95
|
+
+ `mistake, not an absence.`);
|
|
96
|
+
}
|
|
97
|
+
return { path, source: "supplied", manifest };
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
// ── the profile this writes ────────────────────────────────────────────────────────────────────────
|
|
101
|
+
/**
|
|
102
|
+
* Which marketplaces this brand owner's searches cover — supplied, or the Generic default, SAID OUT LOUD.
|
|
103
|
+
*
|
|
104
|
+
* A customer bundle is a COMPLETE document in this design, not an overlay on generic: every shipped
|
|
105
|
+
* profile carries its own `platforms`, and the loader requires a non-empty array on every file. The
|
|
106
|
+
* command had no way to supply one and set none, so every bundle it wrote failed to load on this field
|
|
107
|
+
* as well as on the `key` field above — two independent invalidities, and onboarding could not produce
|
|
108
|
+
* a loadable brand owner at all.
|
|
109
|
+
*
|
|
110
|
+
* Defaulting rather than refusing, and naming it, is this command's own established idiom: the same
|
|
111
|
+
* ruling governs the framework one function down. Which marketplaces a client's clearance searches is
|
|
112
|
+
* not a detail to decide silently, so an operator who supplies nothing is TOLD what they got and can
|
|
113
|
+
* refine it in the portal.
|
|
114
|
+
*/
|
|
115
|
+
export function resolvePlatforms(supplied, roster) {
|
|
116
|
+
if (supplied?.length) return { platforms: supplied, source: "supplied" };
|
|
117
|
+
const house = roster?.get?.("generic")?.platforms ?? [];
|
|
118
|
+
if (!house.length)
|
|
119
|
+
throw new Refusal(
|
|
120
|
+
"no --platforms was given and the Generic default carries none, so there is nothing to onboard this "
|
|
121
|
+
+ "brand owner with. Pass --platforms, or repair the generic profile in the store.",
|
|
122
|
+
{ code: "no_marketplaces" });
|
|
123
|
+
return { platforms: [...house], source: "house default" };
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
export function buildProfile({ key, name, domains, platforms, framework, industry,
|
|
127
|
+
tradingNames, classes, territories }) {
|
|
128
|
+
// NO `key` IN THE DOCUMENT. The loader derives it from the FILENAME and injects it — readProfilesLayer
|
|
129
|
+
// composes `{ key, ...p }` — so a `key` written here is redundant on the way in and fatal on the way
|
|
130
|
+
// out: it is not in KNOWN_PROFILE_KEYS, and the deny-unknown-key gate hard-fails the whole roster over
|
|
131
|
+
// it. Every bundle this command wrote carried one, so the first successful onboarding made the store
|
|
132
|
+
// unloadable and the next command to read profiles threw. The parameter stays — the filename and the
|
|
133
|
+
// roster checks are addressed by key — it simply does not travel into the file.
|
|
134
|
+
const profile = { name, platforms };
|
|
135
|
+
if (domains?.length) profile.matchDomains = domains;
|
|
136
|
+
if (industry) profile.industry = industry;
|
|
137
|
+
// THE OPTIONAL THREE, OMITTED WHEN EMPTY RATHER THAN WRITTEN AS `[]`. An empty array is a real
|
|
138
|
+
// instruction to the reader of a profile, not the absence of one, so writing it for a field nobody
|
|
139
|
+
// filled in states something nobody said. The whole create path treats absent and empty as different
|
|
140
|
+
// and this is where that has to hold.
|
|
141
|
+
if (tradingNames?.length) profile.selfExclusionOwners = tradingNames;
|
|
142
|
+
if (classes?.length) profile.defaultClasses = classes;
|
|
143
|
+
if (territories?.length) profile.defaultJurisdictions = territories;
|
|
144
|
+
// ALWAYS SET, per the ruling. `frameworkFor` would fall back to the same value if this were absent —
|
|
145
|
+
// but "the tool sets it" is the point of the setup, and an explicit selection is what makes the
|
|
146
|
+
// receipt below mean anything.
|
|
147
|
+
profile.frameworkPath = framework.path;
|
|
148
|
+
return profile;
|
|
149
|
+
}
|
|
150
|
+
|
|
151
|
+
// ── the proposed roster, validated whole ───────────────────────────────────────────────────────────
|
|
152
|
+
/**
|
|
153
|
+
* Validate the candidate the way the deployment will read it: as one member of the roster, not alone.
|
|
154
|
+
*
|
|
155
|
+
* `loadProfiles` composes every profile in the store and throws on the FIRST cross-profile conflict it
|
|
156
|
+
* finds — a domain claimed twice, most of all. That throw is not scoped to the offending file: it stops
|
|
157
|
+
* the roster loading at all. So the only honest check is over the proposed state, which is what
|
|
158
|
+
* bin/grant.mjs does with `accessView` for exactly the same reason.
|
|
159
|
+
*/
|
|
160
|
+
/**
|
|
161
|
+
* The roster as it stands, where AN EMPTY STORE IS AN EMPTY ROSTER — F42.
|
|
162
|
+
*
|
|
163
|
+
* `loadProfiles` with an explicit `dir` reads that directory ALONE and refuses a store with no
|
|
164
|
+
* `generic.json`, which is right for what that refusal is for: `generic` is the universal fallback every
|
|
165
|
+
* unprofiled job resolves to, and a RUN against a store without it would silently reprofile a client.
|
|
166
|
+
* The explicit-dir form deliberately has no fall-through, because the fixtures that build a roster
|
|
167
|
+
* assert on precisely that roster and layering the bundled set underneath would widen three set-level
|
|
168
|
+
* guards until none still tested its own name.
|
|
169
|
+
*
|
|
170
|
+
* BUT THIS COMMAND IS NOT RUNNING A CLEARANCE. It reads the store to answer one question — does this key
|
|
171
|
+
* or one of its domains already exist — and on a fresh install the honest answer is "no, there is
|
|
172
|
+
* nothing here yet". Instead it stack-traced on the FIRST day-one command a new operator types, with a
|
|
173
|
+
* refusal about a file they had never heard of and did not need: the runtime resolves `generic` from the
|
|
174
|
+
* product's bundled set through the OVERLAY path, so a deployment store never needs its own copy.
|
|
175
|
+
*
|
|
176
|
+
* So the missing-generic refusal is caught BY NAME and answered as the empty roster it describes. Caught
|
|
177
|
+
* by name rather than broadly, because every other thing that loader throws — an overlapping domain, an
|
|
178
|
+
* unknown key, an unreadable store — is a real refusal this command must still relay.
|
|
179
|
+
*/
|
|
180
|
+
/**
|
|
181
|
+
* The key a company gets from its name — the filename, and the key every screen and route uses.
|
|
182
|
+
*
|
|
183
|
+
* ONE SPELLING FOR BOTH DOORS. The command line takes the key as an argument and the browser derives it
|
|
184
|
+
* from the name, so without this the two doors would file "Acme Ltd" under different keys depending on
|
|
185
|
+
* which one you used, and neither would be wrong.
|
|
186
|
+
*
|
|
187
|
+
* Capped well under the validator's 39 characters and stripped of leading and trailing hyphens, so a
|
|
188
|
+
* name made entirely of punctuation produces nothing rather than a key of dashes — the caller then
|
|
189
|
+
* refuses and asks for the key, which is honest, where inventing one would file a company under a name
|
|
190
|
+
* nobody chose.
|
|
191
|
+
*/
|
|
192
|
+
export function companyKeyFrom(name) {
|
|
193
|
+
return String(name ?? "")
|
|
194
|
+
.toLowerCase()
|
|
195
|
+
.normalize("NFKD")
|
|
196
|
+
// COMBINING MARKS ARE DROPPED, NOT SEPARATED. NFKD splits "ü" into "u" + a combining diaeresis, and
|
|
197
|
+
// the class below would turn that mark into a hyphen — so "Zürich Präzision" filed as
|
|
198
|
+
// "zu-rich-pra-zision". Accented company names are the normal case in the languages this product
|
|
199
|
+
// works in, not an edge.
|
|
200
|
+
.replace(/[\u0300-\u036f]/g, "")
|
|
201
|
+
.replace(/[^a-z0-9]+/g, "-")
|
|
202
|
+
.replace(/^-+|-+$/g, "")
|
|
203
|
+
.slice(0, 39)
|
|
204
|
+
.replace(/-+$/, "");
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
export function rosterAsItStands(store, loadProfiles) {
|
|
208
|
+
try { return loadProfiles({ dir: store, force: true }); }
|
|
209
|
+
catch (e) {
|
|
210
|
+
if (/generic\.json is REQUIRED/.test(String(e?.message ?? ""))) return new Map();
|
|
211
|
+
throw e;
|
|
212
|
+
}
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
export function assertRosterAccepts({ store, key, profile, loadProfiles }) {
|
|
216
|
+
// THE LOADER'S OWN VALIDATOR, over the CANDIDATE — the discipline `project add` already applies to an
|
|
217
|
+
// overlay, in the same words, for the same reason: whatever the tree would refuse at load is refused
|
|
218
|
+
// here, before anything is written. Everything below this line reads the store AS IT STANDS, which is
|
|
219
|
+
// why none of it could ever see a bad field in the file about to be added: the candidate never met the
|
|
220
|
+
// loader until the next command did, and by then the write had landed.
|
|
221
|
+
// `generic` IS RESERVED AND THE KEY RULE DOES NOT SAY SO. `PROFILE_KEY_RE` matches it, while the
|
|
222
|
+
// comment above that regex claims the create path never allows it — so the rule was a sentence rather
|
|
223
|
+
// than a check. On a store with no `generic.json` of its own, creating one here writes a file that
|
|
224
|
+
// shadows the bundled universal fallback for every job that names no company, which is silent and
|
|
225
|
+
// affects every clearance rather than one. Refused at the gate both doors pass through.
|
|
226
|
+
if (String(key).toLowerCase() === "generic")
|
|
227
|
+
throw new Refusal(
|
|
228
|
+
`"generic" is the house default every unprofiled clearance falls back to, so it cannot be created `
|
|
229
|
+
+ `as a company. Nothing has been written. Pick another key.`);
|
|
230
|
+
|
|
231
|
+
const v = validateProfileEdit(key, profile);
|
|
232
|
+
if (!v.ok)
|
|
233
|
+
throw new Refusal(`the company bundle is not valid, so nothing was written:\n ${v.errors.join("\n ")}`,
|
|
234
|
+
{ code: "invalid_bundle", errors: v.errors });
|
|
235
|
+
|
|
236
|
+
const existing = rosterAsItStands(store, loadProfiles);
|
|
237
|
+
if (existing.has(key))
|
|
238
|
+
throw new Refusal(
|
|
239
|
+
`a brand owner "${key}" already exists in ${store}. This command creates; it does not overwrite an `
|
|
240
|
+
+ `existing bundle. Edit it in the portal, or remove the file deliberately first.`,
|
|
241
|
+
{ code: "key_exists", key });
|
|
242
|
+
|
|
243
|
+
for (const d of profile.matchDomains ?? []) {
|
|
244
|
+
const dl = String(d).toLowerCase();
|
|
245
|
+
for (const [otherKey, other] of existing) {
|
|
246
|
+
if ((other.matchDomains ?? []).some((o) => String(o).toLowerCase() === dl))
|
|
247
|
+
throw new Refusal(
|
|
248
|
+
`domain "${dl}" is already claimed by the brand owner "${otherKey}". Two owners claiming one `
|
|
249
|
+
+ `domain makes the WHOLE roster refuse to load on the next start — not just this bundle — so `
|
|
250
|
+
+ `nothing has been written.`,
|
|
251
|
+
{ code: "domain_claimed", domain: dl, heldBy: otherKey });
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
}
|
|
255
|
+
|
|
256
|
+
|
|
257
|
+
// ── argument parsing ───────────────────────────────────────────────────────────────────────────────
|
|
258
|
+
// Long flags only, and an unknown one REFUSES. A permissive parser on a command that writes into the
|
|
259
|
+
// customers directory is how a typo'd flag becomes a silently missing field — `--fraemwork` would
|
|
260
|
+
// otherwise onboard a client under the Generic default while the operator believed they had set theirs.
|
|
261
|
+
const FLAGS = ["--name", "--domains", "--platforms", "--framework", "--industry", "--context"];
|
package/driver/compare.mjs
CHANGED
|
@@ -95,7 +95,7 @@ export function refuseToCompare(a = {}, b = {}) {
|
|
|
95
95
|
const modelOf = (x) => String(x?.modelUsed ?? x?.model ?? "").trim();
|
|
96
96
|
if (modelOf(a) !== modelOf(b)) return null;
|
|
97
97
|
return "REFUSING TO COMPARE — neither record names an engine and both name the same model "
|
|
98
|
-
+ `(${modelOf(a) || "none"}), so there is nothing here that tells these two apart
|
|
98
|
+
+ `(${modelOf(a) || "none"}), so there is nothing here that tells these two apart. `
|
|
99
99
|
+ "A model id resolved from a tier is not the answer: it maps through the Anthropic table whichever "
|
|
100
100
|
+ "engine ran, so two arms on different engines read identically.\n"
|
|
101
101
|
+ " The per-attempt `_driver/<stage>.jsonl` records `engine` and `modelUsed` — compare from there.";
|
package/driver/compose-read.mjs
CHANGED
|
@@ -266,7 +266,7 @@ export function makeReadBudget({ perHour = 20 } = {}) {
|
|
|
266
266
|
*
|
|
267
267
|
* It used to say the client was built from `@anthropic-ai/sdk`, in the present tense, long after the
|
|
268
268
|
* change recorded below moved this route through the engine door. The SDK has now been removed from
|
|
269
|
-
* the manifests entirely
|
|
269
|
+
* the manifests entirely — nothing in the tree imports it.
|
|
270
270
|
*/
|
|
271
271
|
/**
|
|
272
272
|
* Every way a parsed payload can fail to be a read, named.
|
|
@@ -336,7 +336,7 @@ export function makeComposeReader({ turn, now = () => new Date() } = {}) {
|
|
|
336
336
|
message: `That is ${text.length.toLocaleString("en-GB")} characters — paste up to ${MAX_BRIEF.toLocaleString("en-GB")}, or set the search up below.`,
|
|
337
337
|
};
|
|
338
338
|
}
|
|
339
|
-
// NO `thinking` BLOCK, DELIBERATELY.
|
|
339
|
+
// NO `thinking` BLOCK, DELIBERATELY. Ruling 2026-07-22: sonnet, thinking off. This is
|
|
340
340
|
// extraction, not judgment — the brief already contains every fact the answer needs, and the
|
|
341
341
|
// reasoning that matters (is this mark clear?) happens later, in the run, where it is paid for.
|
|
342
342
|
// Off also keeps the button feeling like a button.
|
|
@@ -279,7 +279,7 @@ const CASELAW_SITES = Object.freeze({
|
|
|
279
279
|
* a byte of it.
|
|
280
280
|
*/
|
|
281
281
|
/**
|
|
282
|
-
* Is this credential file something the bridge could actually use?
|
|
282
|
+
* Is this credential file something the bridge could actually use?
|
|
283
283
|
*
|
|
284
284
|
* `existsSync` was the whole test, and a zero-byte file therefore reported as an enrolled case-law
|
|
285
285
|
* source. That is not merely an operator-facing inaccuracy: `driver/case-law-sources.mjs` hands the
|
|
@@ -347,7 +347,7 @@ export function caseLawInventory(env = process.env) {
|
|
|
347
347
|
// in to from one that is simply part of the build.
|
|
348
348
|
enrolment: "oauth",
|
|
349
349
|
configured,
|
|
350
|
-
// THE THIRD STATE, WHICH `configured` CANNOT HOLD
|
|
350
|
+
// THE THIRD STATE, WHICH `configured` CANNOT HOLD. `configured` is a boolean
|
|
351
351
|
// and the world has three cases in it: never enrolled, enrolled and usable, enrolled and NOT
|
|
352
352
|
// usable. Collapsing the third into either of the others is the defect — into the first it
|
|
353
353
|
// reads as "not set up" and hides a credential the operator believes in; into the second it
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
// each, from a closed enum, plus the validator tokens that speak about that element.
|
|
12
12
|
// E2 declarations ∪ validator vocabulary is a closed partition, in two arms of different strength:
|
|
13
13
|
// arm 1 a token no element accounts for → hard red, now
|
|
14
|
-
// arm 2 an element no token speaks about → a per-stage RATCHET (
|
|
14
|
+
// arm 2 an element no token speaks about → a per-stage RATCHET (ruling 2026-08-13)
|
|
15
15
|
// E3 structure is returned, never emitted as text — with a named, shrinking backlog for the sites
|
|
16
16
|
// that exist today, because a lint that greenlights every existing hole certifies the problem.
|
|
17
17
|
|
|
@@ -195,7 +195,7 @@ export const E3_BACKLOG = [
|
|
|
195
195
|
// quote — M2 LANDED. The live field is `anchor` (prelim-common-law/SKILL.md:197).
|
|
196
196
|
// Neither was reworded. Both were removed, and this row is discharged of them.
|
|
197
197
|
//
|
|
198
|
-
// ── (b) THE NEW, NARROWER CLAIM — RE-EVIDENCED for B (the form path is DELETED,
|
|
198
|
+
// ── (b) THE NEW, NARROWER CLAIM — RE-EVIDENCED for B (the form path is DELETED, ruling
|
|
199
199
|
// 2026-08-17). The seat is no longer told to open any file: the surviving position-dictation now
|
|
200
200
|
// orders a `record_dispositions` CALL carrying `row_index` / `receipt_index` / `ruling` / `note` per
|
|
201
201
|
// row, and the receiver resolves BOTH positions — made the row an ordinal too, on the finding
|
|
@@ -82,7 +82,7 @@ export const VOCABULARY = [
|
|
|
82
82
|
{ token: "half_path_unrecognized", stages: ["common-law-half"], site: "driver/verify.mjs:395" },
|
|
83
83
|
{ token: "no_coverage_status_row", stages: [...CL, "register-digest"], site: "driver/verify.mjs:452, 1416" },
|
|
84
84
|
// B — `connotation_form_unparseable` and `connotation_form_untouched` are GONE: they
|
|
85
|
-
// were states only a hand-authored document could be in, and the form path is deleted (
|
|
85
|
+
// were states only a hand-authored document could be in, and the form path is deleted (ruling
|
|
86
86
|
// 2026-08-17). Their subject matter is carried by the call tokens under the D1 family row below.
|
|
87
87
|
{ token: "connotation_form_damaged", stages: CL, site: "driver/verify.mjs:889" },
|
|
88
88
|
{ token: "connotation_", stages: CL, site: "driver/verify.mjs:898", family: "driver/connotation-search.mjs:894 (CONNOTATION_FORM_REASONS, exported at :819)", dynamic: "D1" },
|
|
@@ -210,7 +210,7 @@ export function contradictionFor(reason, row, scope) {
|
|
|
210
210
|
+ `own-right or duplicate-of-delivered.`;
|
|
211
211
|
}
|
|
212
212
|
|
|
213
|
-
// ── ECONOMY IS NOT A REASON ABOUT A LIVE IN-CLASS MARK (
|
|
213
|
+
// ── ECONOMY IS NOT A REASON ABOUT A LIVE IN-CLASS MARK (ruling 2026-09-07) ─────────────────
|
|
214
214
|
//
|
|
215
215
|
// `not-worth-the-line` means "it fits none of the four manageable categories and is not relevant
|
|
216
216
|
// enough to drive the read". That is a statement about the REPORT'S BUDGET, not about the record. On a
|
|
@@ -21,7 +21,7 @@
|
|
|
21
21
|
# the prelim-outbox.timer rescan owns the delivery at a sane cadence).
|
|
22
22
|
set -u
|
|
23
23
|
|
|
24
|
-
#
|
|
24
|
+
# this fallback chain MIRRORS driver.config.mjs (`outboxDir` = CLEAROTRON_OUTBOX_DIR, else
|
|
25
25
|
# <workspaceRoot>/prelim-outbox; `workspaceRoot` = CLEAROTRON_WORK_DIR, else $HOME/trademark/workspace).
|
|
26
26
|
# It is a second derivation of the same answer in a language that cannot import the first, so it moves
|
|
27
27
|
# whenever that one does. If they disagree, the driver writes markers where this script never looks and
|
|
@@ -37,7 +37,7 @@ DRAIN_WAIT="${CLEAROTRON_OUTBOX_DRAIN_WAIT:-180}"
|
|
|
37
37
|
WAKE_MODEL="${CLEAROTRON_OUTBOX_WAKE_MODEL-anthropic/claude-haiku-4-5}"
|
|
38
38
|
MSG="The trademark engine's outbox has pending events. Run the clearotron-deliver skill now: list_outbox_events, route each event (delivered → get_delivery_packet + send VERBATIM + mark_sent; every other kind → relay its text + ack_event)."
|
|
39
39
|
|
|
40
|
-
# THE WALL, RESOLVED ONCE AND NAMED WHEN IT IS ABSENT
|
|
40
|
+
# THE WALL, RESOLVED ONCE AND NAMED WHEN IT IS ABSENT.
|
|
41
41
|
#
|
|
42
42
|
# Every wake below runs under timeout(1) — GNU coreutils, not POSIX and not in a stock macOS or a
|
|
43
43
|
# slim container. Without it `timeout …` is simply a command that is not there: bash answers 127, and
|
|
@@ -165,7 +165,7 @@ done
|
|
|
165
165
|
# Bounded drain-wait after a successful wake: exit early once the courier consumed everything
|
|
166
166
|
# (mark_sent/ack_event), else fall through to the pacing sleep / path-unit re-fire. NEVER delete here.
|
|
167
167
|
#
|
|
168
|
-
# POLL FIRST, THEN WAIT
|
|
168
|
+
# POLL FIRST, THEN WAIT. `remaining` used to be assigned ONLY inside the loop body, so
|
|
169
169
|
# CLEAROTRON_OUTBOX_DRAIN_WAIT=0 — the value an operator sets to make delivery synchronous, which is to
|
|
170
170
|
# say the value they set while already chasing a late delivery — ran the body zero times and left the
|
|
171
171
|
# post-loop `${#remaining[@]}` reading an unset array. Under `set -u` that is an error: the echo never
|
package/driver/dev-portal.mjs
CHANGED
|
@@ -76,7 +76,7 @@ export const DEV_COCKPIT_JOB_FIELDS = Object.freeze({
|
|
|
76
76
|
notCarried: Object.freeze({
|
|
77
77
|
registerFixtures: "a run that reads canned register payloads instead of calling a register. The cockpit "
|
|
78
78
|
+ "is a form for composing REAL dev runs; a fixture run is declared by the job file that wants one, so "
|
|
79
|
-
+ "the fact travels with the run rather than with whoever filled in a form
|
|
79
|
+
+ "the fact travels with the run rather than with whoever filled in a form.",
|
|
80
80
|
promptParts: "the requester's declaration that the prose rides as SIDECAR files. The cockpit composes a job "
|
|
81
81
|
+ "from its own form and writes no sidecars, so it may not claim that shape (#1085).",
|
|
82
82
|
forwarderDomain: "the cockpit's assembler defaults it; a dev form has no forwarding domain to state.",
|
|
@@ -87,6 +87,8 @@ export const DEV_COCKPIT_JOB_FIELDS = Object.freeze({
|
|
|
87
87
|
projectKey: "spec-62 engagements are a customer-configuration surface; this form has no project picker "
|
|
88
88
|
+ "and inventing one here would let a dev job claim an overlay nobody chose.",
|
|
89
89
|
parentRunId: "escalation lineage. Nothing on this form can name a parent run.",
|
|
90
|
+
tenant: "which organisation's Generic a run is filed under. The portal and the connector stamp it from a "
|
|
91
|
+
+ "verified person; this dev form has no person to resolve it from, so a dev Generic run is filed under none.",
|
|
90
92
|
customerUnknown: "arms candidate-self classification. A dev form cannot honestly say the applicant was "
|
|
91
93
|
+ "neither stated nor implied — the Customer box above states it or leaves it blank.",
|
|
92
94
|
brief: "the intake confirmation brief, written by the email door's own gate. There is no such gate here.",
|
package/driver/digest-queue.mjs
CHANGED
|
@@ -108,7 +108,7 @@ export function markFlushed(queue, ids, flushedAt) {
|
|
|
108
108
|
// fired its own pass. An invariant with an undeclared violation is worse than a narrower invariant
|
|
109
109
|
// stated honestly, because the next reader trusts the wrong one — so the exemption is DECLARED here,
|
|
110
110
|
// with the reason, and an arm censuses every dispatch site against this list. Silence between an
|
|
111
|
-
// invariant and a violation is the defect
|
|
111
|
+
// invariant and a violation is the defect.
|
|
112
112
|
//
|
|
113
113
|
// WHY RECALL-RECONCILE CANNOT MINT — measured on the tree, not reasoned from the design:
|
|
114
114
|
//
|
|
@@ -400,7 +400,7 @@ export function recordDispositions(spec, received, { now = () => new Date().toIS
|
|
|
400
400
|
try {
|
|
401
401
|
mkdirSync(dirname(accum), { recursive: true });
|
|
402
402
|
// The accumulator in `_driver/` is the ONLY copy. The seat-facing mirror at `dispositions_path`
|
|
403
|
-
// died with the form path (
|
|
403
|
+
// died with the form path (ruling 2026-08-17, delete-not-gate): the seat never reads or
|
|
404
404
|
// writes it, the validator reads the accumulator, and a mirror nothing reads is a second writer
|
|
405
405
|
// waiting to drift. The path itself survives in the spec as the accumulator's name anchor.
|
|
406
406
|
writeFileSync(accum, json);
|
package/driver/doc-constants.mjs
CHANGED
|
@@ -279,7 +279,7 @@ export const DOC_CONSTANTS = [
|
|
|
279
279
|
doc: "INSTALL.md",
|
|
280
280
|
pattern: /# TRADEMARK_MCP_HTTP_PORT=(\d+)/,
|
|
281
281
|
},
|
|
282
|
-
// ── TWO BINDINGS REMOVED: THEIR DOCUMENTS ARE NOT ON THIS TREE
|
|
282
|
+
// ── TWO BINDINGS REMOVED: THEIR DOCUMENTS ARE NOT ON THIS TREE ───────────────────────────────────
|
|
283
283
|
//
|
|
284
284
|
// `.env.deployment.example` and `.env.prod.example` each carried a `TRADEMARK_MCP_HTTP_PORT default`
|
|
285
285
|
// binding. Both files are withheld from the public tree by the cut, so on this tree the bindings
|
package/driver/drain-posture.mjs
CHANGED
|
@@ -4,8 +4,8 @@
|
|
|
4
4
|
//
|
|
5
5
|
// ── WHY THIS IS A MODULE AND NOT A LINE IN EACH ARM ─────────────────────────────────────────────────
|
|
6
6
|
//
|
|
7
|
-
// Two arms of the same deploy check reached opposite conclusions about one box
|
|
8
|
-
//
|
|
7
|
+
// Two arms of the same deploy check reached opposite conclusions about one box, measured on
|
|
8
|
+
// `c6e183d`. The queue arm read the worker unit, found it enabled, and said the .path/timer
|
|
9
9
|
// posture it was written for is retired here. The drainer arm did not read anything, applied the
|
|
10
10
|
// timer-era rule, and called an absent drainer a fault. Both sentences shipped in the same report.
|
|
11
11
|
//
|
|
@@ -86,7 +86,7 @@ export const DRAINER_CMD = /\b(runner\.mjs|clearotron[^\s]*\s+start)\b/;
|
|
|
86
86
|
* @returns {{state: "pass"|"fail"|"warn", message: string}}
|
|
87
87
|
*/
|
|
88
88
|
export function drainerVerdict({ stamp, headCommit, isAlive, processes, ppidOf = null, posture = null }) {
|
|
89
|
-
// ── WHICH POSTURE IS THIS BOX IN, and say it out loud whatever the answer
|
|
89
|
+
// ── WHICH POSTURE IS THIS BOX IN, and say it out loud whatever the answer.
|
|
90
90
|
//
|
|
91
91
|
// Only ONE branch below changes state on it — a stamped drainer whose process is gone. That is the
|
|
92
92
|
// state the ruling is about: between ticks on a scheduled box, and nothing is executing runs on a
|