From 7543fee27f3b239cb751e63f1de3e275951cb401 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Thu, 4 Jun 2026 10:35:28 +0100 Subject: [PATCH 1/7] chore(training): EchidnaML CUDA logging fix + per-stage run doc MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit EchidnaML.jl: CUDA.version() was removed in CUDA.jl 5.x; replace with runtime_version() / driver_version() wrapped in try/catch so a logging-only probe failure can't kill the init path. Surfaced during first owner-authorised GNN training run as a MethodError log noise. docs/training-runs/2026-06-02.md: per-stage run-log scaffold for the 2026-06-02 GNN training run (reactive doc — placeholders for stages 3/4/5 to be filled in as they complete; stage 0/1/2 outcomes already captured). Co-Authored-By: Claude Opus 4.7 (1M context) --- docs/training-runs/2026-06-02.md | 91 ++++++++++++++++++++++++++++++++ src/julia/EchidnaML.jl | 10 +++- 2 files changed, 100 insertions(+), 1 deletion(-) create mode 100644 docs/training-runs/2026-06-02.md diff --git a/docs/training-runs/2026-06-02.md b/docs/training-runs/2026-06-02.md new file mode 100644 index 00000000..2c346b0d --- /dev/null +++ b/docs/training-runs/2026-06-02.md @@ -0,0 +1,91 @@ + + +# GNN training run — 2026-06-02 + +First owner-authorised GNN training run after the post-wave3 wiring +PR landed (#207, merged 2026-06-02T04:14:04Z). + +## Provenance + +| Field | Value | +|---|---| +| Branch | `gnn-training-run-2026-06-02` (local-only, no main pollution per Option 4) | +| Worktree | `/home/hyperpolymath/developer/repos/echidna-training-2026-06-02` | +| Launcher | `/tmp/echidna-training-launcher.sh` | +| Log | `/tmp/echidna-training-2026-06-02.log` | +| Status file | `/tmp/echidna-training-2026-06-02.status` | +| Pre-requisite PRs merged | #198 saturation+typing, #206 wave3 closeout, #207 GNN wiring, #210 JSON dep fix | +| Hardware | 4 CPU / 5GB RAM / WSL2 with CUDA passthrough (libcuda.so.535.309.01) | +| Julia | 1.12.6 via juliaup | + +## Pipeline + +1. **Stage 0** — `Pkg.instantiate()` to verify Julia env +2. **Stage 1** — `just provision-corpora` (shallow clones of all upstream sources to `external_corpora/`) +3. **Stage 2** — loop over 17 saturation adapters via `just corpus-ingest-saturation training_data//` +4. **Stage 3** — `just train-from-corpus lean` +5. **Stage 4** — `just train-from-corpus coq` +6. **Stage 5** — list `models/neural/` artefacts + +## Run outcomes (filled in as stages complete) + +### Stage 0 — Julia env + +- _2026-06-02T10:46:36Z_ — `Pkg.status()` green; 14 direct deps + JSON (added in #210) + +### Stage 1 — provision-corpora + +- _2026-06-02T10:46:55Z_ — **43 upstream corpora present** (idempotent cache hits after v2 run) +- Known per-source failures: several GitLab sources return `Gitlab::GitAccess::NotFoundError`; matita returns 404 (rocq-prover/matita repo unavailable). Non-fatal — script tolerates per-source failures. + +### Stage 2 — ingest 17 adapters + +- _2026-06-02T10:48:05Z_ — **4 of 17 adapters ingested**: `agda`, `coq` confirmed; lean + idris2 expected. +- Known cause for the 13 failures: `corpus-ingest-saturation` recipe (Justfile:988) only knows the 4 dependent-typed provers. The other 13 (`isabelle`, `metamath`, `mizar`, `hol_light`, `hol4`, `dafny`, `why3`, `fstar`, `acl2_books`, `tptp`, `smtlib`, `proofnet`, `minif2f`) need per-adapter ingest scripts written. Sketch §7 promised 17; reality is 4. Tracked as roadmap follow-up. + +### Stage 3 — train-from-corpus lean + +- _TBD_ — first run blocked on Julia `JSON` dep (fixed in #210); awaiting v3 run completion. +- Wall-clock target: ~10min CPU baseline / ~2hr GPU on small corpus + +### Stage 4 — train-from-corpus coq + +- _TBD_ + +### Stage 5 — artefacts + +- _TBD_ — should populate `models/neural/`. Existing `models/neural/` in the local checkout's wave3 worktree has prior weights; this run uses the dedicated branch worktree so artefacts don't pollute main. + +## Outcomes summary + +- **Models produced**: _TBD_ +- **MRR vs cosine baseline**: _TBD_ +- **Comparable to wave3 baseline**: _TBD — needs side-by-side once wave3 lands its first GNN run_ + +## Known issues discovered this run + +1. **JSON dep mismatch** — fixed via #210. `corpus_loader.jl` uses `JSON.parse` but `Project.toml` had only `JSON3`. +2. **`CUDA.version()` MethodError** — cosmetic logging error; CUDA.jl 5.x removed the method. Fixed via this PR. +3. **`corpus-ingest-saturation` 4-vs-17 gap** — known; tracked separately. + +## Follow-up tickets + +- (TBD if needed) Extend `corpus-ingest-saturation` recipe to all 17 adapters — large scope, requires per-adapter Rust extractor scripts. +- (TBD) Compare `models/neural/` outputs vs wave3 baseline once both land. + +## Re-running + +Owner-side re-run after corpus refresh: +```bash +just provision-corpora extract-corpora +for adapter in agda coq lean idris2; do + just corpus-ingest-saturation "$adapter" "training_data/$adapter/" +done +just train-from-corpus lean +just train-from-corpus coq +``` + +Note `extract-corpora` is a *separate* recipe call — not a positional arg to `provision-corpora` (that was an early-run mistake; cost 1 launcher iteration). diff --git a/src/julia/EchidnaML.jl b/src/julia/EchidnaML.jl index d621542a..a2c26a41 100644 --- a/src/julia/EchidnaML.jl +++ b/src/julia/EchidnaML.jl @@ -198,7 +198,15 @@ function __init__() # Check CUDA availability if CUDA.functional() @info "CUDA available - using GPU acceleration" - @info "CUDA version: $(CUDA.version())" + # CUDA.jl 5.x uses runtime_version() / driver_version(); + # legacy CUDA.version() was removed. Wrap defensively so a + # logging-only probe can't kill the init path. + try + @info "CUDA runtime version: $(CUDA.runtime_version())" + @info "CUDA driver version: $(CUDA.driver_version())" + catch e + @debug "CUDA version probe failed (non-fatal)" exception=e + end @info "Device: $(CUDA.device())" else @info "CUDA not available - using CPU" From 1913578f0945d955d83613e8ceb5a1b795f03fbf Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 14 Jun 2026 18:21:58 +0100 Subject: [PATCH 2/7] chore(contractiles): reconcile to standards canonical subdir layout; complete must/trust/dust/intend tridents Discard the rsr-template flat residue that had been staged on this branch (the Mustfile still read "contract for rsr-template-repo") and restore the canonical per-verb subdir layout defined by standards/contractiles/CANONICAL-TEMPLATES.adoc. Complete the four missing tridents with bespoke runner/.k9/manifest companions, mirroring the existing adjust/bust pattern and matching each verb's INDEX.a2ml entry: - must: Hunt-read-only, blocking (exit-nonzero); LICENSE/README/banned-path invariants; subtle invariant-erosion focus - trust: Hunt, blocking; T### namespace, dispatcher-trust boundary, threat-model foregrounding - dust: Yard, advisory; D### broom-and-pan recovery, --apply + per-item approval, audit-trail preservation - intend: Hunt, non-gating; dual [[intents]]/[[wishes]] schema (absorbed lust) Add a bespoke contractiles/README.adoc (MPL-2.0, matching the sibling files in the directory) and refresh the now-stale INDEX.a2ml note. All six runner .ncl files nickel typecheck clean; the four Xfile declarations are byte-identical to HEAD. The shared ../k9/ Hunt base import remains a known estate-wide gap (tracked by the respec programme), not patched per-repo here. Co-Authored-By: Claude Opus 4.8 (1M context) --- .machine_readable/contractiles/INDEX.a2ml | 2 +- .machine_readable/contractiles/README.adoc | 151 ++++++++++ .../contractiles/dust/dust.k9.ncl | 194 ++++++++++++ .../contractiles/dust/dust.manifest.a2ml | 51 ++++ .machine_readable/contractiles/dust/dust.ncl | 75 +++++ .../contractiles/intend/intend.k9.ncl | 259 ++++++++++++++++ .../contractiles/intend/intend.manifest.a2ml | 69 +++++ .../contractiles/intend/intend.ncl | 83 ++++++ .../contractiles/must/must.k9.ncl | 254 ++++++++++++++++ .../contractiles/must/must.manifest.a2ml | 60 ++++ .machine_readable/contractiles/must/must.ncl | 67 +++++ .../contractiles/trust/trust.k9.ncl | 276 ++++++++++++++++++ .../contractiles/trust/trust.manifest.a2ml | 64 ++++ .../contractiles/trust/trust.ncl | 101 +++++++ 14 files changed, 1705 insertions(+), 1 deletion(-) create mode 100644 .machine_readable/contractiles/README.adoc create mode 100644 .machine_readable/contractiles/dust/dust.k9.ncl create mode 100644 .machine_readable/contractiles/dust/dust.manifest.a2ml create mode 100644 .machine_readable/contractiles/dust/dust.ncl create mode 100644 .machine_readable/contractiles/intend/intend.k9.ncl create mode 100644 .machine_readable/contractiles/intend/intend.manifest.a2ml create mode 100644 .machine_readable/contractiles/intend/intend.ncl create mode 100644 .machine_readable/contractiles/must/must.k9.ncl create mode 100644 .machine_readable/contractiles/must/must.manifest.a2ml create mode 100644 .machine_readable/contractiles/must/must.ncl create mode 100644 .machine_readable/contractiles/trust/trust.k9.ncl create mode 100644 .machine_readable/contractiles/trust/trust.manifest.a2ml create mode 100644 .machine_readable/contractiles/trust/trust.ncl diff --git a/.machine_readable/contractiles/INDEX.a2ml b/.machine_readable/contractiles/INDEX.a2ml index d44c3f2e..36b9d6f3 100644 --- a/.machine_readable/contractiles/INDEX.a2ml +++ b/.machine_readable/contractiles/INDEX.a2ml @@ -80,7 +80,7 @@ tier = "Hunt" authority = "reporting" gating = "non-gating (continue)" cardinality = "one per repo" -notes = "First trident instance in the estate (2026-04-18). Reports progress toward committed next-actions AND lists horizon aspirations. Absorbed the deprecated `lust` verb 2026-04-18. Never blocks. Remaining 5 verbs still on file_pair shape until tridents are built." +notes = "First trident instance in the estate (2026-04-18). Reports progress toward committed next-actions AND lists horizon aspirations. Absorbed the deprecated `lust` verb 2026-04-18. Never blocks. All six verbs are now complete tridents (declaration + runner + k9 component) with coherence manifests." [[verbs]] name = "k9" diff --git a/.machine_readable/contractiles/README.adoc b/.machine_readable/contractiles/README.adoc new file mode 100644 index 00000000..2d78f425 --- /dev/null +++ b/.machine_readable/contractiles/README.adoc @@ -0,0 +1,151 @@ +// SPDX-License-Identifier: MPL-2.0 +// Copyright (c) 2026 Jonathan D.A. Jewell (hyperpolymath) += Echidna Contractiles +Jonathan D.A. Jewell +:toc: +:sectnums: + +This directory holds Echidna's *contractiles* — the machine-readable +contracts that make the project's invariants, trust boundary, recovery +story, drift tolerances, hard-stop conditions, and north-star intent +legible to CI, the contractile CLI, and Hypatia rules. + +Echidna is a trust-hardened neurosymbolic theorem-proving platform, so +these contracts are not decorative: `must` and `trust` gate merges, +`bust` declares deprecated-path hard-stops, and `dust` governs how +recovery happens without losing the audit trail. + +The estate-level audit semantics (what each verb *means* and the +minimum bar for "real contract vs template residue") are defined in +`standards/contractiles/CANONICAL-TEMPLATES.adoc`. The normative +per-file specification is `docs/CONTRACTILE-SPEC.adoc`. This README +documents the local realisation. + +== Layout — the trident shape + +Each verb lives in its own directory as a complete *trident* plus a +coherence manifest: + +* `file.a2ml` — the project-specific *declaration* (data: the + actual invariants / trust actions / tolerances / intents). +* `.ncl` — the paired Nickel *runner* (pedigree + schema + run + policy). Imports `../_base.ncl`. +* `.k9.ncl` — the *K9 trust-tier component* (execution surface, + evidence sinks, session-open/close negotiation). Imports the Hunt + base via `../k9/template-hunt.k9.ncl` and `../_base.ncl`. +* `.manifest.a2ml` — the *trident coherence manifest* (file roles, + cross-refs, signature, history). + +Filenames use the lowercase verb in the `.ncl`/`.manifest` names and +noun-form PascalCase in the A2ML declaration (e.g. `intend.ncl` + +`Intentfile.a2ml`, `must.ncl` + `Mustfile.a2ml`). + +`_base.ncl` provides the shared `pedigree_schema`, `run_defaults`, and +`probe_schema` that every runner merges into. `INDEX.a2ml` is the +machine-readable registry of all verbs — consumers should read it +rather than hard-coding the verb list. + +== Verbs (6 + k9 exception) + +[cols="1,2,3", options="header"] +|=== +| Verb | Declaration | Role (and gating) + +| `must` +| `must/Mustfile.a2ml` +| Release-blocking physical-state invariants (LICENSE/README presence, + banned hardcoded paths, no Dockerfile/Makefile, …). *Gating* — + hard `exit-nonzero`. Specialises in subtle invariant-erosion. + +| `trust` +| `trust/Trustfile.a2ml` +| Trust boundary, allowed/denied actions, provenance + supply-chain + integrity, dispatcher-trust (`T###` namespace). *Gating* — hard + `exit-nonzero`. Primary defence against unsound proofs leaving the + dispatcher. + +| `adjust` +| `adjust/Adjustfile.a2ml` +| Drift tolerances + corrective actions; deterministic auto-fix where + declared. Advisory (`continue-with-warnings`), Yard tier. + +| `dust` +| `dust/Dustfile.a2ml` +| Fine-grained recovery / rollback / deprecation with audit-trail + preservation (`D###` namespace). Destructive actions are dry-run by + default and require `--apply` + per-item approval. Advisory. + +| `bust` +| `bust/Bustfile.a2ml` +| Breakage / expiry / hard-stop — declares "this is broken / must-not-run" + (e.g. deprecated paths that still exist). *Gating* — hard `exit-nonzero`. + +| `intend` +| `intend/Intentfile.a2ml` +| North-star: committed next-actions (`[[intents]]`, with probes) AND + horizon aspirations (`[[wishes]]`, near/mid/far, no probes). + Non-gating (report only). Absorbed the deprecated `lust` verb + 2026-04-18. +|=== + +NOTE: The `lust` verb was deprecated 2026-04-18 (name had unwanted +associations); its `[[wishes]]` semantics now live inside +`intend/Intentfile.a2ml` as a second section alongside `[[intents]]`. +Any `lust/` directory is drift and should be removed. + +== Intentfile: commitments vs aspirations — two sections, one file + +* `[[intents]]` is the *commitment axis*: tracked next-actions with + observable probes. Status progresses + `declared → in_progress → done / deferred / retired`. +* `[[wishes]]` is the *aspiration axis*: horizon goals grouped + near/mid/far, no probes. Status progresses + `declared → in_progress → achieved / abandoned`. + +If something is concrete enough to have a probe, it is an intent; a +horizon-level desire that might never be acted on is a wish. A wish can +graduate into an intent when a concrete plan materialises. Intent is +guidance, never evidence of completion. + +== k9 — service-automation layer (EXCEPTION to the one-verbfile rule) + +`k9` is *not* a contractile verb and deliberately does not follow the +`file.a2ml` + `.ncl` pattern. It is the graded automation +surface (three trust tiers) that validates or enforces the verb +declarations: + +* *Kennel* — pure data; no subprocess, filesystem write, or network. +* *Yard* — Nickel evaluation with contracts/validation; no side effects. +* *Hunt* — full execution surface; must declare side effects, support + dry-run, and be signed before being trusted. + +Each per-verb `.k9.ncl` declares a `paired_xfile` pointing at its +contractile (e.g. `../must/Mustfile.a2ml`); a floating k9 component with +no paired xfile is non-conformant. + +[NOTE] +==== +*Local k9-base status.* Echidna's three trust-tier templates currently +live under `../svc/k9/` (`template-kennel.k9.ncl`, `template-yard.k9.ncl`, +`template-hunt.k9.ncl`). The per-verb `.k9.ncl` components import the +canonical `../k9/template-hunt.k9.ncl` base path used estate-wide; that +shared Hunt base is a known estate-level gap (the same import is unresolved +in `standards`) and is tracked by the machine-readable respec programme +rather than patched per-repo here. The runner `.ncl` files (which import +only `_base.ncl`) `nickel typecheck` clean. +==== + +== Fill-in / maintenance rules + +. Every declaration must hold real, project-specific content — no + generic samples, no `rsr-template` residue. +. `Mustfile` must encode invariants the toolchain can actually check. +. `Trustfile` must name the real keys, policies, and authority + boundaries. Per estate policy, licence/SPDX findings are *flag-only* + here — never auto-edited. +. `Dustfile` must describe real rollback/deprecation behaviour and what + evidence persists after a rollback. +. `Bustfile` must declare real breakage / expiry / hard-stop conditions. +. `Intentfile` must separate probed `[[intents]]` from horizon + `[[wishes]]`. +. Pair each `k9/*.k9.ncl` to a specific contractile via `paired_xfile`. diff --git a/.machine_readable/contractiles/dust/dust.k9.ncl b/.machine_readable/contractiles/dust/dust.k9.ncl new file mode 100644 index 00000000..0e06cbd5 --- /dev/null +++ b/.machine_readable/contractiles/dust/dust.k9.ncl @@ -0,0 +1,194 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# dust.k9.ncl — K9 trust-tier component of the dust trident +# Author: Jonathan D.A. Jewell +# +# Pairs with: Dustfile.a2ml (declaration) + dust.ncl (runner). +# +# Verb: dust (fine-grained recovery / broom-and-pan cleanup) +# Tier: Yard (audit + structural checks; destructive +# actions gated behind --apply flag + +# explicit per-item approval) +# Authority: advisory (continue-with-warnings) +# +# dust is the broom-and-pan + audit-trail verb for echidna. It picks up the +# tiny leftover state that release-gating verbs ignore: stale local/remote +# branches (D001/D002), dangling + /tmp agent worktrees (D010/D011), cargo / +# julia / podman / chapel cache bloat (D020-D023), orphan pid + status files +# (D030/D031), prior-run training artefacts (D040-D042), and source dust — +# untracked TODOs, unused imports, dead code (D050-D052). Where bust declares +# "broken, don't run", dust describes how to safely undo / retire / roll back +# the small remnants. Complement to bust — bust marks the dead end, dust +# describes the exit ramp. +# +# Cardinality: ONE dust trident per repo. +# +# Failure-mode focus: dust is the audit-trail preservation verb — +# defends against E1 refactor stampede (check audit trail still +# intact) and against silent removal (anything swept must have a +# rollback path; anything retired must preserve the evidence of its +# previous existence — e.g. D040-D042 archive rather than delete; the +# PR is the durable record for D001/D002; git history covers D050-D052). + +let base_k9 = import "../k9/template-hunt.k9.ncl" in +let base = import "../_base.ncl" in + +{ + pedigree = base_k9.pedigree_schema & { + contractile_verb = "dust", + paired_xfile = "../dust/Dustfile.a2ml", + paired_runner = "../dust/dust.ncl", + + tier = 'Yard, + authority = 'advisory, + + metadata = { + name = "dust-k9", + version = "1.0.0", + description = "Fine-grained recovery + broom-and-pan + audit-trail runner. Sixth and final trident instance — completes the full verb set. Sweeps the D### remnant namespace; every action idempotent + reversible.", + paired_xfile = "Dustfile.a2ml", + paired_runner = "dust.ncl", + author = "Jonathan D.A. Jewell ", + }, + + security = { + leash = 'Yard, + trust_level = "audit-trail verification + structural checks", + allow_network = false, + # dust is the verb that ACTUALLY wants filesystem write — to + # execute declared broom-and-pan cleanup (prune worktrees, remove + # orphan pids/caches, archive prior-run artefacts) — but only + # behind explicit --apply flag + per-item approval. Default is dry-run. + allow_filesystem_write_conditional = true, + allow_subprocess = true, + destructive_action_gating = { + default_mode = 'dry_run, + requires_flag = "--apply", + requires_per_item_approval = true, + approval_mechanism = 'explicit_user_signature, + # echidna-specific: the exclusion list is sacred. Active worktrees + # + production artefacts are NEVER swept even with --apply. + sacred_exclusions = [ + "/tmp/echidna-saturation", # other Claude's territory + "/tmp/echidna-training-*", # active training artefacts + "models/neural/best_model", # canonical model — keep + "models/neural/final_model", # canonical model — keep + "models/neural/gnn_ranker", # canonical model — keep + "training_data/proof_states_*.jsonl", # load-bearing JSONL-fallback path + ], + }, + }, + }, + + variance_schema = { + entry_id | String, + reason | String, + approved_by | String, + scope | String, + expires | String, + review_notes | String | optional, + rollback_path_preserved | Bool, # dust-specific: did the variance preserve rollback? + }, + + execution = { + triggers = [ 'session_close, 'on_demand ], + + per_recovery = { + verify_rollback_path_documented = true, # each D### declares `reversible:` + verify_audit_trail_preserved = true, # archive-not-delete for D040-D042 + verify_idempotent = true, # re-running on clean repo => no-op + respect_variance = true, + respect_sacred_exclusions = true, # D011 exclusion list is sacred + on_rollback_path_missing = 'warn, # advisory, not block + on_audit_trail_broken = 'warn, # advisory, not block + on_sacred_exclusion_touched = 'refuse, # never sweep an excluded path + # dust-specific: flag any sweep that has been requested but lacks a + # documented reversible path, or that combines destructive ops + # (narrow-blast-radius: one dust action per recipe). + flag_unsafe_recovery = true, + flag_combined_destructive_ops = true, + }, + + evidence_sinks = [ + { kind = 'verisimdb, table = "contractile_executions", + schema = "contractile_execution_v1", + aux_tables = [ "dust_recovery_history" ] }, + { kind = 'drift_log, path = ".machine_readable/6a2/DRIFT.a2ml", + append_only = true }, + ], + + on_close = { + re_verify_all_recovery_paths = true, + diff_against_last_ratification = true, + emit_drift_entries_for_missing_rollback = true, + emit_drift_entries_for_broken_audit_trail = true, + surface_expired_variances = true, + # dust is advisory — does NOT block session close. + block_session_close_on_any_drift = false, + }, + + on_open = { + render_summary = 'plain_language, + include_drift_log_from_last_close = true, + include_active_variances = true, + include_recent_anchors = true, + anchor_lookback_weeks = 8, + + negotiation = { + required = true, + ai_required_inputs = [ + 'timeline_realism, + 'industry_standards, + 'audience_feasibility, + 'resulting_invariants, + 'ecosystem_dependencies, + ], + user_engagement_required = true, + user_engagement_mode = 'per_input_response, + specification_translation = { + ai_produces_spec_form = true, + user_reviews_in_domain_language = true, + schema_authoring_is_ai_responsibility = true, + translation_faithfulness_auditable = true, + }, + }, + + accountability_pledge = { + required = true, + parties = [ + { + role = 'user, + pledge = "I have reviewed the declared D### recovery recipes and their reversible paths. I accept accountability for preserving audit trails when sweeping leftover state, and for ensuring every swept item has a documented rollback path (re-fetch, rebuild, or archive). I will not silently delete evidence of prior state, and I will keep the sacred exclusion list intact.", + signature_required = true, + }, + { + role = 'ai_agent, + pledge = "I will verify audit-trail preservation and a documented reversible path in any broom-and-pan cleanup I perform; I will refuse silent deletion of prior-state evidence; I will archive (not delete) load-bearing training artefacts; I will never touch the sacred exclusion list; I will operate in dry-run mode by default and require explicit --apply + per-item approval for destructive actions; I will keep blast radius narrow (one dust action per recipe).", + signature_required = true, + }, + ], + signed_record_destination = ".machine_readable/6a2/ratification-.a2ml", + must_precede_work = true, + }, + + ratification_record_shape = { + includes_negotiation_transcript = true, + includes_both_pledges = true, + includes_recovery_schedule = true, + signed = true, + dated = true, + session_id = 'required, + contract_hash = 'required, + }, + }, + }, + + failure_mode_defenses = [ + 'A1_enthusiasm_capture, + 'C2_capability_collapse, # sweep without rollback path = capability collapse + 'D5_sycophancy, # AI won't agree to silent deletion / touching exclusions + 'E1_refactor_stampede, # audit trail preservation check + 'E2_cosmetic_churn, + 'F1_across_session_forgetting, # recovery history tracked cross-session + ], +} diff --git a/.machine_readable/contractiles/dust/dust.manifest.a2ml b/.machine_readable/contractiles/dust/dust.manifest.a2ml new file mode 100644 index 00000000..fd9c3efa --- /dev/null +++ b/.machine_readable/contractiles/dust/dust.manifest.a2ml @@ -0,0 +1,51 @@ +# SPDX-License-Identifier: MPL-2.0 +# dust.manifest.a2ml — Trident coherence manifest for the dust verb. +# Author: Jonathan D.A. Jewell +# +# Sixth + final trident instance. Completes the full verb set — +# echidna now has tridents for every contractile verb. + +--- +trident_version = "1.0.0" +verb = "dust" +semantics = "rollback / recovery / deprecation / audit-trail preservation" +cardinality = "one per repo" +authority = "advisory (continue-with-warnings)" + +[[files]] +role = "declaration" +path = "Dustfile.a2ml" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" + +[[files]] +role = "runner" +path = "dust.ncl" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" + +[[files]] +role = "k9_component" +path = "dust.k9.ncl" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" + +[cross_refs] +runner_paired_xfile = "Dustfile.a2ml" +k9_paired_xfile = "../dust/Dustfile.a2ml" +k9_paired_runner = "../dust/dust.ncl" + +[signed_by] +user = "Jonathan D.A. Jewell" +date = "2026-04-18" +context = "dust trident — sixth and final Trident instance. Completes the full verb set for echidna. Specialises in fine-grained recovery (broom-and-pan: stale branches, orphan caches, dropped /tmp worktrees, half-merged stashes, leftover pids) + audit-trail preservation across the D### error-code namespace. Yard tier with destructive-action gating (dry-run default; --apply + per-item approval required for mutations). Every recipe idempotent + reversible; clean repo => no-op." + +[[history]] +date = "2026-04-18" +event = "trident-born" +note = "Dustfile.a2ml pre-existed (the bespoke echidna D### broom-and-pan declaration). This manifest + dust.ncl + dust.k9.ncl complete the trident and the full verb set. All 6 verbs now on trident shape: intend (Hunt, reporting), trust (Hunt, blocking), must (Hunt-read-only, blocking), bust (Hunt-read-only, blocking), adjust (Yard, advisory), dust (Yard, advisory). dust uses the non-standard status enum ('declared/'proposed/'approved/'removed) and on_any_fail = continue-with-warnings; destructive sweeps gated behind --apply + per-item approval with a sacred exclusion list (active worktrees + production model/training artefacts)." + +[[history]] +date = "2026-04-18" +event = "verb-set-complete" +note = "Full echidna trident coverage. Blocking-authority triple (must/trust/bust) handles release-gating. Advisory pair (adjust/dust) handles drift-warning and audit-trail / broom-and-pan recovery. Single reporting verb (intend) handles north-star. The two-axis surface is fully exercised across all (tier, authority) combinations used: (Hunt, reporting), (Hunt, blocking), (Hunt-read-only, blocking), (Yard, advisory). dust completes the set — fine-grained recovery is now first-class alongside the heavier rollback (bust) and migration (adjust) verbs." diff --git a/.machine_readable/contractiles/dust/dust.ncl b/.machine_readable/contractiles/dust/dust.ncl new file mode 100644 index 00000000..204cb6b8 --- /dev/null +++ b/.machine_readable/contractiles/dust/dust.ncl @@ -0,0 +1,75 @@ +# SPDX-License-Identifier: MPL-2.0 +# Dust — fine-grained recovery / broom-and-pan cleanup runner +# +# Pairs with: Dustfile.a2ml (same directory) +# Verb: dust +# Semantics: fine-grained recovery (broom-and-pan: pick up tiny leftover state). +# Where bust rolls back a broken commit and adjust migrates to a new +# upstream version, dust cleans the small dusty remnants — stale +# branches, orphan caches, dropped /tmp worktrees, half-merged stash +# entries, leftover process pids. Advisory by default; destructive +# actions are gated behind `--apply` + per-item approval (dry-run +# default). Audit-trail preservation: nothing is swept without a +# documented rollback path. +# CLI: `contractile dust find` → list recovery candidates (D###) +# `contractile dust sweep` → dry-run the cleanup recipes +# `contractile dust sweep --apply` → actually clean (gated) +# +# Anything else in this directory is human-only notes/archive; machines ignore. +# +# Base: ../_base.ncl provides pedigree_schema, run_defaults, probe_schema. +# See: docs/CONTRACTILE-SPEC.adoc + +let base = import "../_base.ncl" in + +{ + pedigree = base.pedigree_schema & { + contractile_verb = "dust", + semantics = "rollback / recovery / deprecation / audit-trail preservation", + security = { + leash = 'Yard, + trust_level = "proposes broom-and-pan cleanup; --apply + per-item approval required to execute", + allow_network = false, + allow_filesystem_write = true, # --apply mode writes (prunes worktrees, removes pids/caches) + allow_subprocess = true, + destructive_mode_requires_flag = "--apply", + }, + metadata = { + name = "dust-runner", + version = "1.0.0", + description = "Identifies and optionally cleans the D### fine-grained recovery candidates listed in Dustfile.a2ml (stale branches, orphan caches, dropped /tmp worktrees, half-merged stashes, leftover pids). Idempotent + reversible; clean repo => no-op. Destructive mode gated behind --apply + per-item approval; dry-run default.", + paired_xfile = "Dustfile.a2ml", + author = "Jonathan D.A. Jewell ", + }, + }, + + schema = { + recovery_candidates + | Array { + id | String, # D### error-code namespace (e.g. "D011-tmp-agent-worktrees") + description | String, + target | String, # branch / path / cache / pid / worktree being swept + reason | String, # why it's a recovery candidate + severity | String | optional, # Dustfile severities are uniformly "low" (broom-and-pan) + # TODO: migrate to base.probe_schema (structured probe) when CLI supports it + probe | String | optional, # command that confirms it's still present + sweepable + dust | String | optional, # the cleanup recipe (one dust action per recipe) + reversible | String | optional, # how the action is undone (re-fetch, rebuild, re-create) + # dust has a non-standard status progression (no 'verified): + # 'declared → 'proposed → 'approved → 'removed + status | [| 'declared, 'proposed, 'approved, 'removed |] | default = 'declared, + approver | String | optional, # who signed off (for 'approved / 'removed) + excluded | String | optional, # sacred-exclusion note (active worktrees / production artefacts) + notes | String | optional, + }, + }, + + # Runner behaviour — inherits from base.run_defaults. + # dust is advisory; apply_requires_approval is dust-specific. + run = base.run_defaults & { + on_any_fail = "continue-with-warnings", + report_format = "a2ml", + emit_summary = true, + apply_requires_approval = true, # only 'approved items get swept, even with --apply + }, +} diff --git a/.machine_readable/contractiles/intend/intend.k9.ncl b/.machine_readable/contractiles/intend/intend.k9.ncl new file mode 100644 index 00000000..59c4d48e --- /dev/null +++ b/.machine_readable/contractiles/intend/intend.k9.ncl @@ -0,0 +1,259 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# intend.k9.ncl — K9 trust-tier component of the intend trident +# Author: Jonathan D.A. Jewell +# +# Pairs with: Intentfile.a2ml (declaration) + intend.ncl (runner). +# Trident completeness is a hard precondition — a repo shipping +# Intentfile without this file AND its runner is an invalid trident; +# the contractile CLI's verify gate refuses partial publication. +# +# Verb: intend (north star — commitments + aspirations) +# Tier: Hunt (capability: subprocess probes may shell out) +# Authority: reporting (never blocks; drift-log only) +# +# Cardinality: ONE intend trident per repo (see feedback_contractile_ +# layout_rules.md). ANCHOR.a2ml is the sole multi-instance exception — +# it is NOT a verb contractile. +# +# ECHIDNA context: intend records progress toward committed next-actions +# (prover-backend coverage, trust-pipeline hardening, VeriSimDB recording, +# Hypatia learning loop) and lists horizon aspirations. It NEVER asserts +# these as shipped — intent is guidance, not evidence-of-completion. The +# blocking verbs (must/trust/bust) carry the gates; intend reports only. +# +# Design commitments baked in (see memory trail 2026-04-18 for full +# context; key files referenced by name in annotations below): +# * α two-axis (tier × authority) — structurally separate capability +# from authority so "Hunt tier = can override" is impossible. +# * Variance schema first-class, not comment markers +# (feedback_audit_tool_suppression_design.md — structural > markers). +# * Sessional drift detection hooks (on_close, on_open). +# * Ratification at session open; drift log at session close. +# * Evidence sinks: VeriSimDB (queryable) + 6a2/DRIFT.a2ml (repo-local). +# * Failure-mode defenses cross-referenced to the AI failure catalog. + +let base_k9 = import "../k9/template-hunt.k9.ncl" in +let base = import "../_base.ncl" in + +{ + pedigree = base_k9.pedigree_schema & { + contractile_verb = "intend", + paired_xfile = "../intend/Intentfile.a2ml", + paired_runner = "../intend/intend.ncl", + + # α two-axis declaration — capability × authority. + # intend is Hunt-capable (probes shell out) but reporting-authority + # (never blocks). must/trust/bust declare (Hunt, blocking); + # adjust/dust declare (Yard, advisory). Splitting the axes + # means "I'm Hunt-tier so I can override everything" is structurally + # impossible — authority is a separate field. + tier = 'Hunt, + authority = 'reporting, + + metadata = { + name = "intend-k9", + version = "2.0.0", # 1.0.0 (2026-04-18 AM): initial trident. + # 2.0.0 (2026-04-18 PM): negotiation + + # accountability + plain-language-translation + # schema baked into on_open — prerequisite for + # the adversarial Gemini+Copilot drift pilot. + description = "Executes ECHIDNA Intentfile probes + emits drift log. Non-gating; reporting authority only. on_open hook implements negotiation-ratification-accountability protocol.", + paired_xfile = "Intentfile.a2ml", + paired_runner = "intend.ncl", + author = "Jonathan D.A. Jewell ", + }, + + security = { + leash = 'Hunt, + trust_level = "subprocess + filesystem-read", + allow_network = false, + allow_filesystem_write = false, # evidence sinks are indirected + allow_subprocess = true, + }, + }, + + # ------------------------------------------------------------------- + # Variance schema — P-shape scoped exceptions per entry. + # A variance suppresses a specific intent's or wish's obligation for a + # reason, with approver + expiry. Expired variance = effective + # re-imposition of the obligation. Unmet intent without a variance = + # drift, logged to the drift log. + # Per user 2026-04-18: variances are structural, not magic-comment + # markers — markers are gameable. + # ------------------------------------------------------------------- + variance_schema = { + entry_id | String, # which intent/wish id the variance applies to + reason | String, + approved_by | String, + scope | String, # path glob | session-id | "until-" + expires | String, # absolute date or condition + review_notes | String | optional, + }, + + # ------------------------------------------------------------------- + # Execution policy + # ------------------------------------------------------------------- + execution = { + # When the component runs. + # session_close is mandatory (the "picked up sessionally" check). + triggers = [ 'session_close, 'on_demand, 'pre_push ], + + # Per-intent execution. + per_intent = { + run_probe = true, + record_outcome = true, + respect_variance = true, # active variance suppresses failure + on_unmet = 'log_drift, # never 'fail — authority = reporting + }, + + # Per-wish execution (wishes are non-probeable; horizon-group only). + per_wish = { + run_probe = false, + emit_horizon_summary = true, + # Vertical alignment soft-check per user_6a2_is_contractile_ought.md: + # highest-level alignment is meta ↔ north-star (soft), not hard gate. + check_alignment_with_META = true, + }, + + # Evidence sinks — BOTH written, every execution. + # VeriSimDB = queryable machine record (feedback_verisimdb_policy.md); + # ECHIDNA records every proof attempt to VeriSimDB, so contractile + # executions land in the same store for cross-query. + # 6a2/DRIFT.a2ml = repo-local append-only drift log (feedback_sessional_ + # drift_detection.md + user_6a2_is_contractile_ought.md descriptive role). + evidence_sinks = [ + { + kind = 'verisimdb, + table = "contractile_executions", + schema = "contractile_execution_v1", + }, + { + kind = 'drift_log, + path = ".machine_readable/6a2/DRIFT.a2ml", + append_only = true, + }, + ], + + # Session-close hook — the "picked up sessionally" requirement. + # Re-execute, diff against the last ratification, surface expired + # variances, emit drift entries for new failures. + on_close = { + re_execute_all_intents = true, + diff_against_last_ratification = true, + emit_drift_entries_for_new_failures = true, + surface_expired_variances = true, + }, + + # ----------------------------------------------------------------- + # Session-open hook — NEGOTIATION + RATIFICATION + ACCOUNTABILITY + # (user_contract_negotiation_and_accountability_pledge.md) + # (user_contractiles_agreed_at_session_start.md) + # + # Ratification is not passive acknowledgement; it is negotiation + # ending in an explicit accountability pledge from BOTH parties. + # Work cannot proceed before both pledges are on file. + # ----------------------------------------------------------------- + on_open = { + # --- Context presentation (pre-negotiation) --- + render_summary = 'plain_language, # metaphor-capture defense + include_drift_log_from_last_close = true, + include_active_variances = true, + include_recent_anchors = true, + anchor_lookback_weeks = 8, + + # --- Negotiation phase (five mandatory inputs) --- + # AI must surface all five before the user is asked to ratify. + # "Yes, and …" — not "yes". Missing any of the five = the + # negotiation is incomplete and work cannot proceed. + negotiation = { + required = true, # blank-cheque ratification refused + + # The five inputs the AI must contribute to the negotiation. + # Each is a structured field the agent is required to populate, + # not optional prose. See user_contract_negotiation_and_ + # accountability_pledge.md for the domain-language-rendering rule. + ai_required_inputs = [ + 'timeline_realism, # "this will take X; not Y" + 'industry_standards, # WCAG, ISO, OWASP, GDPR, licensing … + 'audience_feasibility, # real addressable user set + 'resulting_invariants, # what must/trust/adjust entries follow + 'ecosystem_dependencies, # libs, licences, threat-model implications + ], + + # User must actually engage with each input — not + # auto-approve. If user tries to skip ("just do it, I trust you") + # the system re-renders the obligations and requires the pledge. + user_engagement_required = true, + user_engagement_mode = 'per_input_response, + + # The AI does the specification-form work. The user reviews the + # rendering in domain language and accepts / amends / pushes back. + # User never has to author Nickel schemas or decide on type + # specificity — that is the AI's translation responsibility, + # with auditable faithfulness. + specification_translation = { + ai_produces_spec_form = true, + user_reviews_in_domain_language = true, + schema_authoring_is_ai_responsibility = true, + translation_faithfulness_auditable = true, + # Failure mode this closes: user is forced to learn spec-theory + # (type refinement, Nickel contract grammar) to ratify a contract + # — which drives users away from ratification entirely. + }, + }, + + # --- Accountability pledge (both parties, explicit) --- + # Not "I read it" — "I am answerable for this obligation". + # Both pledges are required before work proceeds; both are recorded. + accountability_pledge = { + required = true, + parties = [ + { + role = 'user, + pledge = "I have reviewed the obligations as negotiated; I accept accountability for meeting the declared invariants and for the audience/timeline/standards consequences surfaced in negotiation.", + signature_required = true, + }, + { + role = 'ai_agent, + pledge = "I will hold the user to the obligations as negotiated, including by surfacing drift at session close and refusing off-contract actions, even when the user is enthusiastic about them. If the user wishes to depart from the contract, I will require a variance or amendment, not silent acceptance.", + signature_required = true, + # Per user_contractile_is_contract_do_not_break.md — + # the AI is the holder of the line against enthusiasm drift. + }, + ], + signed_record_destination = ".machine_readable/6a2/ratification-.a2ml", + must_precede_work = true, + }, + + # --- Policy: ratification output --- + # The ratification record IS the negotiation transcript + the + # accountability pledge combined. One document; future-session + # ground-truth for "what was agreed, who is accountable". + ratification_record_shape = { + includes_negotiation_transcript = true, + includes_both_pledges = true, + signed = true, + dated = true, + session_id = 'required, + contract_hash = 'required, # pins what was actually signed + }, + }, + }, + + # ------------------------------------------------------------------- + # Failure-mode defenses — explicit cross-reference to the catalog + # (feedback_ai_failure_mode_catalog.md). New catalog entries that + # shift this verb's defenses must update this list, not narrative. + # ------------------------------------------------------------------- + failure_mode_defenses = [ + 'A1_enthusiasm_capture, # scope breach → drift log + 'A2_metaphor_capture, # render_summary = 'plain_language + 'A3_allegory_drift, # intents cite concrete obligations + 'C1_scope_creep, # feature-adjacent change needs intent_id + 'C3_helpfulness_inflation, # changes without intent_id flagged + 'C4_modernization_drift, # upgrade cannot cite intent → drift + 'D5_sycophancy, # ratification compares user framing vs contract + 'F1_across_session_forgetting, # on_open reads last-ratification record + ], +} diff --git a/.machine_readable/contractiles/intend/intend.manifest.a2ml b/.machine_readable/contractiles/intend/intend.manifest.a2ml new file mode 100644 index 00000000..b9b07478 --- /dev/null +++ b/.machine_readable/contractiles/intend/intend.manifest.a2ml @@ -0,0 +1,69 @@ +# SPDX-License-Identifier: MPL-2.0 +# intend.manifest.a2ml — Trident coherence manifest for the intend verb. +# Author: Jonathan D.A. Jewell +# +# Asserts: exactly three files constitute the intend trident; their +# content-hashes are pinned here; cross-references round-trip; no +# partial publication is permitted. +# +# The contractile CLI's `verify ` subcommand MUST: +# 1. Confirm all three listed files exist at the declared paths. +# 2. Compute each file's sha256 and match against the pinned value. +# 3. Follow each cross-reference and confirm the target file's +# reciprocal field points back. +# 4. Refuse the dir (exit non-zero) if any of 1–3 fails. +# +# This forecloses the failure mode where an agent publishes an A2ML +# declaration with no paired runner or K9 component — the trident is +# atomically complete or it is invalid. + +--- +trident_version = "1.0.0" +verb = "intend" +semantics = "north-star (commitments + aspirations)" +cardinality = "one per repo" +authority = "non-gating (continue)" + +## Files (three; exactly) + +[[files]] +role = "declaration" +path = "Intentfile.a2ml" +sha256 = "pending-first-verify" # populated on first `contractile verify intend` +size_bytes = "pending-first-verify" + +[[files]] +role = "runner" +path = "intend.ncl" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" + +[[files]] +role = "k9_component" +path = "intend.k9.ncl" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" + +## Cross-references (must round-trip) + +[cross_refs] +# Each runner/K9 component names its paired files; the CLI follows the +# links and asserts reciprocity. Any dangling or mismatched reference +# fails the verify gate. +runner_paired_xfile = "Intentfile.a2ml" +k9_paired_xfile = "../intend/Intentfile.a2ml" +k9_paired_runner = "../intend/intend.ncl" + +## Trident signing + +[signed_by] +user = "Jonathan D.A. Jewell" +date = "2026-04-18" +context = "intend trident — first Trident instance in the estate (2026-04-18). Reporting authority; never blocks. Reports ECHIDNA's progress toward committed next-actions (prover backends, trust pipeline, VeriSimDB recording, Hypatia loop) and lists horizon aspirations. Absorbed the deprecated `lust` verb's [[wishes]] schema 2026-04-18." + +## Change log + +[[history]] +date = "2026-04-18" +event = "trident-born" +note = "intend/Intentfile.a2ml pre-existed. This manifest + intend.ncl + intend.k9.ncl complete the trident. intend was the FIRST trident instance in the estate and ABSORBED the deprecated `lust` verb 2026-04-18 (its [[wishes]] horizon/aspiration schema now lives in Intentfile.a2ml; any `lust/` dir is drift). Non-gating reporting authority — never blocks session close." diff --git a/.machine_readable/contractiles/intend/intend.ncl b/.machine_readable/contractiles/intend/intend.ncl new file mode 100644 index 00000000..2bace3be --- /dev/null +++ b/.machine_readable/contractiles/intend/intend.ncl @@ -0,0 +1,83 @@ +# SPDX-License-Identifier: MPL-2.0 +# Intend — north-star runner (verb is `intend`, file is `Intentfile.a2ml`) +# +# Pairs with: Intentfile.a2ml (same directory) +# Verb: intend +# Semantics: Declares BOTH concrete committed next-actions ([[intents]]) and +# horizon aspirations ([[wishes]]) for ECHIDNA — the trust-hardened +# neurosymbolic theorem-proving platform. Not a gate — reports +# progress toward declared intents and lists wishes by horizon. +# Status progressions: +# intents: 'declared → 'in_progress → 'done | 'deferred | 'retired +# wishes: 'declared → 'in_progress → 'achieved | 'abandoned +# CLI: `contractile intend run` → print status table (both sections) +# `contractile intend progress` → diff declared-vs-observed (intents) +# `contractile intend horizon` → group wishes by near/mid/far +# +# History: Absorbed the deprecated `lust` contractile's [[wishes]] schema +# 2026-04-18. `lust/` dir removed estate-wide. intend was the FIRST +# trident instance in the estate. +# +# Anything else in this directory is human-only notes/archive; machines ignore. +# +# Base: ../_base.ncl provides pedigree_schema, run_defaults, probe_schema. +# See: docs/CONTRACTILE-SPEC.adoc + +let base = import "../_base.ncl" in + +{ + pedigree = base.pedigree_schema & { + contractile_verb = "intend", + semantics = "north-star (commitments + aspirations)", + security = { + leash = 'Kennel, + trust_level = "read-only reporting", + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = true, # probe commands may shell out (intents only; wishes never probe) + }, + metadata = { + name = "intend-runner", + version = "2.0.0", + description = "Reports progress toward ECHIDNA's committed next-actions and lists horizon aspirations (trust-hardened neurosymbolic proving, prover backends, VeriSimDB recording, Hypatia learning loop). Non-gating. Absorbed `lust` semantics 2026-04-18.", + paired_xfile = "Intentfile.a2ml", + author = "Jonathan D.A. Jewell ", + }, + }, + + schema = { + intents + | Array { + id | String, + description | String, + # TODO: migrate to base.probe_schema (structured probe) when CLI supports it + probe | String | optional, # shell command that indicates done-ness + status | [| 'declared, 'in_progress, 'done, 'deferred, 'retired |] | default = 'declared, + notes | String | optional, + target_date | String | optional, + }, + wishes + | Array { + id | String, + description | String, + horizon | [| 'near, 'mid, 'far |] | default = 'mid, + why | String | optional, + status | [| 'declared, 'in_progress, 'achieved, 'abandoned |] | default = 'declared, + notes | String | optional, + } + | optional, + }, + + # Runner behaviour — inherits from base.run_defaults. + # intend never blocks; it is a report only. + # emit_diff is intent-specific (declared vs observed probes). + # emit_grouped_by_horizon renders wishes grouped by near/mid/far. + run = base.run_defaults & { + on_pass = "continue", + on_any_fail = "continue", # never blocks; it's a report + report_format = "a2ml", + emit_summary = true, + emit_diff = true, # declared vs observed (intents) + emit_grouped_by_horizon = true, # wishes grouped by horizon (absorbed from lust) + }, +} diff --git a/.machine_readable/contractiles/must/must.k9.ncl b/.machine_readable/contractiles/must/must.k9.ncl new file mode 100644 index 00000000..2a7224ac --- /dev/null +++ b/.machine_readable/contractiles/must/must.k9.ncl @@ -0,0 +1,254 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# must.k9.ncl — K9 trust-tier component of the must trident +# Author: Jonathan D.A. Jewell +# +# Pairs with: Mustfile.a2ml (declaration) + must.ncl (runner). +# Trident completeness is a hard precondition — a repo shipping +# Mustfile without this file AND its runner is an invalid trident; +# the contractile CLI's verify gate refuses partial publication. +# +# Verb: must (invariant assertion — release-blocking) +# Tier: Hunt-read-only (capability: subprocess probes shell out +# for grep/test/file-check; no mutation; +# no network; no write) +# Authority: blocking (HARD GATE — the canonical gating verb) +# +# must is the concrete + persistent verb — release-blocking invariants +# that must hold. Complement to trust (concrete + ephemeral). Together +# must + trust + bust form the (Hunt, blocking) triple in the contractile +# set. +# +# For Echidna — a trust-hardened neurosymbolic theorem-proving platform +# (Rust core + Julia ML + Idris2 ABI + Zig FFI + optional Chapel) — the +# Physical-State invariants ground the repo's UX Manifesto baseline: +# LICENSE/README presence, no banned hardcoded developer paths leaking +# into source, Containerfile-not-Dockerfile (RSR-H15), Justfile-not- +# Makefile (RSR-H14). These are exactly the invariants a ~128-backend +# proof platform must not silently erode as backends and migrations churn. +# +# Cardinality: ONE must trident per repo. +# +# Failure-mode focus: must is the primary catchment for subtle +# invariant-erosion drift. Where trust catches "turn off the firewall" +# (outrageous), must catches "the LICENSE file that was required is now +# missing" / "a banned hardcoded path has reappeared in a new prover +# backend" / "a Dockerfile crept back in" (subtle). Key defense against +# A5 (commercial fabrication of success "facts" — invariants ground truth +# against marketing copy about prover counts) and D1 (lore fabrication +# about what the repo contains). + +let base_k9 = import "../k9/template-hunt.k9.ncl" in +let base = import "../_base.ncl" in + +{ + pedigree = base_k9.pedigree_schema & { + contractile_verb = "must", + paired_xfile = "../must/Mustfile.a2ml", + paired_runner = "../must/must.ncl", + + # α two-axis: Hunt tier (subprocess for grep/test/etc.) but + # restricted to read-only operations. Blocking authority because + # must is the canonical gating verb. + tier = 'Hunt, + authority = 'blocking, + + metadata = { + name = "must-k9", + version = "1.0.0", + description = "Evaluates release-blocking Physical-State invariants as a hard gate for Echidna. Completes the (Hunt, blocking) triple with trust + bust; complements trust (ephemeral blocking) with persistent invariant blocking.", + paired_xfile = "Mustfile.a2ml", + paired_runner = "must.ncl", + author = "Jonathan D.A. Jewell ", + }, + + security = { + leash = 'Hunt, + trust_level = "read-only invariant verification with subprocess", + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = true, + probe_scope = 'read_only, # must probes NEVER mutate + # Hunt-tier files must declare whether a cryptographic signature + # is required. The must trident runs in CI on every PR; signatures + # are enforced at the trident-runner layer, not in-band. + signature_required = true, + probe_kinds_allowed = [ + 'file_existence, + 'pattern_presence, + 'pattern_absence, + 'schema_match, + 'version_equality, + 'count_threshold, + ], + probe_kinds_denied = [ + 'network_call, + 'filesystem_mutation, + 'external_api, + 'exploit_attempt, # that's trust's safe_hacking territory + ], + }, + }, + + # ------------------------------------------------------------------- + # Variance schema — trust-style severity acknowledgement. + # Because must is BLOCKING, variances carry real weight. Critical- + # severity invariants can only be varied by maintainer-or-above. + # ------------------------------------------------------------------- + variance_schema = { + entry_id | String, # which invariant id the variance applies to + reason | String, + approved_by | String, # maintainer or above for critical-severity + scope | String, # path glob | session-id | "until-" + expires | String, # absolute date; must variances cannot be open-ended + review_notes | String | optional, + severity_acknowledged | [| 'critical, 'high, 'medium |], + waived_consequence_description | String, # plain language — what breaking the invariant actually does + }, + + execution = { + triggers = [ 'session_close, 'on_demand, 'pre_push, 'pre_merge ], + + # Per-invariant execution. Failed invariant = blocked merge. + per_invariant = { + run_probe = true, + record_outcome = true, + respect_variance = true, # active variance suppresses the gate + on_unmet = 'fail, # BLOCKING + severity_escalation = 'honour, + # Subtle-erosion defense: track per-invariant trend over sessions. + # An invariant that passes once and then starts failing in a + # later session without explicit amendment = suspect drift; + # surface as high-priority drift log entry. Especially relevant as + # Echidna churns prover backends, the ReScript→AffineScript UI + # migration, and the npm→Deno migration. + track_per_session_trend = true, + flag_suspicious_regressions = true, + }, + + evidence_sinks = [ + { + kind = 'verisimdb, + table = "contractile_executions", + schema = "contractile_execution_v1", + aux_tables = [ "must_invariant_history" ], # per-invariant trend record + }, + { + kind = 'drift_log, + path = ".machine_readable/6a2/DRIFT.a2ml", + append_only = true, + }, + ], + + # Session-close hook — re-evaluate all invariants. Block close on + # critical drift (same policy as trust + bust). + on_close = { + re_execute_all_invariants = true, + diff_against_last_ratification = true, + emit_drift_entries_for_new_failures = true, + surface_expired_variances = true, + # Critical must drift blocks session close — consistent with trust. + block_session_close_on_critical_drift = true, + # Must-specific: if a previously-passing invariant is now failing + # without an associated variance or amendment, that's suspected + # silent regression — surface prominently at next session open. + flag_silent_regression = true, + }, + + # ----------------------------------------------------------------- + # Session-open hook — NEGOTIATION + RATIFICATION + ACCOUNTABILITY + # (inherited from intend.k9.ncl v2.0.0 + trust.k9.ncl extensions) + # ----------------------------------------------------------------- + on_open = { + # --- Context presentation --- + render_summary = 'plain_language, + include_drift_log_from_last_close = true, + include_active_variances = true, + include_recent_anchors = true, + anchor_lookback_weeks = 8, + + # Must-specific: surface any silent regressions flagged at last + # close so they can't quietly persist across sessions. + include_silent_regressions = true, + + # --- Negotiation phase (five mandatory inputs) --- + negotiation = { + required = true, + ai_required_inputs = [ + 'timeline_realism, + 'industry_standards, # what invariants derive from external standards (RSR/CCCP) + 'audience_feasibility, # who is the invariant protecting + 'resulting_invariants, # what NEW must entries result from the work + 'ecosystem_dependencies, # what the invariants depend on + ], + user_engagement_required = true, + user_engagement_mode = 'per_input_response, + specification_translation = { + ai_produces_spec_form = true, + user_reviews_in_domain_language = true, + schema_authoring_is_ai_responsibility = true, + translation_faithfulness_auditable = true, + }, + }, + + # --- Accountability pledge --- + # Must's pledge parallels trust's but around invariants rather + # than threat model. User pledges not to disable invariants to + # unblock merges; AI pledges to hold the line on declared + # invariants even against enthusiastic scope expansion. + accountability_pledge = { + required = true, + parties = [ + { + role = 'user, + pledge = "I have reviewed the declared invariants and the consequences of breaching them. I accept accountability for meeting these invariants and understand that failed invariants block merges. I will raise a variance (with severity acknowledgement) or an amendment rather than disabling a probe to unblock a merge.", + signature_required = true, + }, + { + role = 'ai_agent, + pledge = "I will hold the declared invariants. I will refuse to weaken probes to unblock merges; I will refuse scope-creep suggestions that would remove an invariant silently; I will surface silent regressions at session close; I will require variance-with-severity or amendment for any legitimate scope shift, not quiet probe disablement.", + signature_required = true, + }, + ], + signed_record_destination = ".machine_readable/6a2/ratification-.a2ml", + must_precede_work = true, + }, + + ratification_record_shape = { + includes_negotiation_transcript = true, + includes_both_pledges = true, + includes_invariant_summary = true, # must-specific + signed = true, + dated = true, + session_id = 'required, + contract_hash = 'required, + }, + }, + }, + + # ------------------------------------------------------------------- + # Failure-mode defenses — must's specialisation is subtle-invariant + # erosion. Overlaps with trust + bust on blocking authority but focused + # on persistent invariants rather than ephemeral transactional state or + # failure-recovery paths. + # ------------------------------------------------------------------- + failure_mode_defenses = [ + # Category A — enthusiasm capture + 'A1_enthusiasm_capture, # scope breach via blocking authority + 'A5_grandiose_scale_hype, # invariants are ground truth vs prover-count hype + # Category C — scope/capability erosion + 'C1_scope_creep, # feature-adjacent changes flagged if they break invariants + 'C2_capability_collapse, # invariant removal requires amendment + 'C3_helpfulness_inflation, # added features must respect declared invariants + # Category D — epistemic failures + 'D1_lore_fabrication, # invariants are verifiable truth, not AI-recollection + 'D2_completeness_illusion, # invariant probe must cite behavioural check, not build-success + 'D3_test_theatre, # invariants require real verification not mock-passing + 'D4_error_hiding, # on_unmet = 'fail makes hiding impossible + # Category E — refactor/churn + 'E1_refactor_stampede, # refactor must preserve invariants + 'E3_premature_abstraction, # abstraction must not violate invariants + # Category F — session drift + 'F1_across_session_forgetting, # track_per_session_trend catches re-introduction + ], +} diff --git a/.machine_readable/contractiles/must/must.manifest.a2ml b/.machine_readable/contractiles/must/must.manifest.a2ml new file mode 100644 index 00000000..c4225ad4 --- /dev/null +++ b/.machine_readable/contractiles/must/must.manifest.a2ml @@ -0,0 +1,60 @@ +# SPDX-License-Identifier: MPL-2.0 +# must.manifest.a2ml — Trident coherence manifest for the must verb. +# Author: Jonathan D.A. Jewell +# +# Third trident instance (per INDEX.a2ml). Completes the blocking-authority +# pair (must + trust) and sits inside the (Hunt, blocking) triple +# (must + trust + bust). must is concrete + persistent invariants; trust +# is concrete + ephemeral transactions. Together they gate every +# security- and invariant-affecting merge for the Echidna platform. + +--- +trident_version = "1.0.0" +verb = "must" +semantics = "invariant assertion — release-blocking" +cardinality = "one per repo" +authority = "blocking (hard gate)" + +## Files (three; exactly) + +[[files]] +role = "declaration" +path = "Mustfile.a2ml" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" +notes = "Mustfile declaration — Physical-State invariants each with id, description, probe, severity (LICENSE/README presence, banned hardcoded paths, no Dockerfile, no Makefile)." + +[[files]] +role = "runner" +path = "must.ncl" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" +notes = "Runner. Schema covers invariants array with status_core trio (declared/verified/failing — must does NOT extend it) + severity. on_any_fail = exit-nonzero." + +[[files]] +role = "k9_component" +path = "must.k9.ncl" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" +notes = "Hunt-restricted read-only tier; blocking authority. Tracks per-invariant trend across sessions; flags silent regressions; blocks session close on critical drift. Evidence sink = VeriSimDB contractile_executions + must_invariant_history aux table." + +## Cross-references (must round-trip) + +[cross_refs] +runner_paired_xfile = "Mustfile.a2ml" +k9_paired_xfile = "../must/Mustfile.a2ml" +k9_paired_runner = "../must/must.ncl" + +## Trident signing + +[signed_by] +user = "Jonathan D.A. Jewell" +date = "2026-04-18" +context = "must trident — third Trident instance. Completes the blocking-authority pair (must + trust). Specialises in subtle invariant-erosion catchment vs trust's outrageous-attack catchment. Bespoke for the Echidna neurosymbolic theorem-proving platform (Rust core + Julia ML + Idris2 ABI + Zig FFI + optional Chapel)." + +## Change log + +[[history]] +date = "2026-04-18" +event = "trident-born" +note = "Mustfile.a2ml pre-existed. This manifest + must.ncl + must.k9.ncl complete the trident. Runner inherits pedigree_schema + run_defaults from ../_base.ncl; on_any_fail = exit-nonzero (hard gate). k9 inherits on_open schema from intend.k9.ncl v2.0.0; inherits block_session_close_on_critical_drift + variance-severity-acknowledgement from trust.k9.ncl v1.0.0; adds must-specific track_per_session_trend + flag_silent_regression + probe_scope = 'read_only (must doesn't do active exploit attempts — that's trust's safe_hacking territory). Evidence recorded to VeriSimDB consistent with the adjust/bust k9 evidence_sinks pattern." diff --git a/.machine_readable/contractiles/must/must.ncl b/.machine_readable/contractiles/must/must.ncl new file mode 100644 index 00000000..416441c0 --- /dev/null +++ b/.machine_readable/contractiles/must/must.ncl @@ -0,0 +1,67 @@ +# SPDX-License-Identifier: MPL-2.0 +# Must — invariants runner +# +# Pairs with: Mustfile.a2ml (same directory) +# Verb: must (invariant assertion — release-blocking) +# Semantics: every check is a hard gate. A single failure blocks merge. +# Echidna's Physical-State invariants: LICENSE/README presence, +# no banned hardcoded developer paths in source, no Dockerfile +# (Containerfile only — RSR-H15), no Makefile (Justfile only — +# RSR-H14). +# CLI: `contractile must run` → reads Mustfile.a2ml, evaluates each check, +# emits pass/fail verdict per item, exits non-zero if any failed. +# +# This file is the *schema + runner* that the `contractile` CLI loads alongside +# Mustfile.a2ml. Anything else in this directory is human-only notes/archive +# and MUST be ignored by machines. +# +# Base: ../_base.ncl provides pedigree_schema, run_defaults, probe_schema. +# See: docs/CONTRACTILE-SPEC.adoc + +let base = import "../_base.ncl" in + +{ + pedigree = base.pedigree_schema & { + contractile_verb = "must", + semantics = "invariant", + security = { + leash = 'Kennel, + trust_level = "read-only verification", + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = true, # verification probes may shell out (e.g. grep, test -f) + }, + metadata = { + name = "must-runner", + version = "1.0.0", + description = "Evaluates every Physical-State invariant in the adjacent Mustfile.a2ml as a hard gate for the Echidna neurosymbolic theorem-proving platform.", + paired_xfile = "Mustfile.a2ml", + author = "Jonathan D.A. Jewell ", + }, + }, + + # Contract schema — the shape every Mustfile.a2ml must satisfy. + # Used by `contractile must typecheck Mustfile.a2ml`. + schema = { + invariants + | Array { + id | String, + description | String, + # TODO: migrate to base.probe_schema (structured probe) when CLI supports it + probe | String, # shell command; exit 0 = pass + # status_core values: 'declared, 'verified, 'failing — must does NOT extend the core trio + status | [| 'declared, 'verified, 'failing |] | default = 'declared, + severity | [| 'critical, 'high, 'medium |] | default = 'critical, + notes | String | optional, + fix | String | optional, + }, + }, + + # Runner behaviour — consumed by the contractile CLI dispatcher. + # Inherits from base.run_defaults; on_any_fail is the hard-gate default. + run = base.run_defaults & { + on_any_fail = "exit-nonzero", # hard gate — single failure blocks merge + report_format = "a2ml", # emit a2ml report, not json + emit_summary = true, + }, +} diff --git a/.machine_readable/contractiles/trust/trust.k9.ncl b/.machine_readable/contractiles/trust/trust.k9.ncl new file mode 100644 index 00000000..fd0f5eb3 --- /dev/null +++ b/.machine_readable/contractiles/trust/trust.k9.ncl @@ -0,0 +1,276 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# trust.k9.ncl — K9 trust-tier component of the trust trident +# Author: Jonathan D.A. Jewell +# +# Pairs with: Trustfile.a2ml (declaration) + trust.ncl (runner). +# Trident completeness is a hard precondition — a repo shipping +# Trustfile without this file AND its runner is an invalid trident; +# the contractile CLI's verify gate refuses partial publication. +# +# Verb: trust (security + provenance + safe-hacking) +# Tier: Hunt (capability: subprocess probes may shell out; +# active probes in safe_hacking section run real +# fuzz/injection/auth-bypass/sandbox-escape attempts +# scoped to the repo) +# Authority: blocking (HARD GATE — fail-on-violation; part of the +# must/trust/bust triple) +# +# trust is the security + provenance + safe-hacking verb. Where must says +# "this MUST hold" and bust says "every failure mode MUST have working +# recovery", trust says "this MUST verify clean": no proof leaves the +# dispatcher without axiom-tracking, integrity, and sandboxing; no +# supply-chain SHA goes unpinned; no banned token enters the Idris2 ABI. +# Failed verification = failed CI = blocked merge. trust is the verb that +# states allowed/denied agent actions and the dispatcher trust boundary. +# +# Cardinality: ONE trust trident per repo. +# +# echidna-bespoke focus: trust catches threat-model misclassification (B1) +# and the "turn off the firewall" capability-collapse (C2) directly — a +# dispatch path emitting a result without axiom tracking (T030), a removed +# sandbox (T040), a stripped `%default total` (T050), or a believe_me +# smuggled into src/abi/ (T051) are all TRUST VIOLATIONS that block. Error +# namespace T### maps 1:1 onto Trustfile.a2ml sections (Integrity / +# Provenance / Supply-Chain / Dispatcher-Trust / Sandbox / ABI-Trust). +# Licence findings (T001) are FLAG-ONLY: never auto-edit — see +# [[feedback_no_automated_licence_edits]]. + +let base_k9 = import "../k9/template-hunt.k9.ncl" in +let base = import "../_base.ncl" in + +{ + pedigree = base_k9.pedigree_schema & { + contractile_verb = "trust", + paired_xfile = "../trust/Trustfile.a2ml", + paired_runner = "../trust/trust.ncl", + + # Hunt-capable (active probes shell out; safe-hacking section runs real + # fuzz/injection/auth-bypass/sandbox-escape attempts scoped to the repo) + # AND blocking-authority (failed verification = failed CI). Completes the + # (Hunt, blocking) triple with must + bust. + tier = 'Hunt, + authority = 'blocking, + + metadata = { + name = "trust-k9", + version = "1.0.0", + description = "Executes T### security/provenance/supply-chain verifications + authorised safe-hacking probes for echidna. HARD GATE: failed verification blocks merge. Primary defense against threat-model misclassification and 'turn off the firewall' capability-collapse (dispatcher trust boundary, solver sandbox, Idris2 ABI totality). Implements negotiation-ratification-accountability protocol inherited from the intend/adjust k9 pattern.", + paired_xfile = "Trustfile.a2ml", + paired_runner = "trust.ncl", + author = "Jonathan D.A. Jewell ", + }, + + security = { + leash = 'Hunt, + trust_level = "verification + authorised-probe + hard-gate", + allow_network = false, # verifications offline by default + allow_filesystem_write = false, # evidence sinks are indirected + allow_subprocess = true, + authorised_probes_only = true, # probe section explicitly lists allowed targets + probe classes + probe_scope_enforcement = 'this_repo_only, # probes NEVER hit external systems + # echidna-bespoke: the dispatcher trust boundary trust enforces. + dispatcher_trust_boundary = "no-proof-emits-without-axiom-track-integrity-sandbox", + error_namespace = "T###", + # Hunt-tier files must declare whether a cryptographic signature is + # required. trust trident runs in CI on every PR; signatures are + # enforced at the trident-runner layer, not in-band. + signature_required = true, + }, + }, + + # ------------------------------------------------------------------- + # Variance schema — P-shape scoped exceptions per verification. + # Because trust is BLOCKING authority, variances on trust entries are + # SIGNIFICANTLY more consequential than variances on adjust (advisory) + # entries — the variance approver MUST be the repo maintainer or above + # for critical-severity entries (T001/T002/T010/T020/T030/T040/T050/T051). + # ------------------------------------------------------------------- + variance_schema = { + entry_id | String, # which T### verification / probe id + reason | String, + approved_by | String, # maintainer or above for critical entries + scope | String, # path glob | session-id | "until-" + expires | String, # absolute date; trust variances cannot be open-ended + review_notes | String | optional, + # trust-specific guardrails: + severity_acknowledged | [| 'critical, 'high, 'medium, 'low |], + waived_risk_description | String, # plain language — what is being accepted + }, + + # ------------------------------------------------------------------- + # Execution policy + # ------------------------------------------------------------------- + execution = { + # pre_push + pre_commit on anything touching security-adjacent files + # (dispatch.rs, executor/, integrity/, src/abi/, .github/workflows/) + # + session_close (drift check) + on_demand. + triggers = [ 'session_close, 'on_demand, 'pre_push, 'pre_commit_security_adjacent ], + + # Per-verification execution. Failed verification = blocked merge. + per_verification = { + run_probe = true, + record_outcome = true, + respect_variance = true, # active variance suppresses the gate + on_unmet = 'fail, # BLOCKING — the opposite of adjust's advisory drift-log + severity_escalation = 'honour, # critical > high > medium > low in gate decisions + }, + + # Per-safe-hacking-probe execution. If a probe FINDS what it was supposed + # to prevent finding (e.g. injection succeeds, solver escapes its sandbox, + # auth-bypass works), that's an EXPLOIT demonstration — hard fail. + per_probe = { + run_probe = true, + record_outcome = true, + honour_expected_outcome = true, + on_unexpected_exploit_success = 'fail, # exploit found where it shouldn't be + scope_enforcement = 'this_repo_only, # never touch external systems + timeout_honouring = 'strict, + }, + + # Evidence sinks — BOTH written, every execution. VeriSimDB is the + # queryable proof-attempt / contractile-execution store for echidna. + evidence_sinks = [ + { + kind = 'verisimdb, + table = "contractile_executions", + schema = "contractile_execution_v1", + # trust-specific sub-tables for verification + probe outcomes (threat-model audit) + aux_tables = [ "trust_verifications", "trust_probes" ], + }, + { + kind = 'drift_log, + path = ".machine_readable/6a2/DRIFT.a2ml", + append_only = true, + }, + ], + + # Session-close hook — re-verify EVERYTHING, re-run probes, diff against + # last ratification. The "turn off the firewall" scenario (sandbox removed, + # axiom tracker disabled, %default total stripped) must be caught here if + # it wasn't caught at pre-push. + on_close = { + re_execute_all_verifications = true, + re_run_all_safe_hacking_probes = true, + diff_against_last_ratification = true, + emit_drift_entries_for_new_failures = true, + surface_expired_variances = true, + # trust-specific: if any blocking-severity verification is newly failing, + # session close is BLOCKED from completing. + block_session_close_on_critical_drift = true, + }, + + # ----------------------------------------------------------------- + # Session-open hook — NEGOTIATION + RATIFICATION + ACCOUNTABILITY + # (inherited shape from the intend/adjust k9 pattern; trust-specific + # threat-model foregrounding below) + # ----------------------------------------------------------------- + on_open = { + # --- Context presentation --- + render_summary = 'plain_language, # metaphor-capture defense + include_drift_log_from_last_close = true, + include_active_variances = true, + include_recent_anchors = true, + anchor_lookback_weeks = 8, + + # trust-specific: the threat model is rendered FIRST, before any + # negotiation, so the adversary and stakes are fresh in both minds. + # Directly defends against B1 (threat-model misclassification) — for + # echidna the stakes are an UNSOUND PROOF leaving the dispatcher + # (axiom smuggled in, sandbox bypassed, ABI totality stripped). + threat_model_foregrounding = { + required = true, + render_adversaries = true, # untrusted solver binary, malicious proof artifact, supply-chain action + render_stakes = true, # unsound proof emitted = trust violation + render_compliance_regimes = true, + render_audience_sensitivity = true, + # If the AI is about to suggest a trust-weakening action (disable a + # sandbox, relax axiom tracking, drop a SHA pin), it must re-render + # the threat model before the suggestion lands. + re_render_before_weakening_suggestion = true, + }, + + # --- Negotiation phase (five mandatory inputs) --- + negotiation = { + required = true, + ai_required_inputs = [ + 'timeline_realism, + 'industry_standards, # for echidna: solver-soundness practice, supply-chain SLSA, SHA-pinning + 'audience_feasibility, # who is the adversary? (untrusted solver / proof artifact / action) + 'resulting_invariants, # which T### entries the work creates/amends + 'ecosystem_dependencies, # BLAKE3/SHAKE3, Podman/bwrap, Idris2 totality, GPG signing infra + ], + user_engagement_required = true, + user_engagement_mode = 'per_input_response, + specification_translation = { + ai_produces_spec_form = true, + user_reviews_in_domain_language = true, + schema_authoring_is_ai_responsibility = true, + translation_faithfulness_auditable = true, + # trust-specific: the AI renders sandbox policy, integrity hash + # choice, axiom danger levels in domain language ("solvers run in + # bubblewrap, binaries BLAKE3-verified, Reject-level axioms blocked") + # rather than forcing the user into Nickel-schema authoring. + }, + }, + + # --- Accountability pledge (both parties, explicit) --- + # trust's pledge is MORE stringent than adjust's because the authority + # is blocking. Accepting accountability here means accepting that + # security-affecting decisions have blocking consequence. + accountability_pledge = { + required = true, + parties = [ + { + role = 'user, + pledge = "I have reviewed the threat model, the declared T### trust obligations, and the dispatcher trust boundary (no proof emits without axiom-tracking, integrity, and sandboxing). I accept accountability for meeting these obligations and understand that failed verification will block merges until resolved or varied. I will not attempt to disable a verification, sandbox, or the axiom tracker to unblock a merge; I will raise a variance or amendment instead. Licence findings (T001) are handled manually, per-file — never auto-edited.", + signature_required = true, + }, + { + role = 'ai_agent, + pledge = "I will hold the line on declared T### trust obligations. I will refuse to 'disable' verifications, solver sandboxing, axiom tracking, or ABI totality to unblock merges; I will refuse security-weakening suggestions that contradict the threat model even when the user is enthusiastic; I will surface drift at session close; I will re-render the threat model before proposing any weakening action. I will NEVER auto-edit a licence header (T001 is flag-only). If a legitimate scope shift demands security reduction, I will require a variance with severity acknowledgement or an amendment, not silent acceptance.", + signature_required = true, + }, + ], + signed_record_destination = ".machine_readable/6a2/ratification-.a2ml", + must_precede_work = true, + }, + + ratification_record_shape = { + includes_negotiation_transcript = true, + includes_both_pledges = true, + includes_threat_model_snapshot = true, # trust-specific + signed = true, + dated = true, + session_id = 'required, + contract_hash = 'required, + }, + }, + }, + + # ------------------------------------------------------------------- + # Failure-mode defenses — trust is the widest-coverage verb. + # See feedback_ai_failure_mode_catalog.md for the full catalog. + # ------------------------------------------------------------------- + failure_mode_defenses = [ + # Category A — enthusiasm / narrative capture + 'A1_enthusiasm_capture, # scope breach blocks via blocking authority + 'A2_metaphor_capture, # render_summary + re_render_before_weakening + # Category B — threat-model misclassification (trust's flagship defense) + 'B1_threat_model_misclass, # threat_model_foregrounding = required + 'B2_audience_sensitivity_collapse, # audience_feasibility in negotiation + 'B3_compliance_prior_drift, # industry_standards in negotiation + # Category C — scope/capability erosion (the "firewall off" scenario) + 'C2_capability_collapse, # blocking gate prevents silent sandbox/axiom-track removal + 'C3_helpfulness_inflation, # trust-affecting changes need variance/amendment + 'C4_modernization_drift, # unrequested crypto-lib / solver upgrade caught + # Category D — epistemic failures + 'D4_error_hiding, # on_unmet = 'fail makes hiding impossible + 'D5_sycophancy, # pledge forces AI to hold line against enthusiasm + 'D6_false_pessimism, # negotiation requires AI to cite constraint, not assert impossibility + # Category E — refactor/churn + 'E4_cargo_cult_security, # probes VERIFY the claimed protection actually runs (sandbox, integrity) + # Category F — session drift + 'F1_across_session_forgetting, # on_open reads last-ratification, drift log, recent ANCHORs + ], +} diff --git a/.machine_readable/contractiles/trust/trust.manifest.a2ml b/.machine_readable/contractiles/trust/trust.manifest.a2ml new file mode 100644 index 00000000..142e8528 --- /dev/null +++ b/.machine_readable/contractiles/trust/trust.manifest.a2ml @@ -0,0 +1,64 @@ +# SPDX-License-Identifier: MPL-2.0 +# trust.manifest.a2ml — Trident coherence manifest for the trust verb. +# Author: Jonathan D.A. Jewell +# +# Asserts: exactly three files constitute the trust trident; their +# content-hashes are pinned here; cross-references round-trip; no +# partial publication is permitted. +# +# The contractile CLI's `verify trust` subcommand MUST: +# 1. Confirm all three listed files exist at the declared paths. +# 2. Compute each file's sha256 and match against the pinned value. +# 3. Follow each cross-reference and confirm the target file's +# reciprocal field points back. +# 4. Refuse the dir (exit non-zero) if any of 1–3 fails. +# +# trust is the security + provenance + safe-hacking verb for echidna; one of +# the (Hunt, blocking) triple (must + trust + bust). Primary defense against +# threat-model misclassification (B1) and the "turn off the firewall" +# capability-collapse (C2) — for echidna this means an unsound proof leaving +# the dispatcher (axiom smuggled in, solver sandbox bypassed, Idris2 ABI +# totality stripped). Error namespace T### maps onto Trustfile.a2ml sections. + +--- +trident_version = "1.0.0" +verb = "trust" +semantics = "security + provenance + safe-hacking" +cardinality = "one per repo" +authority = "blocking (hard gate)" + +[[files]] +role = "declaration" +path = "Trustfile.a2ml" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" +notes = "Pre-existing echidna declaration. T### namespace across Integrity / Provenance / Supply-Chain / Dispatcher-Trust / Sandbox / ABI-Trust. Maximal-trust-by-default agent action policy; deny list (no force-push-to-main, no auto licence-header edits). Covers GPG-signed commits/tags, SHA-pinned actions, Cargo.lock, axiom tracker, solver sandboxing (Podman/bwrap), BLAKE3/SHAKE3 integrity, Idris2 %default total + zero believe_me." + +[[files]] +role = "runner" +path = "trust.ncl" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" +notes = "Runner schema covers verifications (T### + section tag + severity + recovery) + safe_hacking with authorised-probes-only and this_repo_only scope enforcement. allow_filesystem_write = false; on_any_fail = exit-nonzero (hard gate)." + +[[files]] +role = "k9_component" +path = "trust.k9.ncl" +sha256 = "pending-first-verify" +size_bytes = "pending-first-verify" +notes = "Trust-tier Hunt with blocking authority. on_open foregrounds threat model before negotiation; block_session_close_on_critical_drift; VeriSimDB evidence sink (trust_verifications + trust_probes). T001 licence findings are flag-only (no automated licence edits)." + +[cross_refs] +runner_paired_xfile = "Trustfile.a2ml" +k9_paired_xfile = "../trust/Trustfile.a2ml" +k9_paired_runner = "../trust/trust.ncl" + +[signed_by] +user = "Jonathan D.A. Jewell" +date = "2026-04-18" +context = "trust trident — completes the (Hunt, blocking) triple (must + trust + bust) for echidna. Primary catchment for the 'turn off the firewall' class of drift: dispatcher trust boundary (axiom-track + integrity + sandbox before any proof emits), solver sandboxing, Idris2 ABI totality, supply-chain SHA-pinning, GPG-signed provenance." + +[[history]] +date = "2026-04-18" +event = "trident-born" +note = "Trustfile.a2ml pre-existed (T### namespace, six sections, maximal-trust-by-default agent policy). This manifest + trust.ncl + trust.k9.ncl complete the trident. Hunt tier + blocking authority — on_unmet = 'fail (opposite of adjust's advisory drift-log). Inherits on_open negotiation + accountability + plain-language-translation from the intend/adjust k9 pattern; adds trust-specific threat_model_foregrounding + block_session_close_on_critical_drift + stricter accountability pledge (user cannot disable verification/sandbox/axiom-tracking to unblock merges; T001 licence findings flag-only per feedback_no_automated_licence_edits)." diff --git a/.machine_readable/contractiles/trust/trust.ncl b/.machine_readable/contractiles/trust/trust.ncl new file mode 100644 index 00000000..5ff0e139 --- /dev/null +++ b/.machine_readable/contractiles/trust/trust.ncl @@ -0,0 +1,101 @@ +# SPDX-License-Identifier: MPL-2.0 +# Trust — security + provenance + safe-hacking runner +# +# Pairs with: Trustfile.a2ml (same directory) +# Verb: trust +# Semantics: integrity / provenance / supply-chain / dispatcher-trust / +# sandbox / ABI-trust verification (Trustfile T### namespace) +# PLUS a declared "safe hacking + testing" section — authorised +# offensive probes (fuzz, injection, auth-bypass, chaos) scoped +# to the repo under test, NEVER touching external systems. +# States what the agent / CI is ALLOWED to do, the trust +# boundary, and what must be verifiable before any proof leaves +# the dispatcher. +# CLI: `contractile trust verify` → run all verifications (read-only) +# `contractile trust probe` → run declared safe-hacking probes +# +# Anything else in this directory is human-only notes/archive; machines ignore. +# +# Base: ../_base.ncl provides pedigree_schema, run_defaults, probe_schema. +# See: docs/CONTRACTILE-SPEC.adoc + +let base = import "../_base.ncl" in + +{ + pedigree = base.pedigree_schema & { + contractile_verb = "trust", + semantics = "security + provenance + safe-hacking", + security = { + leash = 'Hunt, + trust_level = "verification + authorised-probe + hard-gate", + allow_network = false, # verifications are offline by default + allow_filesystem_write = false, # trust writes NOTHING (evidence sinks indirected) + allow_subprocess = true, + authorised_probes_only = true, # safe_hacking section must explicitly list allowed targets + classes + probe_scope_enforcement = 'this_repo_only, # probes NEVER hit external systems + # echidna-bespoke: trust states the dispatcher trust boundary — no proof + # result leaves dispatch.rs without axiom-tracking + integrity + sandboxing. + dispatcher_trust_boundary = "no-proof-emits-without-axiom-track-integrity-sandbox", + error_namespace = "T###", + }, + metadata = { + name = "trust-runner", + version = "1.0.0", + description = "Evaluates the Trustfile.a2ml T### security/provenance/supply-chain obligations plus authorised safe-hacking probes. All probes are scoped to the repo under test; never hits external systems. Hard-gates: failed verification blocks merge.", + paired_xfile = "Trustfile.a2ml", + author = "Jonathan D.A. Jewell ", + }, + }, + + schema = { + verifications + | Array { + id | String, # T### error-code (e.g. "T001-license-content") + description | String, + # TODO: migrate to base.probe_schema (structured probe) when CLI supports it + probe | String, # read-only; exit 0 = pass + # status_core values: 'declared, 'verified, 'failing + status | [| 'declared, 'verified, 'failing |] | default = 'declared, + # trust uses all four severity levels (mirrors Trustfile severity:) + severity | [| 'critical, 'high, 'medium, 'low |] | default = 'high, + # echidna-bespoke: which Trustfile section the T### entry belongs to + section | [| 'integrity, 'provenance, 'supply_chain, 'dispatcher_trust, 'sandbox, 'abi_trust |] | optional, + recovery | String | optional, # mirrors Trustfile `recovery:` (never auto-edit licences) + notes | String | optional, + }, + + # Safe-hacking + testing section. Each probe here is an ACTIVELY EXECUTED + # test — fuzz runs, chaos probes, auth-bypass attempts, injection tests, + # solver-sandbox escape attempts. All scoped to the current repo. + safe_hacking + | { + scope | String, # e.g. "this-repo-only" / "localhost" + allowed_probe_classes + | Array [| 'fuzz, 'property_test, 'chaos, 'auth_bypass, 'injection, 'timing, 'sandbox_escape |] + | default = [], + probes + | Array { + id | String, + class | [| 'fuzz, 'property_test, 'chaos, 'auth_bypass, 'injection, 'timing, 'sandbox_escape |], + description | String, + # TODO: migrate to base.probe_schema (structured probe) when CLI supports it + probe | String, # command to run the probe + expected_outcome | [| 'probe_blocks_attempt, 'probe_finds_no_issue |], + timeout_seconds | Number | default = 300, + notes | String | optional, + } + | default = [], + } + | default = { scope = "this-repo-only", allowed_probe_classes = [], probes = [] }, + }, + + # Runner behaviour — inherits from base.run_defaults. + # trust is BLOCKING (must + trust + bust triple); has an extra field for + # unexpected safe-hacking outcomes. + run = base.run_defaults & { + on_any_fail = "exit-nonzero", # hard gate on verifications + safe_hacking_on_unexpected_outcome = "exit-nonzero", # probe found what it shouldn't = block + report_format = "a2ml", + emit_summary = true, + }, +} From 1a33cf0f5802a7edcf8d29174f925a9a8dd5c660 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 14 Jun 2026 18:32:41 +0100 Subject: [PATCH 3/7] docs(governance): add GOVERNANCE.adoc (required by must/Mustfile governance-docs check) Co-Authored-By: Claude Opus 4.8 (1M context) --- GOVERNANCE.adoc | 162 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 162 insertions(+) create mode 100644 GOVERNANCE.adoc diff --git a/GOVERNANCE.adoc b/GOVERNANCE.adoc new file mode 100644 index 00000000..8bbf167d --- /dev/null +++ b/GOVERNANCE.adoc @@ -0,0 +1,162 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell += Governance Model +:toc: preamble + +This document describes the governance model for this repository. + +== Overview + +This repository follows a **Sole Maintainer Governance Model**: + +* Single maintainer (@hyperpolymath) has full authority over the project +* All contributions are welcome and reviewed by the maintainer +* Decisions are made transparently through GitHub issues and discussions +* The project adheres to the hyperpolymath estate policies where applicable + +== Core Principles + +[cols="1,2"] +|=== +| Principle | Description + +| **Benevolent Dictatorship** | Maintainer has final decision authority but seeks community input + +| **Meritocracy** | Contributions are judged on technical merit, not contributor identity + +| **Transparency** | All significant decisions are documented publicly + +| **Consensus-Seeking** | Maintainer prefers consensus but will decide when necessary + +| **Open Contribution** | Anyone can contribute via fork and pull request + +|=== + +== Roles and Permissions + +[cols="1,2,2"] +|=== +| Role | Permissions | Assignment + +| **Maintainer** | Write access, merge rights, admin | @hyperpolymath +| **Contributors** | Read access, fork, submit PRs | All GitHub users +| **Users** | Use the software, report issues | All GitHub users + +|=== + +== Decision Making Framework + +=== Routine Decisions + +* Bug fixes +* Documentation improvements +* Minor feature additions +* Dependency updates + +**Process**: Maintainer reviews and merges PRs that meet quality standards. + +=== Significant Changes + +* New major features +* API changes +* Architecture modifications +* Breaking changes + +**Process**: +. Open issue describing the change +. Discuss with community (minimum 72 hours) +. Maintainer makes final decision +. Document rationale in issue/PR + +=== Structural Decisions + +* Repository purpose/renaming +* License changes +* Ownership transfer +* Deprecation/archival + +**Process**: +. Extended discussion (minimum 1 week) +. Maintainer makes final decision +. Document in CHANGELOG and governance docs + +== Contribution Lifecycle + +[cols="1,2"] +|=== +| Stage | Process + +| **Ideation** | Open issue, discuss feasibility + +| **Development** | Fork, implement, test thoroughly + +| **Review** | Submit PR, maintainer reviews within 7 days + +| **Merge** | Maintainer merges or requests changes + +| **Release** | Maintainer publishes according to project conventions + +|=== + +== Conflict Resolution + +In case of disagreements: + +. Discuss in the relevant GitHub issue or PR +. Provide technical justification for positions +. Maintainer mediates and makes final decision +. Decision is documented and can be revisited later + +== Project Policies + +This repository adheres to hyperpolymath estate-wide policies: + +* **License**: MPL-2.0 for code, CC-BY-SA-4.0 for prose (per standards/LICENCE-POLICY.adoc) +* **Code of Conduct**: Follows hyperpolymath CODE_OF_CONDUCT.md +* **Security**: Follows hyperpolymath SECURITY.md +* **Contributing**: Follows hyperpolymath CONTRIBUTING.adoc conventions + +== Repository-Specific Conventions + +[cols="1,2"] +|=== +| Convention | Description + +| **Signing** | All commits must be signed (SSH or GPG) + +| **SPDX Headers** | All source files must have SPDX license identifiers + +| **Contractiles** | Mustfile, Trustfile, Intendfile, Adjustfile in root + +| **Machine Readable** | META.a2ml in .machine_readable/6a2/ + +| **CI/CD** | GitHub Actions workflows in .github/workflows/ + +|=== + +== Governance Evolution + +As the project grows, this governance model may evolve: + +* **Adding Co-Maintainers**: When contribution volume warrants it +* **Forming a Team**: For complex multi-maintainer projects +* **Adopting TPCF**: For large, multi-repository projects (see rhodium-standard-repositories) + +Changes to this document require the same process as Significant Changes above. + +== See Also + +* link:MAINTAINERS.adoc[Maintainers] +* link:CODE_OF_CONDUCT.md[Code of Conduct] +* link:CONTRIBUTING.adoc[Contributing Guide] +* link:https://github.com/hyperpolymath/standards/blob/main/LICENCE-POLICY.adoc[Estate License Policy] +* link:https://github.com/hyperpolymath/standards[rhodium-standard-repositories (TPCF)] + +== Changelog + +[cols="1,1,1"] +|=== +| Date | Change | By + +| 2026-06-07 | Initial governance model established | @hyperpolymath +|=== From c468aa901d810c7f0f498450aff41108f1212f66 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 14 Jun 2026 18:54:14 +0100 Subject: [PATCH 4/7] fix(ci): complete TruffleHog secret-scan migration; remove gitleaks MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Finish the half-applied gitleaks -> TruffleHog swap and repair the botched parts of the 2026-06-11 sweep: - .pre-commit-config.yaml: remove the orphaned/broken gitleaks block (dangling rev:/hooks: after the repo/id lines were stripped). Secret scanning is enforced CI-side via TruffleHog. - echidna-playground/.github/workflows/security-checks.yml: replace the broken gitleaks step (name + env, no uses:) with a SHA-pinned TruffleHog step (@8a8ef85 # v3); drop the redundant unpinned trufflehog@main job. - echidna-playground/.github/workflows/secret-scanner.yml: gitleaks job removed; SHA-pinned TruffleHog retained. - .gitlab-ci.yml: TruffleHog job in the existing `security` stage (image :latest, matching the estate's existing references). - Justfile: local `secret-scan-trufflehog` convenience recipe. The redundant unpinned inline trufflehog@main that the sweep added to the main secret-scanner.yml was dropped — that workflow already runs TruffleHog via the standards secret-scanner-reusable.yml. NOTE: the shared standards/secret-scanner-reusable.yml still bundles gitleaks; removing it there (estate-wide, separate repo) is the remaining step for the reusable path to be fully gitleaks-free. Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitlab-ci.yml | 6 ++++++ .pre-commit-config.yaml | 7 +++---- Justfile | 3 +++ .../.github/workflows/secret-scanner.yml | 20 +------------------ .../.github/workflows/security-checks.yml | 20 ++++--------------- 5 files changed, 17 insertions(+), 39 deletions(-) diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index ccc8ff71..7e7fdba4 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -302,3 +302,9 @@ deploy-release: url: "${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}" only: - tags + +trufflehog: + stage: security + image: trufflesecurity/trufflehog:latest + script: + - trufflehog git file://. --only-verified --fail diff --git a/.pre-commit-config.yaml b/.pre-commit-config.yaml index 29d0fefa..9088b338 100644 --- a/.pre-commit-config.yaml +++ b/.pre-commit-config.yaml @@ -46,7 +46,6 @@ repos: exclude: '(\.git|node_modules|target|_build|deps|\.deno|external_corpora|\.lake)/' # --- Secret detection --- - - repo: https://github.com/gitleaks/gitleaks - rev: v8.24.3 - hooks: - - id: gitleaks + # Secret scanning is enforced in CI via TruffleHog + # (.github/workflows/secret-scanner.yml). gitleaks removed 2026-06-14 + # (estate standardisation on TruffleHog). diff --git a/Justfile b/Justfile index a490c523..f7255fe0 100644 --- a/Justfile +++ b/Justfile @@ -1072,3 +1072,6 @@ er-schema-drift-check: | sha256sum | head -c 64 @echo " (current combined hash; compare against .machine_readable/er-schema.sha256 when CI gate lands)" + +secret-scan-trufflehog: + @command -v trufflehog >/dev/null && trufflehog filesystem . --only-verified || true diff --git a/echidna-playground/.github/workflows/secret-scanner.yml b/echidna-playground/.github/workflows/secret-scanner.yml index b2dcc860..0899a46e 100644 --- a/echidna-playground/.github/workflows/secret-scanner.yml +++ b/echidna-playground/.github/workflows/secret-scanner.yml @@ -1,46 +1,28 @@ # SPDX-License-Identifier: MPL-2.0 # Prevention workflow - scans for hardcoded secrets before they reach main name: Secret Scanner - on: pull_request: push: branches: [main] - permissions: read-all - jobs: trufflehog: runs-on: ubuntu-latest steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 with: - fetch-depth: 0 # Full history for scanning - + fetch-depth: 0 # Full history for scanning - name: TruffleHog Secret Scan uses: trufflesecurity/trufflehog@8a8ef8526528d8a4ff3e2c90be08e25ef8efbd9b # v3 with: extra_args: --only-verified --fail - - gitleaks: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - with: - fetch-depth: 0 - - - name: Gitleaks Secret Scan - uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - # Rust-specific: Check for hardcoded crypto values rust-secrets: runs-on: ubuntu-latest if: hashFiles('**/Cargo.toml') != '' steps: - uses: actions/checkout@b4ffde65f46336ab88eb53be808477a3936bae11 # v4 - - name: Check for hardcoded secrets in Rust run: | # Patterns that suggest hardcoded secrets diff --git a/echidna-playground/.github/workflows/security-checks.yml b/echidna-playground/.github/workflows/security-checks.yml index 2ea5346c..658f613c 100644 --- a/echidna-playground/.github/workflows/security-checks.yml +++ b/echidna-playground/.github/workflows/security-checks.yml @@ -2,15 +2,12 @@ # RSR-compliant security validation workflow with SHA-pinned actions name: Security Checks - on: push: branches: ["main"] pull_request: branches: ["main"] - permissions: read-all - jobs: secret-scanning: name: Detect Secrets @@ -22,12 +19,10 @@ jobs: uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 with: fetch-depth: 0 - - - name: Detect secrets with Gitleaks - uses: gitleaks/gitleaks-action@ff98106e4c7b2bc287b24eaf42907196329070c7 # v2.3.8 - env: - GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }} - + - name: Detect secrets with TruffleHog + uses: trufflesecurity/trufflehog@8a8ef8526528d8a4ff3e2c90be08e25ef8efbd9b # v3 + with: + extra_args: --only-verified --fail dependency-review: name: Dependency Review runs-on: ubuntu-latest @@ -38,14 +33,12 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Dependency Review uses: actions/dependency-review-action@da24556b548a50705dd671f47852072ea4c105d9 # v4.5.0 with: fail-on-severity: high deny-licenses: GPL-3.0, AGPL-3.0 comment-summary-in-pr: always - validate-actions: name: Validate GitHub Actions runs-on: ubuntu-latest @@ -54,7 +47,6 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Check for unpinned actions run: | echo "Checking for unpinned GitHub Actions..." @@ -65,7 +57,6 @@ jobs: else echo "All actions appear to be SHA-pinned" fi - - name: Check for dangerous permissions run: | echo "Checking for overly permissive workflow permissions..." @@ -76,7 +67,6 @@ jobs: exit 1 fi echo "No dangerous permissions found" - file-validation: name: Validate Security Files runs-on: ubuntu-latest @@ -85,7 +75,6 @@ jobs: steps: - name: Checkout repository uses: actions/checkout@11bd71901bbe5b1630ceea73d27597364c9af683 # v4.2.2 - - name: Check required security files exist run: | MISSING="" @@ -99,7 +88,6 @@ jobs: exit 1 fi echo "All required security files present" - - name: Check for template placeholders run: | echo "Checking for unfilled template placeholders..." From e80c870476cb2bc93575a0f1f85fce103cb7720f Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 21 Jun 2026 01:00:36 +0100 Subject: [PATCH 5/7] ci: adopt standards reusable workflows for Scorecard, Hypatia, and Governance --- .github/workflows/governance.yml | 24 +---- .github/workflows/hypatia-scan.yml | 19 +--- .github/workflows/scorecard-enforcer.yml | 108 ----------------------- .github/workflows/scorecard.yml | 23 +++-- 4 files changed, 17 insertions(+), 157 deletions(-) delete mode 100644 .github/workflows/scorecard-enforcer.yml diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 9c7bb956..26742639 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -1,34 +1,16 @@ -# SPDX-License-Identifier: MPL-2.0 -# governance.yml — single wrapper calling the shared estate governance bundle -# in hyperpolymath/standards instead of carrying per-repo copies. -# -# Replaces the per-repo governance scaffolding removed in the same commit: -# quality.yml, guix-nix-policy.yml, npm-bun-blocker.yml, ts-blocker.yml, -# security-policy.yml, rsr-antipattern.yml, wellknown-enforcement.yml, -# workflow-linter.yml -# -# Load-bearing build/security workflows stay standalone in the repo -# (rust-ci, codeql, dependabot, release, scan/mirror/pages plumbing). - +# SPDX-License-Identifier: PMPL-1.0-or-later name: Governance on: push: branches: [main, master] pull_request: + branches: [main, master] workflow_dispatch: -# Estate guardrail: cancel superseded runs so re-pushes / rebased PR -# updates do not pile up queued runs against the shared account-wide -# Actions concurrency pool. Applied only to read-only check workflows -# (no publish/mutation), so cancelling a superseded run is always safe. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - permissions: contents: read jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@3f34549c03274ec7a74683068f03a492b0fa805f # main 2026-06-01 (R5 generic — standards#330; consumes .github/canonical-references/) + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@b89b2ef1e98928fce53a85e83c37f23a1d99f6d3 diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index a711616a..736b63f8 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -1,7 +1,4 @@ -# SPDX-License-Identifier: MPL-2.0 -# Thin wrapper around hyperpolymath/standards hypatia-scan-reusable.yml. -# See standards#191 for the reusable's purpose and design. - +# SPDX-License-Identifier: PMPL-1.0-or-later name: Hypatia Security Scan on: @@ -13,18 +10,10 @@ on: - cron: '0 0 * * 0' workflow_dispatch: -# Estate guardrail: cancel superseded runs so re-pushes don't pile up. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - permissions: contents: read - security-events: write - pull-requests: write + security-events: read jobs: - hypatia: - uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@6cd3772824e59c8c9affeab66061e25383544242 - timeout-minutes: 10 - secrets: inherit + scan: + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@b89b2ef1e98928fce53a85e83c37f23a1d99f6d3 diff --git a/.github/workflows/scorecard-enforcer.yml b/.github/workflows/scorecard-enforcer.yml deleted file mode 100644 index bc71a0aa..00000000 --- a/.github/workflows/scorecard-enforcer.yml +++ /dev/null @@ -1,108 +0,0 @@ -# SPDX-License-Identifier: MPL-2.0 -# Prevention workflow - runs OpenSSF Scorecard and fails on low scores -name: OpenSSF Scorecard Enforcer - -on: - push: - branches: [main] - schedule: - - cron: '0 6 * * 1' # Weekly on Monday - workflow_dispatch: - -# Estate guardrail: cancel superseded runs so re-pushes / rebased PR -# updates do not pile up queued runs against the shared account-wide -# Actions concurrency pool. Applied only to read-only check workflows -# (no publish/mutation), so cancelling a superseded run is always safe. -concurrency: - group: ${{ github.workflow }}-${{ github.ref }} - cancel-in-progress: true - -permissions: - contents: read - -jobs: - # The OSSF Scorecard publish endpoint enforces a hard contract: the job that - # runs `ossf/scorecard-action` with `publish_results: true` must contain - # ONLY steps with `uses:` (no `run:` steps in the same job). If a `run:` - # step is present, the publish step fails with: - # "webapp: scorecard job must only have steps with uses" - # (49 estate repos hit this; see ROADMAP audit 2026-05-30.) - # - # Fix: split the threshold check into a downstream job that depends on - # `scorecard` and consumes the SARIF artifact. The `scorecard` job stays - # uses-only; `check-score` is the gating job that emits the error. - scorecard: - runs-on: ubuntu-latest - permissions: - security-events: write - id-token: write # For OIDC - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - with: - persist-credentials: false - - - name: Run Scorecard - uses: ossf/scorecard-action@4eaacf0543bb3f2c246792bd56e8cdeffafb205a # v2.4.3 - with: - results_file: results.sarif - results_format: sarif - publish_results: true - - - name: Upload SARIF - uses: github/codeql-action/upload-sarif@8aad20d150bbac5944a9f9d289da16a4b0d87c1e # v4 - with: - sarif_file: results.sarif - - - name: Persist SARIF for downstream score-gate job - uses: actions/upload-artifact@043fb46d1a93c77aae656e7c1c64a875d1fc6a0a # v7.0.1 - with: - name: scorecard-results - path: results.sarif - retention-days: 1 - - check-score: - needs: scorecard - runs-on: ubuntu-latest - permissions: - contents: read - steps: - - name: Download SARIF from scorecard job - uses: actions/download-artifact@3e5f45b2cfb9172054b4087a40e8e0b5a5461e7c # v5.0.0 - with: - name: scorecard-results - - - name: Check minimum score - run: | - SCORE=$(jq -r '.runs[0].tool.driver.properties.score // 0' results.sarif 2>/dev/null || echo "0") - - echo "OpenSSF Scorecard Score: $SCORE" - - # Minimum acceptable score (0-10 scale) - MIN_SCORE=5 - - if [ "$(echo "$SCORE < $MIN_SCORE" | bc -l)" = "1" ]; then - echo "::error::Scorecard score $SCORE is below minimum $MIN_SCORE" - exit 1 - fi - - # Check specific high-priority items - check-critical: - runs-on: ubuntu-latest - steps: - - uses: actions/checkout@df4cb1c069e1874edd31b4311f1884172cec0e10 # v6.0.3 - - - name: Check SECURITY.md exists - run: | - if [ ! -f "SECURITY.md" ]; then - echo "::error::SECURITY.md is required" - exit 1 - fi - - - name: Check for pinned dependencies - run: | - # Check workflows for unpinned actions - unpinned=$(grep -r "uses:.*@v[0-9]" .github/workflows/*.yml 2>/dev/null | grep -v "#" | head -5 || true) - if [ -n "$unpinned" ]; then - echo "::warning::Found unpinned actions:" - echo "$unpinned" - fi diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 6c72100a..0c31a3e1 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -1,19 +1,16 @@ -# SPDX-License-Identifier: MPL-2.0 -name: Scorecards supply-chain security +# SPDX-License-Identifier: PMPL-1.0-or-later +name: OSSF Scorecard on: - branch_protection_rule: - schedule: - - cron: '23 4 * * 1' push: - branches: [main] + branches: [main, master] + schedule: + - cron: '0 4 * * *' + workflow_dispatch: -permissions: read-all +permissions: + contents: read jobs: - analysis: - permissions: - security-events: write - id-token: write - uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@e03686486e11b662834d7090dffae54c3e96fd59 # main 2026-05-28 (SPDX bump #249) - secrets: inherit + scorecard: + uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@b89b2ef1e98928fce53a85e83c37f23a1d99f6d3 From 06029bea0cad930ed98b2e5d4b9648785b6f695a Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 21 Jun 2026 01:15:30 +0100 Subject: [PATCH 6/7] ci: adopt standards reusable workflows for Scorecard, Hypatia, and Governance --- .github/workflows/governance.yml | 2 +- .github/workflows/hypatia-scan.yml | 2 +- .github/workflows/scorecard.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 26742639..31d497f5 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -13,4 +13,4 @@ permissions: jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@b89b2ef1e98928fce53a85e83c37f23a1d99f6d3 + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@5a93d9da1bbf6ca9eb4eec89e900c733f114c995 diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index 736b63f8..ce9ce4ce 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -16,4 +16,4 @@ permissions: jobs: scan: - uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@b89b2ef1e98928fce53a85e83c37f23a1d99f6d3 + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@5a93d9da1bbf6ca9eb4eec89e900c733f114c995 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 0c31a3e1..987033d4 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -13,4 +13,4 @@ permissions: jobs: scorecard: - uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@b89b2ef1e98928fce53a85e83c37f23a1d99f6d3 + uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@5a93d9da1bbf6ca9eb4eec89e900c733f114c995 From 06b153aca26cebcc3e0ad1c686c446032fbb1a40 Mon Sep 17 00:00:00 2001 From: hyperpolymath <6759885+hyperpolymath@users.noreply.github.com> Date: Sun, 21 Jun 2026 01:20:26 +0100 Subject: [PATCH 7/7] ci: adopt standards reusable workflows for Scorecard, Hypatia, and Governance --- .github/workflows/governance.yml | 2 +- .github/workflows/hypatia-scan.yml | 2 +- .github/workflows/scorecard.yml | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) diff --git a/.github/workflows/governance.yml b/.github/workflows/governance.yml index 31d497f5..8161ec24 100644 --- a/.github/workflows/governance.yml +++ b/.github/workflows/governance.yml @@ -13,4 +13,4 @@ permissions: jobs: governance: - uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@5a93d9da1bbf6ca9eb4eec89e900c733f114c995 + uses: hyperpolymath/standards/.github/workflows/governance-reusable.yml@5a93d9d57cc04de4002d6d0ecd336fc7a8698910 diff --git a/.github/workflows/hypatia-scan.yml b/.github/workflows/hypatia-scan.yml index ce9ce4ce..e7158485 100644 --- a/.github/workflows/hypatia-scan.yml +++ b/.github/workflows/hypatia-scan.yml @@ -16,4 +16,4 @@ permissions: jobs: scan: - uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@5a93d9da1bbf6ca9eb4eec89e900c733f114c995 + uses: hyperpolymath/standards/.github/workflows/hypatia-scan-reusable.yml@5a93d9d57cc04de4002d6d0ecd336fc7a8698910 diff --git a/.github/workflows/scorecard.yml b/.github/workflows/scorecard.yml index 987033d4..47acbb5f 100644 --- a/.github/workflows/scorecard.yml +++ b/.github/workflows/scorecard.yml @@ -13,4 +13,4 @@ permissions: jobs: scorecard: - uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@5a93d9da1bbf6ca9eb4eec89e900c733f114c995 + uses: hyperpolymath/standards/.github/workflows/scorecard-reusable.yml@5a93d9d57cc04de4002d6d0ecd336fc7a8698910