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/shared/staff-domain.mjs
CHANGED
|
@@ -1,40 +1,28 @@
|
|
|
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
|
-
// staff-domain.mjs —
|
|
3
|
+
// staff-domain.mjs — the sign-in addresses setup and `clearotron start` refuse, and why.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
7
|
-
// (
|
|
5
|
+
// This module used to decide what a staff-domain rule would admit, because the install built one from
|
|
6
|
+
// everything after the `@` of the address it was given. That rule is deleted: a person is admitted by
|
|
7
|
+
// their own entry in the grants file (shared/scope.mjs `resolvePerson`), and nothing about the part of an
|
|
8
|
+
// address after its `@` admits anyone. The person who installs is the first person, with access to
|
|
9
|
+
// everything (bin/start.mjs `installerGrants`).
|
|
8
10
|
//
|
|
9
|
-
//
|
|
10
|
-
// was given. On a laptop that address is `<account>@localhost` and the rule admits the one identity
|
|
11
|
-
// that can sign in, which is why it was safe and why nobody looked at it again. Given a real address
|
|
12
|
-
// the same line turns a person into their whole employer, or into every customer of a webmail
|
|
13
|
-
// provider, with nothing said to the operator and nothing to undo. A stranger's install granted a
|
|
14
|
-
// documentation domain that way, read its own settings page back, and reported it as a back door.
|
|
11
|
+
// Two refusals about the ADDRESS ITSELF outlive the rule, and this module owns the second:
|
|
15
12
|
//
|
|
16
|
-
//
|
|
13
|
+
// not a single email address refused where it is typed (bin/onboard.mjs `askSignIn`, bin/start.mjs),
|
|
14
|
+
// because the portal refuses a multi-`@` identity outright.
|
|
15
|
+
// a public or reserved domain refused here. The address becomes the first person, with access to
|
|
16
|
+
// everything, and a personal mailbox at a webmail provider is not an
|
|
17
|
+
// address an organisation controls. A domain reserved for documentation
|
|
18
|
+
// receives no real mail, so an address there is a placeholder somebody
|
|
19
|
+
// typed — which is how an outside install came to be set up in the name of
|
|
20
|
+
// nobody.
|
|
17
21
|
//
|
|
18
|
-
//
|
|
19
|
-
//
|
|
20
|
-
// "public" a webmail or shared provider. Never a staff rule: it would admit strangers, and no
|
|
21
|
-
// deployment can want it. Refused outright, and the refusal names the domain.
|
|
22
|
-
// "reserved" a domain reserved for documentation and testing (RFC 2606). Nobody's real mail lives
|
|
23
|
-
// there, so a rule built from one is always an address somebody typed as a placeholder.
|
|
24
|
-
// Refused for the same reason, with a different sentence, because the remedy differs.
|
|
25
|
-
// "wide" an ordinary routable domain. It may well be the right rule — it is how a firm admits
|
|
26
|
-
// its own lawyers — but it admits people the operator has not met, so it is stated in
|
|
27
|
-
// the words the settings page will use and confirmed before it is written. It is never
|
|
28
|
-
// derived silently from one address.
|
|
29
|
-
//
|
|
30
|
-
// FAIL-CLOSED, in the direction of a smaller grant. A domain wrongly classified `public` refuses, and
|
|
31
|
-
// the operator sets `PORTAL_STAFF_DOMAINS` themselves in one line — an explicit decision, recorded
|
|
32
|
-
// where the settings page can name it. A domain wrongly classified `wide` asks a question. Neither
|
|
33
|
-
// outcome grants anything, which is why the lists below are allowed to be short and stay short: the
|
|
34
|
-
// `wide` branch is what actually protects an operator, and the lists only decide whether the product
|
|
35
|
-
// asks a question or refuses to ask one.
|
|
22
|
+
// The lists stay short on purpose. An unlisted provider is accepted, and since the domain rule went an
|
|
23
|
+
// accepted address admits that one address and nobody else, so a miss admits no stranger.
|
|
36
24
|
|
|
37
|
-
/** Last-@ semantics, matching
|
|
25
|
+
/** Last-@ semantics, matching shared/scope.mjs `resolvePerson`. */
|
|
38
26
|
export function domainOfEmail(email) {
|
|
39
27
|
const e = String(email ?? "").trim().toLowerCase();
|
|
40
28
|
const at = e.lastIndexOf("@");
|
|
@@ -44,10 +32,9 @@ export function domainOfEmail(email) {
|
|
|
44
32
|
/**
|
|
45
33
|
* Webmail and shared mailbox providers, exactly.
|
|
46
34
|
*
|
|
47
|
-
* Not a census of the internet, and deliberately not growing into one: an unlisted provider
|
|
48
|
-
*
|
|
49
|
-
*
|
|
50
|
-
* sentence about why, instead of being offered as a rule somebody might say yes to.
|
|
35
|
+
* Not a census of the internet, and deliberately not growing into one: an unlisted provider is accepted.
|
|
36
|
+
* What this list buys is that the commonest addresses a person types — their own personal mail — are
|
|
37
|
+
* sent back with a sentence about why.
|
|
51
38
|
*/
|
|
52
39
|
export const PUBLIC_EMAIL_DOMAINS = Object.freeze(new Set([
|
|
53
40
|
"gmail.com", "googlemail.com", "icloud.com", "me.com", "mac.com",
|
|
@@ -81,10 +68,10 @@ export const PUBLIC_EMAIL_FAMILIES = Object.freeze(new Set([
|
|
|
81
68
|
*
|
|
82
69
|
* `example.com`, `example.net`, `example.org` and everything under them, plus the whole of the
|
|
83
70
|
* `.example`, `.invalid`, `.test` and `.localhost` top-level names. Real mail is never delivered to
|
|
84
|
-
* any of them, so an address at one is a placeholder somebody typed
|
|
71
|
+
* any of them, so an address at one is a placeholder somebody typed.
|
|
85
72
|
*
|
|
86
|
-
* `localhost` ITSELF IS NOT HERE. A bare `localhost` is the local-account default
|
|
87
|
-
*
|
|
73
|
+
* `localhost` ITSELF IS NOT HERE. A bare `localhost` is the local-account default, `<account>@localhost`,
|
|
74
|
+
* and every first install takes it; only `something.localhost` reaches this test.
|
|
88
75
|
*/
|
|
89
76
|
function isReservedDomain(domain) {
|
|
90
77
|
if (/^example\.(com|net|org)$/.test(domain)) return true;
|
|
@@ -93,62 +80,42 @@ function isReservedDomain(domain) {
|
|
|
93
80
|
return domain.includes(".") && ["example", "invalid", "test", "localhost"].includes(tld);
|
|
94
81
|
}
|
|
95
82
|
|
|
96
|
-
/** A domain that cannot name a second person on this machine: `localhost`, or any dotless hostname. */
|
|
97
|
-
function isNarrowDomain(domain) {
|
|
98
|
-
return domain === "localhost" || domain === "localhost.localdomain" || !domain.includes(".");
|
|
99
|
-
}
|
|
100
|
-
|
|
101
83
|
/**
|
|
102
|
-
*
|
|
84
|
+
* Whether an address at this domain may be the one that signs in.
|
|
103
85
|
*
|
|
104
|
-
* @param {string} domain a bare domain
|
|
105
|
-
* @returns {"
|
|
86
|
+
* @param {string} domain a bare domain — `domainOfEmail` produces one.
|
|
87
|
+
* @returns {"public"|"reserved"|"accepted"|""} "" only when there is no domain to classify.
|
|
106
88
|
*/
|
|
107
|
-
export function
|
|
89
|
+
export function classifyAddressDomain(domain) {
|
|
108
90
|
const d = String(domain ?? "").trim().toLowerCase();
|
|
109
91
|
if (!d) return "";
|
|
110
|
-
if (isNarrowDomain(d)) return "narrow";
|
|
111
92
|
if (isReservedDomain(d)) return "reserved";
|
|
112
93
|
if (PUBLIC_EMAIL_DOMAINS.has(d)) return "public";
|
|
113
|
-
if (PUBLIC_EMAIL_FAMILIES.has(d.slice(0, d.indexOf(".")))) return "public";
|
|
114
|
-
return "
|
|
94
|
+
if (d.includes(".") && PUBLIC_EMAIL_FAMILIES.has(d.slice(0, d.indexOf(".")))) return "public";
|
|
95
|
+
return "accepted";
|
|
115
96
|
}
|
|
116
97
|
|
|
117
98
|
/**
|
|
118
|
-
*
|
|
99
|
+
* Why this address cannot be the one that signs in, or null when it can.
|
|
119
100
|
*
|
|
120
|
-
*
|
|
121
|
-
*
|
|
122
|
-
* every brand owner. A consent prompt phrased any other way asks about one thing and shows another.
|
|
101
|
+
* NAMES THE SETTING, because the reader's next act is to change it: the address is `PORTAL_LOCAL_USER`
|
|
102
|
+
* whether it arrived from setup's question, from `--user` or from the environment file.
|
|
123
103
|
*/
|
|
124
|
-
export function
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
}
|
|
128
|
-
|
|
129
|
-
/**
|
|
130
|
-
* Why this domain cannot become a staff rule, and what to do instead.
|
|
131
|
-
*
|
|
132
|
-
* Returns null for a domain that CAN — `narrow` and `wide` are not refusals, and `wide` is answered by
|
|
133
|
-
* the confirmation the caller runs, not by this function.
|
|
134
|
-
*/
|
|
135
|
-
export function staffDomainRefusal(domain, { variable = "PORTAL_STAFF_DOMAINS" } = {}) {
|
|
136
|
-
const d = String(domain ?? "").trim().toLowerCase();
|
|
137
|
-
const verdict = classifyStaffDomain(d);
|
|
104
|
+
export function addressRefusal(email) {
|
|
105
|
+
const d = domainOfEmail(email);
|
|
106
|
+
const verdict = classifyAddressDomain(d);
|
|
138
107
|
if (verdict === "public") {
|
|
139
|
-
return `${d} is a public email provider
|
|
140
|
-
+
|
|
141
|
-
+
|
|
142
|
-
+ "domain
|
|
143
|
-
+ "
|
|
144
|
-
+ "address as <account>@localhost.";
|
|
108
|
+
return `${d} is a public email provider. The address that signs in here becomes the first person on `
|
|
109
|
+
+ "this install, with access to everything, and a personal mailbox is not an address your "
|
|
110
|
+
+ "organisation controls. Refusing it.\n"
|
|
111
|
+
+ " Use an address at your organisation's own domain as PORTAL_LOCAL_USER, or, if this machine is "
|
|
112
|
+
+ "only yours, the local-account form <account>@localhost.";
|
|
145
113
|
}
|
|
146
114
|
if (verdict === "reserved") {
|
|
147
|
-
return `${d} is reserved for documentation and receives no real mail (RFC 2606), so
|
|
148
|
-
+ "address somebody typed as a placeholder rather than one that signs in. Refusing
|
|
149
|
-
+ "
|
|
150
|
-
+ "
|
|
151
|
-
+ "if this machine is only yours.";
|
|
115
|
+
return `${d} is reserved for documentation and receives no real mail (RFC 2606), so this is an `
|
|
116
|
+
+ "address somebody typed as a placeholder rather than one that signs in. Refusing it.\n"
|
|
117
|
+
+ " Set PORTAL_LOCAL_USER to the address you actually sign in with, or to the local-account form "
|
|
118
|
+
+ "<account>@localhost if this machine is only yours.";
|
|
152
119
|
}
|
|
153
120
|
return null;
|
|
154
121
|
}
|
|
@@ -2,8 +2,8 @@
|
|
|
2
2
|
// Copyright 2026 Cordillera Sàrl. Additional terms under section 7 of the AGPL-3.0 apply — see ADDITIONAL-TERMS.md
|
|
3
3
|
// THE SUMMARY'S BLOCK GRAMMAR — one definition, because it has four readers.
|
|
4
4
|
//
|
|
5
|
-
//
|
|
6
|
-
//
|
|
5
|
+
// Decided 2026-08-31: keep the length and add the structure, so long as length stays
|
|
6
|
+
// broadly consistent. The assess seat now writes sub-headers and bullets inside the
|
|
7
7
|
// summary it emits, and FOUR surfaces read that string back:
|
|
8
8
|
//
|
|
9
9
|
// driver/publish/render-knockout.mjs the per-mark report and the grouped HTML
|
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
//
|
|
6
6
|
// ── ONE DEFINITION, BECAUSE THE SECOND CALLER HAD NONE ───────────────────────────────────────────────
|
|
7
7
|
//
|
|
8
|
-
// `connect` learned this the expensive way
|
|
8
|
+
// `connect` learned this the expensive way: its `systemctl` calls ran with
|
|
9
9
|
// `stdio: "ignore"`, so systemd's own explanation was thrown away before anyone could read it and the
|
|
10
10
|
// whole output was `connect: Command failed: systemctl --user daemon-reload`; and the session-bus remedy
|
|
11
11
|
// was appended to EVERY failure, so a unit that would not start for a bound port or a bad ExecStart was
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
//
|
|
15
15
|
// `start --background` never learned it. Its `enable --now` loop ran uncaught, so the same refusal
|
|
16
16
|
// arrived as a raw Node stack trace — `at genericNodeError (node:internal/errors:983:15)`, a status
|
|
17
|
-
// code, and no statement of what had happened to the install
|
|
17
|
+
// code, and no statement of what had happened to the install. That is the failure
|
|
18
18
|
// `shared/listen.mjs` was written to end one layer down, and its rule is the rule here: an unrecognised
|
|
19
19
|
// failure still gets a sentence and still exits non-zero; what it must not do is arrive as a stack trace
|
|
20
20
|
// with no statement of consequence.
|
|
@@ -31,7 +31,7 @@ import { existsSync } from "node:fs";
|
|
|
31
31
|
/**
|
|
32
32
|
* Does this failure say the SESSION BUS is missing, rather than anything about the unit?
|
|
33
33
|
*
|
|
34
|
-
* ONE AUTHORITY, because more than one reader asks it (
|
|
34
|
+
* ONE AUTHORITY, because more than one reader asks it (criterion 3). The failure text
|
|
35
35
|
* below offers the bus remedy on a yes, and connect's health reader refuses to translate a yes into "the
|
|
36
36
|
* door is not open" — that mistranslation is the defect, and a second copy of this test is how the two
|
|
37
37
|
* would come to disagree about which failures are bus failures.
|
package/shared/tracked-files.mjs
CHANGED
|
@@ -117,7 +117,7 @@ export function grepTrackedFiles(guard, { root, args }) {
|
|
|
117
117
|
/**
|
|
118
118
|
* The tracked corpus WITH ITS INDEX MODES, or null when this tree has no checkout to read it from.
|
|
119
119
|
*
|
|
120
|
-
* WHY THIS IS HERE RATHER THAN LEFT TO ITS ONE CALLER
|
|
120
|
+
* WHY THIS IS HERE RATHER THAN LEFT TO ITS ONE CALLER. The executable-bits guard
|
|
121
121
|
* needs `ls-files -s`, which carries the mode, and `trackedFiles` above deliberately returns paths
|
|
122
122
|
* only. So that guard called the helper for its skip contract and then spawned git a SECOND time,
|
|
123
123
|
* raw, for the modes — correct, because the raw call sits behind the helper's null check, but
|
package/shared/trigger-lane.mjs
CHANGED
|
@@ -135,7 +135,7 @@ export function triggerLaneVerdict({ url = null, hasToken = false, verbs = null,
|
|
|
135
135
|
message: `the trigger lane answers at ${raw}${probe.status ? ` (${probe.status})` : ""}${challengeNote(challenge)}` };
|
|
136
136
|
}
|
|
137
137
|
const why = probe.error ? probe.error : `it answered ${probe.status}`;
|
|
138
|
-
// ── A DOOR THAT IS NOT LISTENING YET IS NOT A DOOR THAT IS DOWN
|
|
138
|
+
// ── A DOOR THAT IS NOT LISTENING YET IS NOT A DOOR THAT IS DOWN ───────────────────────────────────
|
|
139
139
|
//
|
|
140
140
|
// On a simultaneous restart of the unit set the portal binds before the engine door does — the units
|
|
141
141
|
// carry no ordering between them — and this branch logged an OUTAGE in the present indicative, with a
|
package/shared/tty-style.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
|
// tty-style.mjs — weight where the meaning is, and NOTHING when the reader is not a terminal.
|
|
4
4
|
//
|
|
5
|
-
// ── why this exists (
|
|
5
|
+
// ── why this exists (ruling 2026-08-31) ────────────────────────────────────
|
|
6
6
|
//
|
|
7
7
|
// "new terminals can do things like bold and colours etc — good formatting impresses people because it's
|
|
8
8
|
// made clear to them someone put the time in." That is a product argument, not a decorative one: setup
|
package/shared/usage-block.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
|
// ── The synopsis a verb prints for --help, read out of its own header ─────────────────────────────
|
|
4
4
|
//
|
|
5
|
-
//
|
|
5
|
+
// Four verbs printed their LICENCE HEADER as the first line of `--help`:
|
|
6
6
|
//
|
|
7
7
|
// $ npx clearotron doctor --help
|
|
8
8
|
// SPDX-License-Identifier: AGPL-3.0-only
|
package/shared/vacuous-pass.mjs
CHANGED
|
@@ -29,7 +29,7 @@
|
|
|
29
29
|
//
|
|
30
30
|
// The GATED (env-conditional) member named in is NOT here: measured on the same tree, the
|
|
31
31
|
// dominant `if (process.env.X …)` idiom is save/restore teardown, which guards no assertion and cannot
|
|
32
|
-
// pass vacuously. Ruled out as measured-harmless (
|
|
32
|
+
// pass vacuously. Ruled out as measured-harmless (ruling 2026-08-20) rather than left unbuilt.
|
|
33
33
|
|
|
34
34
|
/**
|
|
35
35
|
* THE REMEDY, at the site. Wrap the set a loop walks:
|
package/shared/verb-shim.mjs
CHANGED
|
@@ -7,7 +7,7 @@
|
|
|
7
7
|
// directory, and got `npm error could not determine executable to run`. Not our error, naming no
|
|
8
8
|
// product, suggesting no fix. `npx` resolves a local package by walking UP from the current directory
|
|
9
9
|
// to find node_modules, so every command this product prints worked only where the reader happened to
|
|
10
|
-
// be standing.
|
|
10
|
+
// be standing. Ruling, 2026-08-26: put the verb on PATH.
|
|
11
11
|
//
|
|
12
12
|
// WHY A PER-USER SHIM AND NOT `npm link`. `npm link` writes to npm's global prefix, which on a default
|
|
13
13
|
// install is `/usr` and refuses without root (recorded, which is why that issue
|