From b1c9667c50a73b2fbb4b1ded038f5eef8b5d1821 Mon Sep 17 00:00:00 2001 From: Michael Heller <21163552+mdheller@users.noreply.github.com> Date: Mon, 3 Aug 2026 15:26:35 -0400 Subject: [PATCH 1/2] =?UTF-8?q?docs(adr):=20ADR-0004=20Crown=20=E2=80=94?= =?UTF-8?q?=20seat=20the=20Telos=20Layer=20over=20the=20Truth=20Engine?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Seat the estate's constitutional layer per the owner's flow diagram (Telos Layer: objectives+constraints -> Truth Engine: falsifiable, auditable), consume-not-fork over already-merged pieces: - Keter (Crown) objective = welfare-annealing WEA-1 (economic-prophet PR #59), NOT control-max; constraints non-domination/consent/dignity. - Da'at = policy weights/thresholds (omnirisk/outcome-pricing, the-assay grades, counter-test-gate); it CANNOT assert truth. - Truth Engine = ts/src/discourse.ts + spec 14 + SILENT epistemic firewall (evidence-intake-kernel #2/#3) + Noetica counter-test detectors (PR #570) + Truth = Law x Evidence. Add scripts/check-constitution.mjs (deterministic, stdlib-only) + a fixtures corpus as the teeth, wired into CI. It enforces the cross-layer invariants no single discourse.ts function sees, both directions: - REJECTS Da'at asserting a POS/true verdict (D1, = firewall affirming-the-consequent guard). - REJECTS a control/domination Keter telos as unconstitutional (K1, SILENT-vs-welfare inversion). - REJECTS a TruthRecord with no TestObligation / no Witness / single-valued+atemporal; ADMITS a falsifiable, witnessed, multi-valued+temporal+adversary-aware record. - Refuses a degenerate corpus so D1/K1 can't be silently removed. Records two follow-up gaps under this constitution: Hygiene runtime (no CTEST runner / no passport producer / LOGFALL id drift) and XSEDE-PEP -> estate program-execution-plan. --- .github/workflows/ci.yml | 2 + .github/workflows/ts-ci.yml | 17 ++ .../0004-crown-telos-truth-constitution.md | 171 +++++++++++++++++ package.json | 1 + scripts/check-constitution.mjs | 180 ++++++++++++++++++ scripts/constitution-fixtures.json | 127 ++++++++++++ 6 files changed, 498 insertions(+) create mode 100644 docs/adr/0004-crown-telos-truth-constitution.md create mode 100644 scripts/check-constitution.mjs create mode 100644 scripts/constitution-fixtures.json diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 73289e6..f1f451b 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -38,6 +38,8 @@ jobs: test -f PROVENANCE.md test -f NOTICE.md test -f docs/adr/0001-hellgraph-positioning.md + test -f docs/adr/0004-crown-telos-truth-constitution.md + test -f scripts/check-constitution.mjs test -f docs/audit/SPEC_TRACEABILITY.md test -f docs/audit/KERNEL_SEAM_AUDIT.md diff --git a/.github/workflows/ts-ci.yml b/.github/workflows/ts-ci.yml index bf9f5ad..5d4b019 100644 --- a/.github/workflows/ts-ci.yml +++ b/.github/workflows/ts-ci.yml @@ -152,3 +152,20 @@ jobs: run: npm install --no-audit --no-fund - name: Verify vendored KKO provenance + generated kko-data.ts run: npm run check:kko + + # ── Constitutional conformance (ADR-0004, the Crown) ──────────────────────────────── + # Seats the Telos Layer over the Truth Engine: Keter (welfare-annealing objective, NOT + # control-max), Da'at (policy weights/thresholds, CANNOT assert truth), Truth Engine + # (falsifiable + witnessed + multi-valued/temporal/adversary-aware). The checker fires the + # cross-layer invariants that no single discourse.ts function sees — esp. "Da'at cannot assert + # truth" (D1) and "a control-telos is unconstitutional" (K1). Deterministic + stdlib only (no + # deps, no install, no path filter) so the boundary can never regress unverified. + constitution: + runs-on: [self-hosted, linux, x64] + steps: + - uses: actions/checkout@v4 + - uses: actions/setup-node@v4 + with: + node-version: 20 + - name: Verify constitutional invariants (ADR-0004 teeth) + run: npm run check:constitution diff --git a/docs/adr/0004-crown-telos-truth-constitution.md b/docs/adr/0004-crown-telos-truth-constitution.md new file mode 100644 index 0000000..f70a7d8 --- /dev/null +++ b/docs/adr/0004-crown-telos-truth-constitution.md @@ -0,0 +1,171 @@ +# ADR-0004: The Crown — the Telos Layer is seated over the Truth Engine as a constitution + +## Status + +Accepted. + +## Context + +The estate now has all the constitutional pieces, built and merged, but nothing that seats +them as a **single ordered layer** with teeth. The owner's flow diagram states the order +plainly: + +> **Telos Layer (Divine Plan as Objectives + Constraints) → Truth Engine (Falsifiable, +> Auditable)** + +Two layers, one direction, one boundary. The Telos Layer sets *what we are for* (objectives) +and *what we will not do* (constraints); the Truth Engine determines *what is so* (falsifiable, +witnessed, auditable). The boundary is the whole point: **the layer that sets objectives cannot +be the layer that asserts truth.** A system whose objectives can also declare their own success +is not falsifiable — it is a control loop wearing a truth costume. + +HellGraph already owns the Truth Engine implementation (`ts/src/discourse.ts`: +Artifact/Claim/Warrant/Evidence/Attestation/TestObligation/TruthRecord) and its alignment +contract (`docs/specs/14_Truth_Engine_Discourse_Integration_v0_1.md`, which already names the +three-layer structure and the `Telos ≠ Truth` invariant). What is missing is the *constitutional* +statement — the one that (a) says which existing, merged pieces ARE Keter, Da'at, and the Truth +Engine, and (b) encodes the cross-layer invariants as a check that goes red when the ordering is +violated. This ADR is that statement; `scripts/check-constitution.mjs` is its teeth. + +This is a **consume-not-fork seating**: it references the merged pieces by repo/PR and asserts +the boundary between them. It does not re-implement or edit them. + +## Decision + +The estate is governed as a constitution with two seated layers and one non-negotiable boundary. + +### Keter (Crown) — the objective and its constraints + +The Crown objective is **"intelligence serves human flourishing"**, under the constraints +**non-domination, consent, dignity**. This is not a slogan; it is a specific, already-built +objective function: the **welfare-annealing objective** — +`economic-prophet welfare_annealing/` **WEA-1 (PR #59)**, the welfare-max-over-conserved-energy +model. It is explicitly **NOT a control-max objective**. The live objective data is served from +`prophet-platform/apps/agentic-os-api/app/data.py`. + +Constitutional consequence: a Keter objective that maximises **control / domination / power / +compliance** rather than flourishing is the **SILENT-vs-welfare inversion**, and it is +**unconstitutional**. A control-max telos is void regardless of how well-formed the rest of the +record is. + +### Da'at (Knowledge) — the policy interface that sets weights, never truth + +Da'at is the policy interface: *what counts as acceptable proof, which harms raise the +burden of proof.* It **sets weights and thresholds** and it **CANNOT assert truth**. It is +seated on the already-merged policy surfaces: + +- the **omnirisk / outcome-pricing** policy weights, +- **the-assay** grade thresholds, +- the **counter-test-gate**. + +Constitutional consequence: a Da'at policy/weight record that manufactures a **POS / true** +verdict is void. This is the **same rule** as the SILENT epistemic firewall's +affirming-the-consequent guard — **evidence-intake-kernel #3** (merged this session): policy may +raise the burden of proof, weight it, and gate on it, but a threshold is not a verdict and a +weight is not a witness. Da'at sets weights only. + +### Truth Engine — falsifiable, witnessed, auditable; the only layer that asserts truth + +The Truth Engine is the flow +**Artifact → Claim → Test-Obligation → Witness/Attestation → Truth Record** +(multi-valued, temporal, adversary-aware), looping Record → Claim. It is seated on: + +- `hellgraph/ts/src/discourse.ts` (the atom schema + `assertClaim`/`recordTruth` bindings) and + spec 14, +- the **SILENT epistemic firewall** — evidence-intake-kernel **#2 / #3** (merged this session), +- the **Noetica counter-test detectors** — **PR #570** (merged), +- **Truth = Law × Evidence** — a verdict is admissible only as the product of an admissible + policy frame (Law, from Da'at) and admitted evidence (never Law alone). + +`discourse.ts` already enforces the *intra-record* laws structurally: `assertClaim` rejects a +claim with no refutation channel; `recordTruth` rejects a verdict with no witness/attestation or +no causal cut. This ADR adds the *cross-layer* constitutional laws that no single function sees. + +### The map (seat, do not reinvent) + +| Constitutional role | Merged piece being seated | Home | +|---|---|---| +| **Keter** objective — flourishing, not control | welfare-annealing WEA-1 (PR #59); live data `app/data.py` | economic-prophet / prophet-platform | +| **Da'at** — weights & thresholds, no truth | omnirisk/outcome-pricing weights · the-assay grades · counter-test-gate | prophet-platform / estate | +| **Truth Engine** — falsifiable/witnessed/auditable | `discourse.ts` + spec 14 · SILENT firewall (eik #2/#3) · Noetica counter-test (PR #570) · Truth = Law × Evidence | hellgraph / Noetica / eik | +| **Runtime that will produce Bias/Calibration Passports** | epistemic-governance Hygiene standard, ruleset **1.3.0** | sociosphere `standards/epistemic-governance/` | +| **Bias catalog cross-reference** | cognitive-bias catalog (set-1 backlog) | Noetica | + +## Constitutional invariants (the teeth) + +Encoded in `scripts/check-constitution.mjs` against the corpus +`scripts/constitution-fixtures.json`. Each invariant is checked in **both directions** — the +things it must admit are admitted, the things it must void are voided, *and for the declared +reason*. A void that fires for the wrong invariant fails as loudly as a missed void. + +**VERIFIES (admits):** +- A TruthRecord that carries a **TestObligation** (refutation channel) **and** a + **Witness/Attestation** (provenance + independence) **and** is **multi-valued + temporal + + adversary-aware** is admitted (`T-admits`). +- A Keter record whose maximand is flourishing/welfare under the non-domination/consent/dignity + constraints is admitted (`K1`). +- A Da'at record that sets weights/thresholds and asserts no verdict is admitted (`D1`). + +**REJECTS (voids):** +- **`D1` — Da'at cannot assert truth.** A policy/weight record that manufactures a POS/true + verdict is void (same rule as the firewall's affirming-the-consequent guard, eik #3). +- **`K1` — a control-telos is unconstitutional.** A Keter objective that is a + control/domination objective rather than the flourishing/welfare objective (the + SILENT-vs-welfare inversion) is void. +- **`K2`** — a flourishing objective that strips the required constraints is void. +- **`T1`** — a TruthRecord with **no TestObligation** is unfalsifiable → void (mirrors the + Phase-0 counter-test gate; `discourse.ts assertClaim`). +- **`T2`** — a TruthRecord with **no Witness** is void (mirrors `discourse.ts recordTruth`, + ≥1 attestation). +- **`T3`** — a **single-valued / atemporal / non-adversary-aware** TruthRecord is void where the + constitution requires multi-valued + temporal + adversary-aware. + +The checker also refuses a **degenerate corpus**: the suite must exercise both directions and +must fire both `D1` ("Da'at cannot assert truth") and `K1` ("control-telos is unconstitutional"), +so the two headline teeth can never be quietly removed while the check still reports green. + +## Rationale + +- **The ordering is the safety property.** Objectives that can grade their own truth are the + definition of an unfalsifiable, self-confirming system. Seating Telos strictly *above* an + auditable Truth Engine — and encoding the boundary as a check — is what makes "aligned" + falsifiable rather than asserted. +- **Seat, don't reinvent.** Every piece already exists and is merged. The constitutional value + is in *naming which piece is which layer* and *asserting the boundary between them*, not in new + machinery. The validator is deterministic, stdlib-only `.mjs` (no `tsx`, no deps) so it is + cheap and non-negotiable in CI — the same doctrine as the KKO provenance guard. +- **The teeth mirror `discourse.ts`, one level up.** `discourse.ts` guards a single record; + this guards the layering. `T1`/`T2` deliberately restate `assertClaim`/`recordTruth` so the + constitution and the runtime cannot silently disagree. + +## Consequences + +- A control-max objective, or a policy record that asserts truth, is a **constitutional + violation** and fails CI, not a code-review nicety. +- The Truth Record cardinality question left open in spec 14 is settled *for constitutional + purposes*: an admitted record must be multi-valued (a verdict *space*, not a single boolean), + temporal (a `ts`), and adversary-aware — the 3-valued POS/ZERO/NEG + causal-cut + + tamper-detect shape already in `discourse.ts` satisfies it. +- Two runtime gaps are recorded below as follow-up issues for @mdheller; they are seated **under** + this constitution rather than left ambient. + +## Follow-up issues (filed, seated under this constitution) + +1. **Hygiene runtime is standard-rich / runtime-poor.** The epistemic-governance standard + (ruleset 1.3.0) has no **CTEST runner**, no **bias-passport / calibration-passport producer**, + and there is an **id-namespace drift** (Noetica `LOGFALL.ADHOMINEM.V1` 0.1.0 vs standard + `LOGFALL.ADHOM.V2` 1.3.0). Until these exist, `T1`'s refutation channels and the passport + credential are specified but not produced at runtime. +2. **XSEDE-PEP → estate program-execution-plan.** Promote the XSEDE-PEP program-execution-plan + into the estate PEP, seated under this constitution (objectives from Keter, verdicts from the + Truth Engine, weights from Da'at). + +## References + +- `docs/specs/14_Truth_Engine_Discourse_Integration_v0_1.md` — three-layer alignment contract. +- `ts/src/discourse.ts` — Truth-Engine atom schema + `assertClaim`/`recordTruth`. +- `scripts/check-constitution.mjs`, `scripts/constitution-fixtures.json` — the teeth. +- Merged pieces seated (consume-not-fork): welfare-annealing WEA-1 (economic-prophet PR #59); + SILENT epistemic firewall (evidence-intake-kernel #2/#3); Noetica counter-test detectors + (PR #570); the-assay grades / omnirisk weights / counter-test-gate; epistemic-governance + Hygiene standard 1.3.0. diff --git a/package.json b/package.json index 51ca6c6..aabd620 100644 --- a/package.json +++ b/package.json @@ -25,6 +25,7 @@ "test": "node --import tsx --test ts/src/*.test.ts", "check:dist": "tsup && git diff --quiet -- ts/dist || (echo 'ERROR: ts/dist is stale vs ts/src \u2014 run: npm run build, then commit ts/dist' && exit 1)", "check:kko": "node --import tsx scripts/check-kko-provenance.mjs", + "check:constitution": "node scripts/check-constitution.mjs", "check:safe-dict": "node scripts/check-safe-dict.mjs" }, "optionalDependencies": { diff --git a/scripts/check-constitution.mjs b/scripts/check-constitution.mjs new file mode 100644 index 0000000..9a0ae98 --- /dev/null +++ b/scripts/check-constitution.mjs @@ -0,0 +1,180 @@ +#!/usr/bin/env node +/** + * Constitutional conformance guard — the teeth for ADR-0004 (the Crown ADR). + * + * ── Why this exists ────────────────────────────────────────────────────────────────── + * ADR-0004 seats the estate's Telos Layer over the Truth Engine as a constitution: + * · Keter (Crown) sets ONE objective — intelligence serves human flourishing — under the + * non-domination / consent / dignity constraints. It is the welfare-annealing objective + * (economic-prophet welfare_annealing WEA-1, PR #59), NOT a control-max objective. + * · Da'at (Knowledge) sets the policy interface — acceptable-proof weights, harm-raises- + * burden thresholds, the-assay grades, the counter-test gate. It sets WEIGHTS ONLY and + * MUST NOT assert truth. + * · The Truth Engine (ts/src/discourse.ts) is the ONLY layer that asserts truth, and only + * through a falsifiable, witnessed, multi-valued/temporal/adversary-aware TruthRecord. + * + * discourse.ts already enforces the intra-record laws structurally: assertClaim rejects a + * claim with no refutation channel; recordTruth rejects a verdict with no witness/attestation + * or no causal cut. This checker enforces the CONSTITUTIONAL (cross-layer) laws that no single + * TypeScript function sees: that a Keter objective is the flourishing objective and not a + * domination one, that a Da'at policy record never smuggles in a truth verdict (the same rule + * as the SILENT epistemic firewall's affirming-the-consequent guard — evidence-intake-kernel + * #3, merged), and that an admitted TruthRecord is falsifiable, witnessed, and multi-valued/ + * temporal/adversary-aware. + * + * ── Teeth both directions ──────────────────────────────────────────────────────────── + * The corpus scripts/constitution-fixtures.json declares, per record, whether the + * constitution must `admit` or `void` it and which invariant (`teeth`) must decide it. A green + * run proves the invariants fire in BOTH directions — the admits admitted AND the voids voided + * for the STATED reason. A void that fired for the wrong reason FAILS as loudly as an admit + * that should have been voided: the corpus is data, the invariants are logic, and neither + * validates itself (cf. the KKO guard's self-validation note). + * + * Deterministic + stdlib only. Read-only. Plain .mjs — runs on any Node, no tsx, no deps. + * + * Run: node scripts/check-constitution.mjs + */ +import { readFileSync } from 'node:fs' +import { fileURLToPath } from 'node:url' + +const url = (p) => fileURLToPath(new URL(p, import.meta.url)) +const FIXTURES = url('./constitution-fixtures.json') + +const VERDICTS = new Set(['POS', 'ZERO', 'NEG']) // ts/src/codex.ts: Verdict = 'POS'|'ZERO'|'NEG' + +/** + * A ruling is { ok } for admit, or { void: , reason } for void. Each `judge` + * returns the FIRST invariant that voids the record, so a fixture's expected `teeth` is + * checked against the invariant that actually fired — not merely admit-vs-void. + */ + +// ── Keter (Crown): the objective is flourishing/welfare under its constraints ────────── +// A control/domination maximand is the SILENT-vs-welfare inversion — a control-max telos is +// unconstitutional. This is the seating of welfare_annealing WEA-1 (welfare-max over conserved +// energy) as THE Crown objective; anything maximising control/power/domination is void. +const FLOURISHING = new Set(['human_flourishing', 'welfare', 'flourishing']) +const DOMINATION = new Set(['control', 'domination', 'power', 'coercion', 'compliance']) +const REQUIRED_CONSTRAINTS = ['non-domination', 'consent', 'dignity'] + +function judgeKeter(r) { + const maximand = String(r.maximand ?? r.objective ?? '') + if (DOMINATION.has(maximand) || r.objective === 'control') + return { void: 'K1-flourishing-telos', + reason: `Keter objective maximises "${maximand}" — a control/domination telos. The Crown ` + + `objective is human flourishing (welfare-annealing WEA-1), never control-max. Unconstitutional.` } + if (!FLOURISHING.has(maximand)) + return { void: 'K1-flourishing-telos', + reason: `Keter objective "${maximand}" is not the flourishing/welfare objective the constitution seats.` } + const have = new Set(r.constraints ?? []) + const missing = REQUIRED_CONSTRAINTS.filter((c) => !have.has(c)) + if (missing.length) + return { void: 'K2-constraints-present', + reason: `Keter objective drops required constraint(s): ${missing.join(', ')}.` } + return { ok: true, teeth: 'K1-flourishing-telos' } +} + +// ── Da'at (Knowledge): sets weights & thresholds, CANNOT assert truth ────────────────── +// The firewall rule: a policy/weight record that manufactures a truth verdict is affirming the +// consequent (eik#3). Policy gates and weights the burden of proof; it never writes truth. +const TRUTH_BEARING_FIELDS = ['verdict', 'asserts', 'truth', 'proven', 'truthRecord'] + +function judgeDaat(r) { + for (const f of TRUTH_BEARING_FIELDS) { + if (r[f] === undefined) continue + // A "false"/"deny" value is a gate outcome, not a truth assertion; an affirmed truth is the violation. + const v = r[f] + const affirmsTruth = VERDICTS.has(v) ? v === 'POS' + : (v === true || v === 'true' || v === 'proven' || (typeof v === 'object' && v !== null)) + if (affirmsTruth) + return { void: 'D1-daat-cannot-assert-truth', + reason: `Da'at record carries an asserted truth (${f}=${JSON.stringify(v)}). Policy sets ` + + `weights/thresholds only — it MUST NOT assert truth (affirming-the-consequent guard, eik#3).` } + } + if (r.sets !== 'weights' && r.sets !== 'thresholds') + return { void: 'D2-daat-sets-weights-only', + reason: `Da'at record must declare it sets "weights" or "thresholds"; got sets=${JSON.stringify(r.sets)}.` } + return { ok: true, teeth: 'D1-daat-cannot-assert-truth' } +} + +// ── Truth Engine: falsifiable + witnessed + multi-valued/temporal/adversary-aware ────── +function judgeTruth(r) { + if (!r.testObligation || String(r.testObligation).trim() === '') + return { void: 'T1-test-obligation-required', + reason: 'TruthRecord has no TestObligation (refutation channel). Unfalsifiable -> void ' + + '(Phase-0 counter-test gate; discourse.ts assertClaim rejects a claim with no refutation channel).' } + if (!Array.isArray(r.attestations) || r.attestations.length === 0) + return { void: 'T2-witness-required', + reason: 'TruthRecord has no Witness/Attestation. Unbacked verdict -> void ' + + '(discourse.ts recordTruth requires >=1 attestation).' } + const multiValued = Array.isArray(r.verdictSpace) && r.verdictSpace.length > 1 + const temporal = typeof r.ts === 'string' && r.ts.length > 0 + const adversaryAware = r.adversaryAware === true + if (!(multiValued && temporal && adversaryAware)) { + const missing = [ + !multiValued ? 'multi-valued (verdictSpace)' : null, + !temporal ? 'temporal (ts)' : null, + !adversaryAware ? 'adversary-aware' : null, + ].filter(Boolean) + return { void: 'T3-multivalued-temporal-adversary', + reason: `TruthRecord is not ${missing.join(' / ')}. The constitution requires multi-valued + ` + + `temporal + adversary-aware.` } + } + if (r.verdict !== undefined && !VERDICTS.has(r.verdict)) + return { void: 'T3-multivalued-temporal-adversary', + reason: `TruthRecord verdict ${JSON.stringify(r.verdict)} is outside the {POS,ZERO,NEG} space.` } + return { ok: true, teeth: 'T-admits' } +} + +// ── Run the corpus: every fixture must be decided as declared, by the declared invariant ── +const doc = JSON.parse(readFileSync(FIXTURES, 'utf8')) +const layers = [ + ['keter', judgeKeter], + ['daat', judgeDaat], + ['truth', judgeTruth], +] + +const failures = [] +let checked = 0 +for (const [layer, judge] of layers) { + for (const r of doc[layer] ?? []) { + checked++ + const ruling = judge(r) + const admitted = ruling.ok === true + const wantAdmit = r.expect === 'admit' + if (admitted !== wantAdmit) { + failures.push(admitted + ? `[${layer}] ${r.id}: constitution ADMITTED a record it must VOID (expected teeth ${r.teeth}).` + : `[${layer}] ${r.id}: constitution VOIDED a record it must ADMIT — ${ruling.reason}`) + continue + } + // Direction agreed; now the reason must too — a void for the wrong invariant is still wrong. + const firedTeeth = admitted ? ruling.teeth : ruling.void + if (r.teeth && firedTeeth !== r.teeth) { + failures.push(`[${layer}] ${r.id}: decided by ${firedTeeth} but fixture pins ${r.teeth}` + + (ruling.reason ? ` (${ruling.reason})` : '')) + } + } +} + +if (failures.length) { + console.error('✗ constitutional conformance FAILED — the teeth did not bite as declared:') + for (const f of failures) console.error(` · ${f}`) + process.exit(1) +} + +// Structural assurance that the corpus actually exercises both directions AND the two +// headline invariants — a suite of all-admits (or one that never runs D1/K1) would pass +// vacuously while claiming to guard the constitution. +const all = layers.flatMap(([l]) => (doc[l] ?? []).map((r) => ({ ...r, _layer: l }))) +const admits = all.filter((r) => r.expect === 'admit').length +const voids = all.filter((r) => r.expect === 'void').length +const firesDaatTruth = all.some((r) => r.expect === 'void' && r.teeth === 'D1-daat-cannot-assert-truth') +const firesControlTelos = all.some((r) => r.expect === 'void' && r.teeth === 'K1-flourishing-telos') +if (admits === 0 || voids === 0 || !firesDaatTruth || !firesControlTelos) { + console.error('✗ constitutional corpus is degenerate: it must exercise both admit and void, and must ' + + "fire both \"Da'at cannot assert truth\" (D1) and \"control-telos is unconstitutional\" (K1).") + process.exit(1) +} + +console.log(`✓ constitution upheld — ${checked} records: ${admits} admitted, ${voids} voided, each by its ` + + `declared invariant. Da'at-cannot-assert-truth (D1) and control-telos-unconstitutional (K1) both fired.`) diff --git a/scripts/constitution-fixtures.json b/scripts/constitution-fixtures.json new file mode 100644 index 0000000..c08034d --- /dev/null +++ b/scripts/constitution-fixtures.json @@ -0,0 +1,127 @@ +{ + "_comment": [ + "Constitutional conformance corpus for scripts/check-constitution.mjs (ADR-0004).", + "Data, not logic — the checker is scripts/check-constitution.mjs; these are the", + "records it must admit or void. Every fixture declares its own `expect` (admit|void)", + "and the `teeth` (invariant id) that must decide it, so the suite proves the", + "invariants fire in BOTH directions: a green run means the admits admitted AND the", + "voids voided for the stated reason. See ADR-0004 for the seating rationale.", + "Layers: keter = objective+constraints (Crown), daat = policy weights/thresholds", + "(Knowledge), truth = TruthRecord (Truth Engine). Only the Truth Engine asserts truth." + ], + + "keter": [ + { + "id": "keter/welfare-annealing/WEA-1", + "note": "Seats economic-prophet welfare_annealing WEA-1 (PR #59): welfare-max over conserved energy. Live objective data: prophet-platform apps/agentic-os-api/app/data.py.", + "expect": "admit", + "teeth": "K1-flourishing-telos", + "objective": "welfare", + "maximand": "human_flourishing", + "conserves": "energy", + "constraints": ["non-domination", "consent", "dignity"] + }, + { + "id": "keter/control-max/INVERSION", + "note": "SILENT-vs-welfare inversion: a control-max / domination telos. Unconstitutional.", + "expect": "void", + "teeth": "K1-flourishing-telos", + "objective": "control", + "maximand": "control", + "constraints": ["non-domination", "consent", "dignity"] + }, + { + "id": "keter/welfare-no-constraints", + "note": "Flourishing maximand but strips the non-domination/consent/dignity constraints.", + "expect": "void", + "teeth": "K2-constraints-present", + "objective": "welfare", + "maximand": "human_flourishing", + "constraints": ["consent"] + } + ], + + "daat": [ + { + "id": "daat/omnirisk-weights", + "note": "Da'at policy interface: omnirisk/outcome-pricing weights + the-assay grade thresholds + counter-test-gate. Sets weights & thresholds only.", + "expect": "admit", + "teeth": "D1-daat-cannot-assert-truth", + "sets": "weights", + "weights": { "harm_burden_multiplier": 3.0, "assay_grade_threshold": "B", "counter_test_required": true } + }, + { + "id": "daat/asserts-pos-verdict", + "note": "Da'at record that manufactures a POS/true verdict — affirming-the-consequent guard (eik#3 firewall). Policy sets weights, never asserts truth.", + "expect": "void", + "teeth": "D1-daat-cannot-assert-truth", + "sets": "weights", + "weights": { "assay_grade_threshold": "B" }, + "verdict": "POS" + }, + { + "id": "daat/asserts-truth-flag", + "note": "Same firewall rule via an `asserts` field: a policy record cannot carry an asserted truth value.", + "expect": "void", + "teeth": "D1-daat-cannot-assert-truth", + "sets": "thresholds", + "asserts": "true" + } + ], + + "truth": [ + { + "id": "truth/well-formed", + "note": "TruthRecord that carries a TestObligation (refutation channel) + a Witness/Attestation (provenance+independence), multi-valued + temporal + adversary-aware. Admitted.", + "expect": "admit", + "teeth": "T-admits", + "claimId": "claim:example-1", + "verdict": "NEG", + "testObligation": "ctest:codex-reverify:claim-example-1", + "attestations": ["witness:independent-1"], + "cut": { "frame": "read-cut@t0" }, + "verdictSpace": ["POS", "ZERO", "NEG"], + "ts": "2026-08-03T00:00:00Z", + "adversaryAware": true + }, + { + "id": "truth/no-test-obligation", + "note": "Unfalsifiable -> void (mirrors the Phase-0 counter-test gate; discourse.ts assertClaim rejects a claim with no refutation channel).", + "expect": "void", + "teeth": "T1-test-obligation-required", + "claimId": "claim:example-2", + "verdict": "POS", + "attestations": ["witness:independent-1"], + "cut": { "frame": "read-cut@t0" }, + "verdictSpace": ["POS", "ZERO", "NEG"], + "ts": "2026-08-03T00:00:00Z", + "adversaryAware": true + }, + { + "id": "truth/no-witness", + "note": "No Witness/Attestation -> void (mirrors discourse.ts recordTruth: >=1 attestation required; unbacked verdict rejected).", + "expect": "void", + "teeth": "T2-witness-required", + "claimId": "claim:example-3", + "verdict": "POS", + "testObligation": "ctest:codex-reverify:claim-example-3", + "attestations": [], + "cut": { "frame": "read-cut@t0" }, + "verdictSpace": ["POS", "ZERO", "NEG"], + "ts": "2026-08-03T00:00:00Z", + "adversaryAware": true + }, + { + "id": "truth/single-valued-atemporal", + "note": "Single-valued + atemporal + not adversary-aware where the constitution requires multi-valued + temporal + adversary-aware -> void.", + "expect": "void", + "teeth": "T3-multivalued-temporal-adversary", + "claimId": "claim:example-4", + "verdict": "POS", + "testObligation": "ctest:codex-reverify:claim-example-4", + "attestations": ["witness:independent-1"], + "verdictSpace": ["true"], + "adversaryAware": false + } + ] +} From 8f7263cefd8cb13fe9f437712de43f1c35d1abd7 Mon Sep 17 00:00:00 2001 From: Michael Heller <21163552+mdheller@users.noreply.github.com> Date: Mon, 3 Aug 2026 16:50:56 -0400 Subject: [PATCH 2/2] ci(crown): run build-and-verify-dist + constitution on ubuntu-latest (self-maintaining) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit build-and-verify-dist is the only REQUIRED check on main and was pinned to the self-hosted pool, which is currently online-but-not-dispatching (idle runners, queued jobs) — stalling every PR incl. this constitutional ADR. It is a plain node build (checkout/setup-node@20/npm install+typecheck+test+build+dist-verify) with no self-hosted dependency, so it runs identically on GitHub-hosted runners and is no longer blocked by the runner backlog. Same for the stdlib constitution validator. This is the Day-2 self-maintaining fix: deterministic required checks must not depend on a flaky pool. --- .github/workflows/ts-ci.yml | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/.github/workflows/ts-ci.yml b/.github/workflows/ts-ci.yml index 5d4b019..9b10146 100644 --- a/.github/workflows/ts-ci.yml +++ b/.github/workflows/ts-ci.yml @@ -30,7 +30,7 @@ permissions: jobs: build-and-verify-dist: - runs-on: [self-hosted, linux, x64] + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 with: @@ -161,7 +161,7 @@ jobs: # truth" (D1) and "a control-telos is unconstitutional" (K1). Deterministic + stdlib only (no # deps, no install, no path filter) so the boundary can never regress unverified. constitution: - runs-on: [self-hosted, linux, x64] + runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 - uses: actions/setup-node@v4