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
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
// values; what changes is that it arrives through the call instead of being authored as a file, which
|
|
15
15
|
// is what lets the joins below run at write time.
|
|
16
16
|
//
|
|
17
|
-
// ── WHAT THIS DOES *NOT* CONVERT, AND IT IS
|
|
17
|
+
// ── WHAT THIS DOES *NOT* CONVERT, AND IT IS A RULING, NOT AN OVERSIGHT ──────────────────────
|
|
18
18
|
//
|
|
19
19
|
// The per-finding write-ups are NOT rendered from `findings.json`. `stages.mjs`' contract element for
|
|
20
20
|
// them classes the second authoring `mechanical:code-rendered` and its `why` records ruling D3 on
|
|
@@ -465,7 +465,7 @@ export function driverReadsFor(runDir) {
|
|
|
465
465
|
* THE STATE THAT ENUMERATION MISSED is the corrective pass. `prepareDeclinationSpec` runs AGAIN before
|
|
466
466
|
* it, and the surface can have grown in between, so the corrective pass is ordered against a LARGER owed
|
|
467
467
|
* set than the main pass answered. If that call is refused, `rollbackCorrectivePass` restores the
|
|
468
|
-
* pre-corrective `findings.json` and — by T3b's
|
|
468
|
+
* pre-corrective `findings.json` and — by T3b's ruling, deliberately — THE RUN DELIVERS. The
|
|
469
469
|
* document that ships satisfied the OLD owed set; the records added since are unaccounted, and no
|
|
470
470
|
* acceptance boundary ever saw a call about them, because the only call that was ordered against them
|
|
471
471
|
* was the one that got refused.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GENERIC UNIT — safe to sync verbatim on deploy: no deployment-specific value lives here.
|
|
2
2
|
#
|
|
3
|
-
# ── INSTALLED WITH THE PRODUCT (
|
|
3
|
+
# ── INSTALLED WITH THE PRODUCT (ruling 2026-09-03, settled point 2) ───────────────────────────
|
|
4
4
|
#
|
|
5
5
|
# This is the CLIENT door: the only surface that accepts a key acting for a brand owner's account. It is
|
|
6
6
|
# a SEPARATE PROCESS from the engine door (clearotron-mcp-face.service), which refuses an account key
|
|
@@ -45,7 +45,7 @@ Type=simple
|
|
|
45
45
|
EnvironmentFile=%h/.env
|
|
46
46
|
Environment=PATH=%h/.local/bin:%h/.npm-global/bin:/usr/local/bin:/usr/bin:/bin
|
|
47
47
|
Environment=CLEAROTRON_NO_ENV_FILE=1
|
|
48
|
-
# ── NO PORT, NO HOST, NO ALLOW-LIST HERE
|
|
48
|
+
# ── NO PORT, NO HOST, NO ALLOW-LIST HERE ─────────────────────────────────────────────────────────────
|
|
49
49
|
#
|
|
50
50
|
# This unit used to carry three Environment= lines pinning the host, the port and the allow-list, and it
|
|
51
51
|
# was the ONLY one of the four that pinned anything a box can legitimately differ on. `Environment=` is
|
|
@@ -55,7 +55,7 @@ Environment=CLEAROTRON_NO_ENV_FILE=1
|
|
|
55
55
|
# It took a running door down on a shared box: two installs, two ports, one baked value, and the
|
|
56
56
|
# reinstall pointed the second install's door at the first's port. Worse, the allow-list travelled with
|
|
57
57
|
# it — pinned to 18811 whatever port the door actually bound — which is the `Invalid Host header`
|
|
58
|
-
# failure of
|
|
58
|
+
# failure of the missing session bus one layer down, produced by our own unit rather than by a tunnel.
|
|
59
59
|
#
|
|
60
60
|
# The values live in %h/.env, which BOTH install paths write through one authority (`enablePlan`), and
|
|
61
61
|
# the code's own defaults are the same 127.0.0.1 and 18811 these lines carried — so a box that has
|
|
@@ -15,9 +15,9 @@ Environment=CLEAROTRON_NO_ENV_FILE=1
|
|
|
15
15
|
|
|
16
16
|
# `clearotron update` is the whole mechanism, and it already owns both refusals:
|
|
17
17
|
#
|
|
18
|
-
# 1. it refuses over a configuration store kept INSIDE the checkout
|
|
18
|
+
# 1. it refuses over a configuration store kept INSIDE the checkout — an upgrade there is a
|
|
19
19
|
# merge conflict at best and a silent overwrite of the operator's own doctrine at worst;
|
|
20
|
-
# 2. it refuses while a run is queued, claimed or in flight
|
|
20
|
+
# 2. it refuses while a run is queued, claimed or in flight — `npm ci` rebuilds node_modules
|
|
21
21
|
# under whatever is running, and a clearance assembled from halves of two builds does not fail, it
|
|
22
22
|
# answers wrongly and says nothing.
|
|
23
23
|
#
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
# GENERIC UNIT — safe to sync verbatim on deploy: no deployment-specific value lives here. See
|
|
2
2
|
# clearotron-portal.service for why this unit exists and why it takes an EnvironmentFile rather than
|
|
3
|
-
# explicit Environment= lines (
|
|
3
|
+
# explicit Environment= lines (ruling 2026-08-26).
|
|
4
4
|
#
|
|
5
5
|
# This is the engine door the portal's Start button calls over real MCP. It is the OTHER of the two
|
|
6
6
|
# children bin/start.mjs supervises, and the other one that had no unit.
|
|
@@ -2,14 +2,14 @@
|
|
|
2
2
|
# listener and every hostname come from EnvironmentFile=%h/.env, which is where a SERVER's configuration
|
|
3
3
|
# lives (INSTALL.md §3; render-units.mjs defaults --env to the same file).
|
|
4
4
|
#
|
|
5
|
-
# ── WHY THIS UNIT EXISTS
|
|
5
|
+
# ── WHY THIS UNIT EXISTS ──────────────────────────────────────────────────────────────────────────────
|
|
6
6
|
#
|
|
7
7
|
# It did not, and that was the gap under the whole issue. `bin/start.mjs` supervises exactly two children
|
|
8
8
|
# — this process and the MCP face — and NOTHING ran either under systemd. So the two processes the door
|
|
9
9
|
# configures had no service to attach to, a hosted deployment kept them alive with a hand launcher, and
|
|
10
10
|
# `clearotron start`'s refusal had nothing to point at.
|
|
11
11
|
#
|
|
12
|
-
# ── ONE CONFIGURATION PER SERVER BOX (
|
|
12
|
+
# ── ONE CONFIGURATION PER SERVER BOX (ruling, 2026-08-26) ───────────────────────────────────────
|
|
13
13
|
#
|
|
14
14
|
# `EnvironmentFile=%h/.env`, like prelim-driver.service, and the CF Access audience lives there. The
|
|
15
15
|
# alternative — explicit `Environment=` lines carrying the audience, as profile-service.service used to —
|
|
@@ -21,7 +21,7 @@ Description=Clearotron portal — the UI, the API, the sign-in and the config su
|
|
|
21
21
|
Documentation=https://github.com/CordilleraSarl/Clearotron/blob/main/docs/architecture/02-architecture.md
|
|
22
22
|
After=network-online.target
|
|
23
23
|
Wants=network-online.target
|
|
24
|
-
# ── THE ENGINE DOOR FIRST, WHERE THERE IS ONE
|
|
24
|
+
# ── THE ENGINE DOOR FIRST, WHERE THERE IS ONE ─────────────────────────────────────────────────────────
|
|
25
25
|
#
|
|
26
26
|
# The units carried no ordering between them, so a simultaneous restart of the set started this process
|
|
27
27
|
# before the door it calls. Its boot probe then logged an outage — in the present tense, with "a
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
# queue and credential comes from EnvironmentFile=%h/.env, which is where a SERVER's configuration lives
|
|
6
6
|
# (INSTALL.md §3; render-units.mjs defaults --env to the same file).
|
|
7
7
|
#
|
|
8
|
-
# ── WHY THIS UNIT EXISTS
|
|
8
|
+
# ── WHY THIS UNIT EXISTS ─────────────────────────────────────────────────────────────────────────────
|
|
9
9
|
#
|
|
10
10
|
# It did not, and its absence was the one that cost a report rather than a restart. `bin/start.mjs`
|
|
11
11
|
# supervises THREE children — the portal, the engine door, and this worker (`:904`). Units shipped for
|
|
@@ -18,7 +18,7 @@
|
|
|
18
18
|
# box reports healthy while no client ever receives a report. That is the failure the retired `.path`
|
|
19
19
|
# unit's own header warned about, arriving through the other door.
|
|
20
20
|
#
|
|
21
|
-
# ── WHY ExecStart IS THE ENTRYPOINT AND NOT `clearotron start` (
|
|
21
|
+
# ── WHY ExecStart IS THE ENTRYPOINT AND NOT `clearotron start` (ruling, 2026-08-31) ────────────
|
|
22
22
|
#
|
|
23
23
|
# "The simple shape wins, implemented the standard way." Per standard service practice the unit invokes
|
|
24
24
|
# the server entrypoint directly under its own EnvironmentFile; it does not shell through the
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
#
|
|
29
29
|
# ── ONE DRAINER, AND THE LOCK IS WHAT ENFORCES IT ────────────────────────────────────────────────────
|
|
30
30
|
#
|
|
31
|
-
# A second claimant on one queue is this lane's recurring defect
|
|
31
|
+
# A second claimant on one queue is this lane's recurring defect. This unit takes no
|
|
32
32
|
# special precaution against it because none would work: the guarantee is CLEAROTRON_RUN_LOCK_DIR, which
|
|
33
33
|
# this worker and a hand-started `node driver/runner.mjs` already share, so the cap holds across both.
|
|
34
34
|
# What this unit must not do is run beside a FOREGROUND `clearotron start`, which supervises its own
|
|
@@ -48,7 +48,7 @@ EnvironmentFile=%h/.env
|
|
|
48
48
|
Environment=PATH=%h/.local/bin:%h/.npm-global/bin:/usr/local/bin:/usr/bin:/bin
|
|
49
49
|
Environment=CLEAROTRON_NO_ENV_FILE=1
|
|
50
50
|
ExecStart=/usr/bin/node ${CLEAROTRON_CHECKOUT_DIR}/driver/runner.mjs --watch
|
|
51
|
-
# ── REFRESH THE CAPABILITY PAGE'S CAPTURE AT EVERY START
|
|
51
|
+
# ── REFRESH THE CAPABILITY PAGE'S CAPTURE AT EVERY START ─────────────────────────────────────────────
|
|
52
52
|
# The capability page renders `pool/_state/prelim-flag-snapshot.json`, and until this line its only
|
|
53
53
|
# writers were a RUN and the `clearotron start` launcher. A hosted box runs neither: these units are the
|
|
54
54
|
# entrypoint, and a deployment being CONFIGURED runs nothing by definition. So the page reported the old
|
|
@@ -57,7 +57,7 @@ ExecStart=/usr/bin/node ${CLEAROTRON_CHECKOUT_DIR}/driver/runner.mjs --watch
|
|
|
57
57
|
# every service restarted, and the page still said Signa.
|
|
58
58
|
#
|
|
59
59
|
# This unit can write it because it carries `EnvironmentFile=%h/.env` — one configuration per server box,
|
|
60
|
-
#
|
|
60
|
+
# ruling 2026-08-26, which removed the second source of truth rather than defending against it.
|
|
61
61
|
# `-` prefix: a snapshot is a diagnostic surface, and failing to write one must never fail the service.
|
|
62
62
|
ExecStartPost=-/usr/bin/node ${CLEAROTRON_CHECKOUT_DIR}/driver/flag-snapshot.mjs
|
|
63
63
|
#
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// ── why this exists ─────────────────────────────────────────────────────────────
|
|
6
6
|
//
|
|
7
|
-
//
|
|
7
|
+
// This names a family rather than a bug: four values this product needs are each written
|
|
8
8
|
// by exactly one command, and for three of them that command is not the documented install. A box built
|
|
9
9
|
// strictly from the document therefore came up with correct units, correct code, and a value nothing
|
|
10
10
|
// ever set — and each one failed as something else. A 502 on submit. An empty screen. A door that would
|
|
@@ -4,7 +4,7 @@
|
|
|
4
4
|
//
|
|
5
5
|
// render-units.mjs — RESOLVE THE UNITS CONFIGURATION CANNOT REACH, AND WRITE THE RESOLVED COPIES.
|
|
6
6
|
//
|
|
7
|
-
//
|
|
7
|
+
// Ruling 2026-08-25 (option B): the installer writes resolved unit copies into
|
|
8
8
|
// ~/.config/systemd/user/, and the tracked units stay generic.
|
|
9
9
|
//
|
|
10
10
|
// WHY THIS EXISTS AT ALL. Most shipped units take what differs per box from `EnvironmentFile=%h/.env`
|
|
@@ -231,7 +231,7 @@ if (isEntrypoint(import.meta.url)) {
|
|
|
231
231
|
}
|
|
232
232
|
if (APPLY) {
|
|
233
233
|
if (laneNote) console.log(laneNote);
|
|
234
|
-
// ── AND WHAT THIS INSTALL STILL HAS NOT GOT
|
|
234
|
+
// ── AND WHAT THIS INSTALL STILL HAS NOT GOT ─────────────────────────────────────────────────────
|
|
235
235
|
//
|
|
236
236
|
// The nine values above are the ones this command RESOLVES. They are not the values the units
|
|
237
237
|
// refuse to start over, and the gap between those two lists is how a documented install produced a
|
|
@@ -301,7 +301,7 @@ export async function writeInstallEnv(envFile) {
|
|
|
301
301
|
// change, and the reader meets it in the output of the command that does it rather than discovering
|
|
302
302
|
// it in a file.
|
|
303
303
|
const fresh = signingSecretIfAbsent(fileEnv.TRADEMARK_MCP_TOKEN_SECRET ?? process.env.TRADEMARK_MCP_TOKEN_SECRET, { randomBytes });
|
|
304
|
-
// ── HELD, NOT PRINTED
|
|
304
|
+
// ── HELD, NOT PRINTED ─────────────────────────────────────────────────────────────────────────────
|
|
305
305
|
//
|
|
306
306
|
// These lines used to print here, and the refusal below exits before anything reaches disk — so a
|
|
307
307
|
// refused run announced "Written to the env file at mode 600" about two files it never wrote. That
|
|
@@ -321,7 +321,7 @@ export async function writeInstallEnv(envFile) {
|
|
|
321
321
|
" file at mode 600, and never printed.");
|
|
322
322
|
}
|
|
323
323
|
|
|
324
|
-
// ── AND THE PORTAL'S SECRET, WHICH THE SAME ARGUMENT ALWAYS COVERED
|
|
324
|
+
// ── AND THE PORTAL'S SECRET, WHICH THE SAME ARGUMENT ALWAYS COVERED ───────────────────────────────
|
|
325
325
|
//
|
|
326
326
|
// The block above was written for the signing secret alone, and `PORTAL_SECRET` is the identical case
|
|
327
327
|
// one door along: 32 random bytes, minted by `bin/start.mjs` and by nothing else, with no value a
|
|
@@ -329,7 +329,7 @@ export async function writeInstallEnv(envFile) {
|
|
|
329
329
|
// strictly from the document got a portal unit that exited at boot, and the only route to a working
|
|
330
330
|
// one was running the local-install command the instructions say a server does not need.
|
|
331
331
|
//
|
|
332
|
-
// That is the family
|
|
332
|
+
// That is the family this is about: a value whose sole writer is a command outside the
|
|
333
333
|
// documented install. Naming the variable in a warning does not help, because there is no action
|
|
334
334
|
// behind the name — the reader cannot invent 32 random bytes that the portal will accept.
|
|
335
335
|
//
|
|
@@ -392,10 +392,10 @@ export async function writeInstallEnv(envFile) {
|
|
|
392
392
|
}
|
|
393
393
|
for (const [k, v] of Object.entries(doorPlan.settings)) want[k] = v;
|
|
394
394
|
|
|
395
|
-
// ── THE SECRETS ARE ON DISK'S SIDE OF THE REFUSAL NOW
|
|
395
|
+
// ── THE SECRETS ARE ON DISK'S SIDE OF THE REFUSAL NOW ─────────────────────────────────────────────
|
|
396
396
|
for (const line of announcements) console.log(line);
|
|
397
397
|
|
|
398
|
-
// ── A DERIVED VALUE MAY NOT GO STALE
|
|
398
|
+
// ── A DERIVED VALUE MAY NOT GO STALE ──────────────────────────────────────────────────────────────
|
|
399
399
|
//
|
|
400
400
|
// Driven on a greenfield install: `--apply`, then an operator edits `CLIENT_MCP_HTTP_PORT` by hand
|
|
401
401
|
// and runs `--apply` again. The door binds the new port; the allow-list still names the old one;
|
|
@@ -413,13 +413,13 @@ export async function writeInstallEnv(envFile) {
|
|
|
413
413
|
//
|
|
414
414
|
// `CLEAROTRON_CHECKOUT_DIR` is deliberately not here either, for the same reason one issue over.
|
|
415
415
|
//
|
|
416
|
-
// ── BOTH DOORS, NOT ONE
|
|
416
|
+
// ── BOTH DOORS, NOT ONE ───────────────────────────────────────────────────────────────────────────
|
|
417
417
|
//
|
|
418
418
|
// This block used to name the client door alone. The engine door's allow-list is the same derivable
|
|
419
419
|
// `host:port` shape, refuses to start without a value in the same words, and was composed by NOTHING
|
|
420
420
|
// on a hosted install — `bin/start.mjs` injects one into its own children's environment, which no
|
|
421
421
|
// systemd unit inherits. So the documented install asked an operator for a value while writing the
|
|
422
|
-
// identical one next to it. That asymmetry was found by the census
|
|
422
|
+
// identical one next to it. That asymmetry was found by the census that called for it, on
|
|
423
423
|
// its first run, which is what that check is for.
|
|
424
424
|
//
|
|
425
425
|
// The pair is DATA now (`DOOR_ALLOW_LISTS`), and the composition is one function taking the public
|
|
@@ -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
|
// terminal-clamp.mjs — WHAT A TERMINAL GUARD DOES WHEN IT FINDS THE REPORT INCOMPLETE.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
5
|
+
// Ruling, 2026-08-27, verbatim intent: **reports always ship**. When a terminal guard finds the
|
|
6
6
|
// report incomplete at delivery, the engine sends it with the gap patched conservatively and the defect
|
|
7
7
|
// named in the run record. It never withholds.
|
|
8
8
|
//
|
package/driver/trigger-cap.mjs
CHANGED
|
@@ -55,12 +55,28 @@ export function triggerCapGap({ accounts = null, roster = [] } = {}) {
|
|
|
55
55
|
/**
|
|
56
56
|
* The one sentence every surface says about a gap, so three of them cannot say three different things.
|
|
57
57
|
*
|
|
58
|
+
* IT WAS TWO UNTIL THE THIRD WAS WIRED. This function's own comment claimed three call sites and there
|
|
59
|
+
* were two — the create command and the portal's boot log. `doctor`, the command whose whole job is to
|
|
60
|
+
* report what a machine is configured for, said nothing at all about a cap that decides which companies
|
|
61
|
+
* can be started. The claim was aspirational and is now accurate.
|
|
62
|
+
*
|
|
58
63
|
* The wording is the portal boot check's, kept deliberately: it was measured to be the thing that would
|
|
59
64
|
* have saved the owner an afternoon, and rewording it per surface is how the good half of a defect gets
|
|
60
65
|
* lost while the fix ships.
|
|
66
|
+
*
|
|
67
|
+
* IT SAID THE RUNS WOULD BE REFUSED, AND THAT STOPPED BEING TRUE. The portal now re-takes its engine
|
|
68
|
+
* credential against the roster as it stands at the start of every call, so a stale cap no longer
|
|
69
|
+
* refuses anything on the ordinary path. The gap is still worth saying — the portal falls back to this
|
|
70
|
+
* token when it cannot re-mint, and then the refusal is real — but the sentence has to describe the
|
|
71
|
+
* fallback rather than the normal case, or it sends an operator to re-mint a credential for a failure
|
|
72
|
+
* that will not happen. Changed HERE rather than at the three call sites, which is the whole reason
|
|
73
|
+
* this function exists.
|
|
61
74
|
*/
|
|
62
75
|
export function triggerCapWarning({ uncovered, union }) {
|
|
63
|
-
return `the roster contains account(s)
|
|
64
|
-
+ `
|
|
76
|
+
return `the roster contains account(s) this token cannot start: ${uncovered.join(", ")}. `
|
|
77
|
+
+ `The portal re-takes its engine credential at the start of every call, so runs for them are not `
|
|
78
|
+
+ `normally refused; they are refused when it CANNOT re-mint — an unreadable signing secret, or a `
|
|
79
|
+
+ `store it cannot list — because it then falls back to this token rather than widening the cap. `
|
|
80
|
+
+ `Re-mint with --accounts ${union.join(",")} `
|
|
65
81
|
+ `(union of the current cap and the roster — check it before using it).`;
|
|
66
82
|
}
|
|
@@ -75,7 +75,7 @@
|
|
|
75
75
|
/** Where a unit is expected to be installed. "none" is a claim, not an absence — see ORPHANED below. */
|
|
76
76
|
export const BOXES = Object.freeze(["prod", "test", "dev"]);
|
|
77
77
|
|
|
78
|
-
// ── RESOLVED UNITS (
|
|
78
|
+
// ── RESOLVED UNITS (ruling 2026-08-25 — option B) ──────────────────────
|
|
79
79
|
//
|
|
80
80
|
// `resolved: [...]` names tracked files the INSTALLER rewrites before installing. Three units carry
|
|
81
81
|
// `@NAME@` placeholders because configuration cannot reach them: a `.path` unit reads no environment at
|
|
@@ -88,7 +88,7 @@ export const BOXES = Object.freeze(["prod", "test", "dev"]);
|
|
|
88
88
|
// everything declared here really carries one. A one-way check would let a new placeholder ship
|
|
89
89
|
// undeclared, which is the same shape as the undeclared units this file was written to make impossible.
|
|
90
90
|
|
|
91
|
-
// ── RETIRED UNITS (;
|
|
91
|
+
// ── RETIRED UNITS (; ruling 2026-08-26, restated 2026-08-31) ────────────
|
|
92
92
|
//
|
|
93
93
|
// `retired: { ruled, filesStayUntil, why }` says a unit's posture has been ruled away while its tracked
|
|
94
94
|
// files are still in the tree. It is a FACT about a decision, not a pointer at a replacement — the
|
|
@@ -99,12 +99,12 @@ export const BOXES = Object.freeze(["prod", "test", "dev"]);
|
|
|
99
99
|
// `filesStayUntil` is a BOX, and it is the whole reason the files are still here. Deleting a tracked
|
|
100
100
|
// file whose unit that box is running today removes the only tracked description of a live service —
|
|
101
101
|
// the mistake this file's own header records being caught once already, for `profile-service` and
|
|
102
|
-
// `prelim-outbox`. Production is on pre-sweep code and is rebuilt rather than migrated (
|
|
102
|
+
// `prelim-outbox`. Production is on pre-sweep code and is rebuilt rather than migrated (ruling
|
|
103
103
|
// 2026-08-26), so the rebuild is the event that releases the files, and until then entry and tree
|
|
104
104
|
// disagree ON PURPOSE.
|
|
105
105
|
//
|
|
106
106
|
// THE REBUILD HAPPENED, and the first two entries this field ever carried have gone with their files.
|
|
107
|
-
// Measured on the production install 2026-09-07
|
|
107
|
+
// Measured on the production install 2026-09-07: seven units, all under the current
|
|
108
108
|
// names, none of the retired ones, with a clearance delivered end to end. `prelim-driver.*` and
|
|
109
109
|
// `prelim-outbox.*` left the tree in the same change that removed their entries. Read the paragraph
|
|
110
110
|
// above as the rule and this one as the event that discharged it — without this line a reader concludes
|
|
@@ -141,7 +141,7 @@ export const UNIT_INVENTORY = Object.freeze([
|
|
|
141
141
|
// held deliberately, a check that says so for as long as the hold lasts is the behaviour to want.
|
|
142
142
|
},
|
|
143
143
|
{
|
|
144
|
-
// ── `tracked` WAS A CLAIM ABOUT A FILE THAT HAS NEVER EXISTED
|
|
144
|
+
// ── `tracked` WAS A CLAIM ABOUT A FILE THAT HAS NEVER EXISTED ──────────────────────────────────
|
|
145
145
|
//
|
|
146
146
|
// This entry named `profile-service.service` as tracked. `git log --all` on that path is empty: it is
|
|
147
147
|
// not in the tree, not in the tarball, and has never been in this repository's history. So
|
|
@@ -170,7 +170,7 @@ export const UNIT_INVENTORY = Object.freeze([
|
|
|
170
170
|
// calls the sanctioned way to say the repo does not carry one.
|
|
171
171
|
//
|
|
172
172
|
// ONE THING THIS DOES NOT SETTLE, left visible rather than decided quietly. The note calls the unit
|
|
173
|
-
// "generic since
|
|
173
|
+
// "generic since the placeholder ruling" — an argument that a placeholder COULD now ship and be compared,
|
|
174
174
|
// because the reason its siblings cannot (real CF Access values inline) may no longer hold here. That
|
|
175
175
|
// is a change which ADDS a file, measured against the deployed copy, and it is not this one. Two
|
|
176
176
|
// prose references in driver/systemd/render-units.mjs also describe this file as though it were in
|
|
@@ -247,7 +247,7 @@ export const UNIT_INVENTORY = Object.freeze([
|
|
|
247
247
|
//
|
|
248
248
|
// Until then it was an orphan BY DESIGN: `clearotron connect` installed and started it on demand,
|
|
249
249
|
// because starting it turned on client-account access and a unit that came up with everything else
|
|
250
|
-
// would have made that consent meaningless (;
|
|
250
|
+
// would have made that consent meaningless (; ruling 2026-08-31, "On demand
|
|
251
251
|
// is fine"). He superseded that knowingly — the door auto-starts and the per-account key is the
|
|
252
252
|
// gate, not whether a process runs.
|
|
253
253
|
//
|
|
@@ -278,7 +278,7 @@ export const UNIT_INVENTORY = Object.freeze([
|
|
|
278
278
|
+ "directly (owner ruling 2026-08-31). Ships tracked, runs nowhere yet — production takes it at "
|
|
279
279
|
+ "its rebuild, which is the same event that lets the retired units' files leave the tree.",
|
|
280
280
|
orphanReason: "AWAITING A BOX, not an orphan by design. It is the hosted posture's drainer, and the "
|
|
281
|
-
+ "hosted posture is an opt-in step no install performs
|
|
281
|
+
+ "hosted posture is an opt-in step no install performs. It must NOT be "
|
|
282
282
|
+ "started beside `clearotron start`, which supervises its own worker — the two postures are "
|
|
283
283
|
+ "alternatives, and running both puts a second claimant on one queue.",
|
|
284
284
|
},
|
package/driver/usage-ledger.mjs
CHANGED
|
@@ -98,7 +98,9 @@ export function matterLedgerPath(qdir) { return join(dirname(qdir), ".matter-led
|
|
|
98
98
|
* No paths in the return value: it is spread into client-facing JSON by both the portal and the MCP
|
|
99
99
|
* preview, and server paths are not a client's business.
|
|
100
100
|
*/
|
|
101
|
-
export function accountUsage({ queueDirs, account, now = Date.now() }) {
|
|
101
|
+
export function accountUsage({ queueDirs, account, organisation = null, now = Date.now() }) {
|
|
102
|
+
// A Generic run counts in the lane of the organisation it was filed under, as checkRunCaps counts it.
|
|
103
|
+
const inLane = (key, org) => (key ?? null) === account && (account !== "generic" || (org ?? null) === organisation);
|
|
102
104
|
const dayKey = new Date(now).toISOString().slice(0, 10);
|
|
103
105
|
const monthKey = new Date(now).toISOString().slice(0, 7);
|
|
104
106
|
let today = 0, thisMonth = 0, queued = 0;
|
|
@@ -113,7 +115,7 @@ export function accountUsage({ queueDirs, account, now = Date.now() }) {
|
|
|
113
115
|
read = true;
|
|
114
116
|
for (const line of rows) {
|
|
115
117
|
let e; try { e = JSON.parse(line); } catch { continue; }
|
|
116
|
-
if (e.profileKey
|
|
118
|
+
if (!inLane(e.profileKey, e.organisation) || typeof e.ts !== "number") continue;
|
|
117
119
|
const stamp = new Date(e.ts).toISOString();
|
|
118
120
|
if (stamp.slice(0, 7) === monthKey) thisMonth++;
|
|
119
121
|
// `today` must count exactly what the WALL counts, or the screen lies. checkRunCaps skips failed
|
|
@@ -132,7 +134,7 @@ export function accountUsage({ queueDirs, account, now = Date.now() }) {
|
|
|
132
134
|
// which the old inline regex here missed (isLiveQueueMarker is the one definition;).
|
|
133
135
|
try {
|
|
134
136
|
for (const f of readdirSync(qdir).filter(isLiveQueueMarker)) {
|
|
135
|
-
try {
|
|
137
|
+
try { const j = JSON.parse(readFileSync(join(qdir, f), "utf8")); if (inLane(j.profileKey, j.tenant)) queued++; }
|
|
136
138
|
catch { /* mid-rename — skip */ }
|
|
137
139
|
}
|
|
138
140
|
} catch { /* no queue dir */ }
|
|
@@ -117,7 +117,7 @@ export function knockoutReceipts(runDir, marks) {
|
|
|
117
117
|
return { ok: failures.length === 0, failures, checked };
|
|
118
118
|
}
|
|
119
119
|
|
|
120
|
-
// ── ratingQualifier: the closed SECOND axis (
|
|
120
|
+
// ── ratingQualifier: the closed SECOND axis (ruling, 2026-08-06) ────────────────────────
|
|
121
121
|
//
|
|
122
122
|
// The band is the rating; the qualifier is the only sub-gradation the doctrine allows, and until now it
|
|
123
123
|
// was free text on the mark row — validated by nothing, rendered verbatim on the client page
|
|
@@ -163,9 +163,9 @@ export const READ_FIELDS = Object.freeze(["basis", "factors", "counterFactors",
|
|
|
163
163
|
export const REGISTER_CLAIM_RE =
|
|
164
164
|
/\b(?:register|registry|registers|trademark office)\s+(?:overlay\s+|search(?:es)?\s+|check(?:s)?\s+|count(?:s)?\s+|data\s+)?(?:has|have|was|were|is|are)\s+(?:not\s+|n't\s+|yet\s+)?(?:been\s+)?(?:run|searched|checked|counted|performed|conducted|reviewed|carried out)\b/i;
|
|
165
165
|
|
|
166
|
-
// ── THE SUMMARY'S SHAPE
|
|
166
|
+
// ── THE SUMMARY'S SHAPE ──────────────────────────────────────────────────
|
|
167
167
|
//
|
|
168
|
-
//
|
|
168
|
+
// Ruling 2026-08-31: "keep the length, add the structure, so long as length is consistent more or
|
|
169
169
|
// less." Two rules follow, and they are deliberately unlike each other.
|
|
170
170
|
//
|
|
171
171
|
// 1. AN H1 IS A CORRECTNESS DEFECT, refused outright. `# ` opens a SECTION of the delivered report:
|
|
@@ -360,7 +360,7 @@ export const validators = {
|
|
|
360
360
|
if (!rating) return { ok: false, reason: `mark "${m.name}": rating is required` };
|
|
361
361
|
if (ladder.length && bandIdx(ladder, rating) < 0)
|
|
362
362
|
return { ok: false, reason: `knockout_band_unknown:${m.name}: rating "${rating}" is not in the frozen ladder (${ladder.join(" / ")}) — rate in the framework's own vocabulary` };
|
|
363
|
-
// the second axis, closed here because this is where the first one is closed (
|
|
363
|
+
// the second axis, closed here because this is where the first one is closed ( ruling)
|
|
364
364
|
if (m.ratingQualifier != null && !normalizeKnockoutQualifier(m.ratingQualifier))
|
|
365
365
|
return { ok: false, reason: `knockout_qualifier_unknown:${m.name}: ratingQualifier "${m.ratingQualifier}" is not one of ${KNOCKOUT_RATING_QUALIFIERS.join(" / ")} — the qualifier is a closed sub-gradation that can only cap a band, and anything else belongs in the band word itself` };
|
|
366
366
|
if (!Array.isArray(m.bullets) || m.bullets.length < 1 || m.bullets.length > 5)
|
|
@@ -419,7 +419,7 @@ export const validators = {
|
|
|
419
419
|
// paragraph names every mark, so publish blanked it rather than put other clients' marks on this
|
|
420
420
|
// client's page, and nothing owed a replacement.
|
|
421
421
|
//
|
|
422
|
-
//
|
|
422
|
+
// Ruling: every mark's own report opens with a model-authored paragraph about that mark.
|
|
423
423
|
// Required rather than optional, because the failure it replaces was a page that opened with
|
|
424
424
|
// NOTHING and looked deliberate — publish blanked the batch paragraph on a per-mark document (it
|
|
425
425
|
// names other clients' marks) and no one field owed a replacement.
|
|
@@ -540,8 +540,8 @@ export function registerSurfacedFilings(recordsPath, { read = readFileSync } = {
|
|
|
540
540
|
/**
|
|
541
541
|
* The survivor sentence's signature, and the engine's own caveats.
|
|
542
542
|
*
|
|
543
|
-
* MOVED HERE FROM pipeline-knockout SO THERE IS ONE DERIVATION, for the identical reason
|
|
544
|
-
*
|
|
543
|
+
* MOVED HERE FROM pipeline-knockout SO THERE IS ONE DERIVATION, for the identical reason an earlier
|
|
544
|
+
* change moved `registerSurfacedFilings`: the producer and the verifier were asking different questions of
|
|
545
545
|
* the same array, and two predicates that must agree forever is how they stop agreeing.
|
|
546
546
|
*
|
|
547
547
|
* `pipeline-knockout.mjs` re-exports `SURVIVOR_BOUNDARY_RE` so its existing importers are untouched; the
|
package/driver/verify.mjs
CHANGED
|
@@ -144,7 +144,7 @@ function nonEmpty(content, min = MIN) {
|
|
|
144
144
|
* ── WHY THE LABEL STAYS AND THE MEMBER IS APPENDED ──────────────────────────────────────────────────
|
|
145
145
|
*
|
|
146
146
|
* The obvious fix — emit the failing marker INSTEAD of the label — silently breaks the corrective hints.
|
|
147
|
-
* `correctionHint` branches on the label text (
|
|
147
|
+
* `correctionHint` in gateway.mjs branches on the label text (its `findings+ledger` arm, and the arm on
|
|
148
148
|
* `negative-results|coverage-ledger|audit-trail|findings-heading`), so a renamed token would fall through
|
|
149
149
|
* to a generic hint. That comment records that arm being removed once already on a reading that was
|
|
150
150
|
* true for only one lane, and put back. Appending keeps every existing matcher matching — they all test
|
|
@@ -166,7 +166,7 @@ function needs(content, markers, label, names = []) {
|
|
|
166
166
|
}
|
|
167
167
|
return ok();
|
|
168
168
|
}
|
|
169
|
-
// ── THE STRUCTURAL SECTION CONTRACT
|
|
169
|
+
// ── THE STRUCTURAL SECTION CONTRACT ────────────────────────────────────────────────────────────────
|
|
170
170
|
//
|
|
171
171
|
// THE CLASS. Every gate below keys on prose the MODEL composes, so each is one phrasing drift away
|
|
172
172
|
// from killing a run that produced the section perfectly. Measured across three runs and two engines:
|
|
@@ -192,7 +192,7 @@ function needs(content, markers, label, names = []) {
|
|
|
192
192
|
// nothing else. Once seats emit anchors reliably, the fallback can be measured and retired; that is a
|
|
193
193
|
// later decision needing evidence, not this change.
|
|
194
194
|
//
|
|
195
|
-
// The label is unchanged on purpose: `correctionHint`
|
|
195
|
+
// The label is unchanged on purpose: `correctionHint` in gateway.mjs branches on the label
|
|
196
196
|
// TEXT, so renaming a token here silently downgrades the seat's corrective hint to a generic one.
|
|
197
197
|
const SECTION_ANCHOR_RE = (name) =>
|
|
198
198
|
new RegExp(`<!--\\s*clearotron:section\\s*=\\s*${name.replace(/[.*+?^${}()|[\]\\]/g, "\\$&")}\\s*-->`, "i");
|
|
@@ -1571,7 +1571,7 @@ export const validators = {
|
|
|
1571
1571
|
//
|
|
1572
1572
|
// The frame is rendered by the driver now, from a typed `record_matter_frame` call. Two of the checks
|
|
1573
1573
|
// below would become the driver comparing its own render against its own input — a guard that CANNOT
|
|
1574
|
-
// FAIL, which reads as coverage and is worse than no guard (
|
|
1574
|
+
// FAIL, which reads as coverage and is worse than no guard (ruling, 2026-08-17). So each one is
|
|
1575
1575
|
// stated here, by token, with what happened to it:
|
|
1576
1576
|
//
|
|
1577
1577
|
// `meaning_angles_missing` — DELETED on the recorded path. `acceptMatterFrame` refuses a call that
|
|
@@ -1867,7 +1867,7 @@ export const validators = {
|
|
|
1867
1867
|
// artifact passing, and a fresh dispatch under this code is held to the new floor. A sibling that
|
|
1868
1868
|
// EXISTS is parsed strictly regardless of vintage (present-and-malformed is always a defect).
|
|
1869
1869
|
placement: (p, c) => {
|
|
1870
|
-
// THE LAST PROSE-KEYED GATE ON A SEAT-WRITTEN ARTIFACT
|
|
1870
|
+
// THE LAST PROSE-KEYED GATE ON A SEAT-WRITTEN ARTIFACT.
|
|
1871
1871
|
//
|
|
1872
1872
|
// 129 named three sites and all three had stopped being members before it was filed: conversions 5
|
|
1873
1873
|
// and 11 moved report-cards/<ord>.md and register-findings.md to driver renders, and audit.md was
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
// SPDX-License-Identifier: AGPL-3.0-only
|
|
2
2
|
// Copyright 2026 Cordillera Sàrl. Additional terms under section 7 of the AGPL-3.0 apply — see ADDITIONAL-TERMS.md
|
|
3
|
-
// whatif-memo-run.mjs — the door onto the memo composer
|
|
3
|
+
// whatif-memo-run.mjs — the door onto the memo composer.
|
|
4
4
|
//
|
|
5
5
|
// WHY THIS FILE EXISTS. Everything a memo needs was already in the tree and nothing could reach it:
|
|
6
6
|
// whatif-memo.mjs composes one, whatIfRefusal admits `kind: "memo"` on a finished run, decodeOp
|
package/driver/whatif-queue.mjs
CHANGED
|
@@ -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
|
// whatif-queue.mjs — the RUN-LOCAL queue a client's what-if waits in.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
5
|
+
// Ruling 2026-08-27 opened what-if to clients. The remote surfaces cannot run it directly and that
|
|
6
6
|
// is a configuration fact rather than a policy: http-server.mjs states in its own header that they "NEVER
|
|
7
7
|
// shell", and lib/whatif.mjs imports driver/pipeline.mjs LAZILY so the read-only face never loads the
|
|
8
8
|
// engine at all. The only thing that runs the engine remotely is the queue, through the worker. So a
|
|
@@ -124,13 +124,13 @@ function pathFor(runDir, id, suffix) { return join(whatIfQueueDir(runDir), `${id
|
|
|
124
124
|
*/
|
|
125
125
|
export function enqueueWhatIf(runDir, { op, requestedBy = null, account = null, now = Date.now() }) {
|
|
126
126
|
if (!op || typeof op !== "object" || !op.runId) throw new Error("enqueueWhatIf: op must carry runId and stage");
|
|
127
|
-
// ── A MEMO CARRIES NO STAGE, AND THIS GUARD USED TO REQUIRE ONE
|
|
127
|
+
// ── A MEMO CARRIES NO STAGE, AND THIS GUARD USED TO REQUIRE ONE ──────────────────────────────────
|
|
128
128
|
//
|
|
129
129
|
// Two consecutive guards, mutually exclusive for a memo. decodeOp refuses a memo token that CARRIES
|
|
130
130
|
// a stage — "a memo re-runs no stage, so a token carrying one was not planned as a memo" — and this
|
|
131
131
|
// line refused one that did not. So every memo minted by whatIfPlan was admitted by whatIfEnqueue's
|
|
132
132
|
// kind-aware refusal and then thrown out by the very next call, on the one non-test path there is
|
|
133
|
-
// (mcp-server/lib/whatif.mjs
|
|
133
|
+
// (`whatIfEnqueue()` in `mcp-server/lib/whatif.mjs`, reached from the live `what_if_run` handler in `mcp-server/server.mjs`).
|
|
134
134
|
//
|
|
135
135
|
// WHY IT SURVIVED A FIX THAT WAS ABOUT EXACTLY THIS. The earlier repair opened whatIfRun's memo
|
|
136
136
|
// branch and was proved by arms that INJECT the composer — routing coverage. A routing arm cannot
|
package/driver/whatif-worker.mjs
CHANGED
|
@@ -50,7 +50,7 @@ export const WHATIF_MAX_CONCURRENT = Math.max(1, Number(process.env.CLEAROTRON_W
|
|
|
50
50
|
// list of markers kept here. This file used to carry its own list, and it included `.failed` where the
|
|
51
51
|
// MCP door's copy did not — so a failed run was planned, enqueued and then refused here. See the
|
|
52
52
|
// declaration for what that cost and why `failed` is now eligible.
|
|
53
|
-
// ── THE MARKERS ARE KIND-INDEPENDENT; THE VERDICT IS NOT
|
|
53
|
+
// ── THE MARKERS ARE KIND-INDEPENDENT; THE VERDICT IS NOT ────────────────────────────────────────────
|
|
54
54
|
//
|
|
55
55
|
// This used to render the verdict here, calling whatIfRefusal with NO `kind` — so it defaulted to
|
|
56
56
|
// "stage" and refused every memo on a delivered run with "what-if runs on live runs only", the third
|
|
@@ -136,7 +136,7 @@ async function settleOne(entry, exec) {
|
|
|
136
136
|
const result = await exec({ confirmationToken });
|
|
137
137
|
// A memo carries no stage — it re-runs none — so the old form logged a literal "undefined" for
|
|
138
138
|
// every memo the worker settled. The kind is what distinguishes the two here, and an operator
|
|
139
|
-
// reading the drain log needs to know which of them just ran
|
|
139
|
+
// reading the drain log needs to know which of them just ran.
|
|
140
140
|
const what = job.op?.kind === "memo" ? "memo" : `${job.op.stage}${job.op.axis ? `:${job.op.axis}` : ""}`;
|
|
141
141
|
note(`[whatif] ${id} ${result?.ok === false ? "failed" : "done"} (${what})`);
|
|
142
142
|
return finishWhatIf(runDir, id, { ok: result?.ok !== false, op: job.op, result, error: result?.ok === false ? (result.fail ?? "the experiment did not complete") : null });
|
package/examples/README.md
CHANGED
|
@@ -5,7 +5,7 @@ The two input files you copy. Nothing here needs a credential and nothing here c
|
|
|
5
5
|
| Path | What it is |
|
|
6
6
|
|---|---|
|
|
7
7
|
| `job.euipo.json` | the whole input contract for one clearance — 14 fields, ready to copy and edit |
|
|
8
|
-
| `grants.example.json` | who may
|
|
8
|
+
| `grants.example.json` | who may see and do what: two organisations, and one of each kind of person |
|
|
9
9
|
|
|
10
10
|
## The finished runs moved to `demo/`
|
|
11
11
|
|
|
@@ -1,40 +1,41 @@
|
|
|
1
1
|
{
|
|
2
2
|
"tenants": {
|
|
3
|
-
"
|
|
4
|
-
"
|
|
5
|
-
"users": {
|
|
6
|
-
"principal@firm.example": "*",
|
|
7
|
-
"associate@firm.example": [
|
|
8
|
-
"demo-brand-owner"
|
|
9
|
-
]
|
|
10
|
-
}
|
|
11
|
-
},
|
|
12
|
-
"brand-owner-direct": {
|
|
3
|
+
"demo-org": {
|
|
4
|
+
"name": "Demo Org",
|
|
13
5
|
"accounts": [
|
|
14
6
|
"demo-brand-owner"
|
|
15
7
|
],
|
|
16
8
|
"users": {
|
|
17
|
-
"*@
|
|
9
|
+
"*@demo-org.example": "*",
|
|
10
|
+
"consultant@outside.example": [
|
|
11
|
+
"demo-brand-owner"
|
|
12
|
+
]
|
|
18
13
|
}
|
|
19
14
|
},
|
|
20
15
|
"evaluation": {
|
|
21
|
-
"
|
|
22
|
-
|
|
23
|
-
],
|
|
16
|
+
"name": "Evaluation",
|
|
17
|
+
"accounts": [],
|
|
24
18
|
"users": {
|
|
25
19
|
"reviewer@partner.example": "*",
|
|
26
|
-
"observer@partner.example":
|
|
27
|
-
"demo-brand-owner"
|
|
28
|
-
]
|
|
20
|
+
"observer@partner.example": "*"
|
|
29
21
|
}
|
|
22
|
+
}
|
|
23
|
+
},
|
|
24
|
+
"people": {
|
|
25
|
+
"you@install.example": {
|
|
26
|
+
"run": true,
|
|
27
|
+
"manage": true,
|
|
28
|
+
"everything": true
|
|
30
29
|
},
|
|
31
|
-
"demo-org": {
|
|
32
|
-
"
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
"
|
|
36
|
-
|
|
37
|
-
|
|
30
|
+
"*@demo-org.example": {
|
|
31
|
+
"run": true
|
|
32
|
+
},
|
|
33
|
+
"consultant@outside.example": {
|
|
34
|
+
"run": true
|
|
35
|
+
},
|
|
36
|
+
"reviewer@partner.example": {
|
|
37
|
+
"run": true,
|
|
38
|
+
"manage": true
|
|
38
39
|
}
|
|
39
40
|
}
|
|
40
41
|
}
|
package/mcp-server/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,15 @@
|
|
|
1
1
|
# trademark-artifacts-mcp
|
|
2
2
|
|
|
3
|
+
## 0.3.0-beta.1
|
|
4
|
+
|
|
5
|
+
### Patch Changes
|
|
6
|
+
|
|
7
|
+
- b8ec213: Fixed: a company you create in the browser now appears in your assistant's list of companies straight away.
|
|
8
|
+
|
|
9
|
+
It used to appear only after the service restarted, although a search could already be started for it. Its projects were missing from the list in the same way, and so was its account in the search options.
|
|
10
|
+
|
|
11
|
+
The live surface check no longer reports a company outside the portal's trigger key as the service and the store disagreeing. It names the key's gap on a line of its own, with the command to widen the key.
|
|
12
|
+
|
|
3
13
|
## 0.2.4
|
|
4
14
|
|
|
5
15
|
## 0.2.3
|