From f2c4a2feb659ff9bd57a5b1ea01ac7235a04f9da Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:15:33 +0100 Subject: [PATCH 1/2] fix(nickel): cure standards' own 20-file Nickel debt (#986) `standards` publishes a Nickel gate it has never run against itself: `ci-pipeline.yml` is `workflow_call:`-only and this repo has no self-caller, so all 20 genuine `.ncl` files carry undetected debt. Measured with the exact binary CI installs (nickel 1.18.0, sha256 9cba4dd6... verified on download): format --check typecheck baseline 20 fail 5 fail after 0 fail 0 fail Selection matches the gate's own pathspec: tracked `*.ncl` excluding `*.k9.ncl` (20 files; the 16 k9 contracts are untouched). WHAT WAS ACTUALLY WRONG #986 described 2 bugs in 5 files. There were 6 distinct defects; 4 are fixed here and 2 are reported below as follow-ups. Each was found only because the previous one stopped masking it. 1. `%{ }` outside a string -- os_detect.ncl, 3 byte-identical copies. `os = %{ "os" } | default = "linux"` is string-interpolation syntax used where a contract belongs, so the file has NEVER parsed as Nickel. Cured as `os | String | default = "linux"` -- the canonical idiom for an overridable injected input, which is what the file's own comment ("detection via environment introspection") and the existing `| default =` already intend. Proven: defaults evaluate (os=linux -> Standard_PC -> nala_native) AND an override works (`& { env.os | force = "darwin" }` -> Apple_Darwin). 2. Record contracts written with `:` -- config.ncl, 2 copies. Nickel's own diagnostic names the cure. #986 quoted `present : Bool` as a one-line fix; it is not. Fixing `present` revealed the same defect on `context`, then on all of `WorkMetadata`, then `ProjectConfig`: a `|`- annotated field disqualifies the enclosing literal from being a record type, so every `:` in these schema records had to become `|`. 3. `std.enum.to_tag` does not exist in the pinned stdlib. Invisible to `typecheck` because `to_json_ld` is annotated `-> _`. Cured with `std.string.from_enum`, whose output is the same string. 4. Infinite recursion in the exported record. Nickel records are recursive, so `validate_work = validate_work` is a self-reference, not a reference to the `let` above it. Distinct aliases bound before the record; the exported field names (the public API) are unchanged. 5. `include` is now a Nickel KEYWORD -- infra.ncl:386. This one is not a code bug and is worth naming: the file typechecks rc=0 while `nickel format` fails to PARSE it, because the formatter's grammar and the evaluator's disagree about a bare field named `include`. Quoting the field name cures it. Proven semantics-preserving on a minimal reproducer: bare and quoted forms `export` to byte-identical JSON. 6. Formatting: the remaining 15 files, `nickel format` (the auto-fix form) run LOCALLY to produce this commit. CI only ever runs `nickel format --check`, which is read-only, per the binding spec's non-destructive rule. STILL BROKEN, DELIBERATELY NOT FIXED HERE `config.ncl` typechecks and formats but still cannot `nickel export` whole, which its own header advertises as its usage. Two pre-existing defects remain, both needing a design decision rather than a bug fix: - `std.record.merge` does not exist either, so `.environments` cannot evaluate. `std.record.merge_all` exists, but the override pattern also needs `| default` priorities on the base record or the merge conflicts. - `.schemas` exports the contract records themselves, whose fields have contracts but no definitions, so they are not exportable data at all. `.config` and `.example` DO evaluate (rc=0), which is what proves defects 3 and 4 above are genuinely fixed rather than merely parsing. Note this class: the gate checks format + typecheck only, so a file that cannot evaluate would still have gone green. That is the same vacuous-gate shape this campaign has been closing elsewhere. VERIFIED BEFORE COMMIT - nickel 1.18.0 downloaded and sha256-checked against the value pinned in ci-pipeline.yml: OK - format --check over all 20: 0 failures (was 20) - typecheck over all 20: 0 failures (was 5) - the 3 os_detect copies byte-identical to each other; the 2 config.ncl copies byte-identical to each other (#986 AC2/AC3) - `git diff --name-only -- '*.k9.ncl'` -> empty; no non-.ncl file touched Closes #986 AC1-AC5. AC6 (a self-caller for ci-pipeline.yml, without which all of the above can silently regress) follows as a separate PR, per owner ruling. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR --- .machine_readable/contractiles/_base.ncl | 16 +- .../contractiles/adjust/adjust.ncl | 71 +++---- .machine_readable/contractiles/bust/bust.ncl | 77 ++++---- .machine_readable/contractiles/dust/dust.ncl | 77 ++++---- .../contractiles/intend/intend.ncl | 85 +++++---- .machine_readable/contractiles/must/must.ncl | 69 +++---- .../contractiles/trust/trust.ncl | 112 +++++------ 1-formats/a2ml/agentic/ncl/lib/os_detect.ncl | 34 ++-- 1-formats/a2ml/agentic/ncl/lib/schema.ncl | 11 +- 1-formats/a2ml/neurosym/ncl/lib/os_detect.ncl | 34 ++-- 1-formats/a2ml/neurosym/ncl/lib/schema.ncl | 11 +- 1-formats/k9/capabilities.ncl | 23 +-- 1-formats/k9/leash.ncl | 31 ++-- 1-formats/k9/pedigree.ncl | 1 - 1-formats/k9/register.ncl | 3 +- .../satellites/palimpsest-license/config.ncl | 175 ++++++++++-------- .../palimpsest-license/config/config.ncl | 175 ++++++++++-------- .../palimpsest-license/config/infra.ncl | 45 +++-- .../rsr-deployer/ncl/lib/os_detect.ncl | 34 ++-- .../rsr-deployer/ncl/lib/schema.ncl | 11 +- 20 files changed, 594 insertions(+), 501 deletions(-) diff --git a/.machine_readable/contractiles/_base.ncl b/.machine_readable/contractiles/_base.ncl index 22306faf5..190df79ed 100644 --- a/.machine_readable/contractiles/_base.ncl +++ b/.machine_readable/contractiles/_base.ncl @@ -38,7 +38,6 @@ # } # # See: docs/CONTRACTILE-SPEC.adoc §Shared Base - { # ------------------------------------------------------------------------- # pedigree_schema @@ -50,11 +49,11 @@ # ------------------------------------------------------------------------- pedigree_schema = { schema_version | String | default = "1.0.0", - contractile_verb | String | default = "UNSET", # MUST override in verb - semantics | String | default = "UNSET", # MUST override in verb + contractile_verb | String | default = "UNSET", # MUST override in verb + semantics | String | default = "UNSET", # MUST override in verb security = { leash | [| 'Kennel, 'Yard, 'Hunt |] | default = 'Kennel, - trust_level | String | default = "UNSET", # MUST override in verb + trust_level | String | default = "UNSET", # MUST override in verb allow_network | Bool | default = false, allow_filesystem_write | Bool | default = false, allow_subprocess | Bool | default = true, @@ -63,10 +62,10 @@ # destructive_mode_requires_flag (dust) }, metadata = { - name | String | default = "UNSET", # MUST override in verb + name | String | default = "UNSET", # MUST override in verb version | String | default = "1.0.0", - description | String | default = "UNSET", # MUST override in verb - paired_xfile | String | default = "UNSET", # MUST override in verb + description | String | default = "UNSET", # MUST override in verb + paired_xfile | String | default = "UNSET", # MUST override in verb author | String | default = "Jonathan D.A. Jewell ", }, }, @@ -118,7 +117,8 @@ allowed_exit_codes | Array Number | default = [0], permission_class | [| 'read_only, 'filesystem_write, 'subprocess, 'network |] - | default = 'read_only, + | default + = 'read_only, }, # ------------------------------------------------------------------------- diff --git a/.machine_readable/contractiles/adjust/adjust.ncl b/.machine_readable/contractiles/adjust/adjust.ncl index d6c24f0ee..f0f074323 100644 --- a/.machine_readable/contractiles/adjust/adjust.ncl +++ b/.machine_readable/contractiles/adjust/adjust.ncl @@ -12,51 +12,54 @@ # # 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 = "adjust", - semantics = "accessibility compliance", - security = { - leash = 'Kennel, - trust_level = "fixes allowed where deterministic", - allow_network = false, - allow_filesystem_write = true, # `adjust fix` may write (deterministic patches only) - allow_subprocess = true, - }, - metadata = { - name = "adjust-runner", - version = "1.0.0", - description = "Evaluates accessibility requirements from Adjustfile.a2ml. Fixes deterministic items; flags the rest for human review.", - paired_xfile = "Adjustfile.a2ml", - author = "Jonathan D.A. Jewell ", + pedigree = + base.pedigree_schema + & { + contractile_verb = "adjust", + semantics = "accessibility compliance", + security = { + leash = 'Kennel, + trust_level = "fixes allowed where deterministic", + allow_network = false, + allow_filesystem_write = true, # `adjust fix` may write (deterministic patches only) + allow_subprocess = true, + }, + metadata = { + name = "adjust-runner", + version = "1.0.0", + description = "Evaluates accessibility requirements from Adjustfile.a2ml. Fixes deterministic items; flags the rest for human review.", + paired_xfile = "Adjustfile.a2ml", + author = "Jonathan D.A. Jewell ", + }, }, - }, schema = { requirements | Array { - id | String, - description | String, - # TODO: migrate to base.probe_schema (structured probe) when CLI supports it - probe | String, - # status_core values: 'declared, 'verified, 'failing; adjust adds 'partial - status | [| 'declared, 'partial, 'verified, 'failing |] | default = 'declared, - compliance | String | optional, # e.g. "WCAG 2.1 AA" - notes | String | optional, - fix | String | optional, # deterministic fix command (optional) - }, + id | String, + description | String, + # TODO: migrate to base.probe_schema (structured probe) when CLI supports it + probe | String, + # status_core values: 'declared, 'verified, 'failing; adjust adds 'partial + status | [| 'declared, 'partial, 'verified, 'failing |] | default = 'declared, + compliance | String | optional, # e.g. "WCAG 2.1 AA" + notes | String | optional, + fix | String | optional, # deterministic fix command (optional) + }, }, # Runner behaviour — inherits from base.run_defaults. # adjust is advisory (continue-with-warnings) not a hard gate. # auto_fix_when_available is adjust-specific. - run = base.run_defaults & { - on_any_fail = "continue-with-warnings", # accessibility is progress-tracked, not a hard gate by default - report_format = "a2ml", - emit_summary = true, - auto_fix_when_available = true, - }, + run = + base.run_defaults + & { + on_any_fail = "continue-with-warnings", # accessibility is progress-tracked, not a hard gate by default + report_format = "a2ml", + emit_summary = true, + auto_fix_when_available = true, + }, } diff --git a/.machine_readable/contractiles/bust/bust.ncl b/.machine_readable/contractiles/bust/bust.ncl index fc8cb8cd2..f273168ff 100644 --- a/.machine_readable/contractiles/bust/bust.ncl +++ b/.machine_readable/contractiles/bust/bust.ncl @@ -14,53 +14,56 @@ # # 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 = "bust", - semantics = "error handling + failure recovery", - security = { - leash = 'Kennel, - trust_level = "controlled failure injection; scoped to system-under-test", - allow_network = false, - allow_filesystem_write = true, # drills may write transient state (tmp dirs, test DBs) - allow_subprocess = true, - injection_scope = "system-under-test-only", - }, - metadata = { - name = "bust-runner", - version = "1.0.0", - description = "Exercises declared failure modes and verifies recovery paths. Hard-gates on any failure mode without working recovery.", - paired_xfile = "Bustfile.a2ml", - author = "Jonathan D.A. Jewell ", + pedigree = + base.pedigree_schema + & { + contractile_verb = "bust", + semantics = "error handling + failure recovery", + security = { + leash = 'Kennel, + trust_level = "controlled failure injection; scoped to system-under-test", + allow_network = false, + allow_filesystem_write = true, # drills may write transient state (tmp dirs, test DBs) + allow_subprocess = true, + injection_scope = "system-under-test-only", + }, + metadata = { + name = "bust-runner", + version = "1.0.0", + description = "Exercises declared failure modes and verifies recovery paths. Hard-gates on any failure mode without working recovery.", + paired_xfile = "Bustfile.a2ml", + author = "Jonathan D.A. Jewell ", + }, }, - }, schema = { failure_modes | Array { - id | String, - description | String, - class | [| 'network, 'disk_full, 'oom, 'timeout, 'partial_write, 'panic, 'crash, 'rollback, 'concurrency |], - # TODO: migrate to base.probe_schema (structured probe) when CLI supports it - injection_probe | String, # command that deterministically causes this failure - # TODO: migrate to base.probe_schema (structured probe) when CLI supports it - recovery_probe | String, # command that verifies recovery (exit 0 = recovered) - expected_recovery_time_seconds | Number | default = 30, - # status_core values: 'declared, 'verified, 'failing; bust adds 'drilled - status | [| 'declared, 'drilled, 'verified, 'failing |] | default = 'declared, - notes | String | optional, - }, + id | String, + description | String, + class | [| 'network, 'disk_full, 'oom, 'timeout, 'partial_write, 'panic, 'crash, 'rollback, 'concurrency |], + # TODO: migrate to base.probe_schema (structured probe) when CLI supports it + injection_probe | String, # command that deterministically causes this failure + # TODO: migrate to base.probe_schema (structured probe) when CLI supports it + recovery_probe | String, # command that verifies recovery (exit 0 = recovered) + expected_recovery_time_seconds | Number | default = 30, + # status_core values: 'declared, 'verified, 'failing; bust adds 'drilled + status | [| 'declared, 'drilled, 'verified, 'failing |] | default = 'declared, + notes | String | optional, + }, }, # Runner behaviour — inherits from base.run_defaults. # bust adds record_recovery_times for performance tier feeding. - run = base.run_defaults & { - on_any_fail = "exit-nonzero", # missing or broken recovery blocks merge - report_format = "a2ml", - emit_summary = true, - record_recovery_times = true, # feeds the performance tier - }, + run = + base.run_defaults + & { + on_any_fail = "exit-nonzero", # missing or broken recovery blocks merge + report_format = "a2ml", + emit_summary = true, + record_recovery_times = true, # feeds the performance tier + }, } diff --git a/.machine_readable/contractiles/dust/dust.ncl b/.machine_readable/contractiles/dust/dust.ncl index 36aa89b80..8da0361af 100644 --- a/.machine_readable/contractiles/dust/dust.ncl +++ b/.machine_readable/contractiles/dust/dust.ncl @@ -14,53 +14,56 @@ # # 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 = "exnovation / removal", - security = { - leash = 'Kennel, - trust_level = "proposes deletion; --apply required to execute", - allow_network = false, - allow_filesystem_write = true, # --apply mode writes (deletes) - allow_subprocess = true, - destructive_mode_requires_flag = "--apply", - }, - metadata = { - name = "dust-runner", - version = "1.0.0", - description = "Identifies and optionally removes exnovation targets listed in Dustfile.a2ml. Destructive mode gated behind --apply.", - paired_xfile = "Dustfile.a2ml", - author = "Jonathan D.A. Jewell ", + pedigree = + base.pedigree_schema + & { + contractile_verb = "dust", + semantics = "exnovation / removal", + security = { + leash = 'Kennel, + trust_level = "proposes deletion; --apply required to execute", + allow_network = false, + allow_filesystem_write = true, # --apply mode writes (deletes) + allow_subprocess = true, + destructive_mode_requires_flag = "--apply", + }, + metadata = { + name = "dust-runner", + version = "1.0.0", + description = "Identifies and optionally removes exnovation targets listed in Dustfile.a2ml. Destructive mode gated behind --apply.", + paired_xfile = "Dustfile.a2ml", + author = "Jonathan D.A. Jewell ", + }, }, - }, schema = { removal_candidates | Array { - id | String, - description | String, - target | String, # file / path / symbol / dep name - reason | String, # why it's a removal candidate - # TODO: migrate to base.probe_schema (structured probe) when CLI supports it - probe | String | optional, # command that confirms it's still removable - # 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) - notes | String | optional, - }, + id | String, + description | String, + target | String, # file / path / symbol / dep name + reason | String, # why it's a removal candidate + # TODO: migrate to base.probe_schema (structured probe) when CLI supports it + probe | String | optional, # command that confirms it's still removable + # 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) + 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 - }, + 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.ncl b/.machine_readable/contractiles/intend/intend.ncl index 091b7f654..175b2b277 100644 --- a/.machine_readable/contractiles/intend/intend.ncl +++ b/.machine_readable/contractiles/intend/intend.ncl @@ -20,49 +20,50 @@ # # 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 committed next-actions and lists horizon aspirations. Non-gating. Absorbed `lust` semantics 2026-04-18.", - paired_xfile = "Intentfile.a2ml", - author = "Jonathan D.A. Jewell ", + 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 committed next-actions and lists horizon aspirations. 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, - }, + 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, - } + 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, }, @@ -70,12 +71,14 @@ let base = import "../_base.ncl" in # 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) - }, + 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.ncl b/.machine_readable/contractiles/must/must.ncl index 47509d3ff..98142267d 100644 --- a/.machine_readable/contractiles/must/must.ncl +++ b/.machine_readable/contractiles/must/must.ncl @@ -14,51 +14,54 @@ # # 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 invariant in the adjacent Mustfile.a2ml as a hard gate.", - paired_xfile = "Mustfile.a2ml", - author = "Jonathan D.A. Jewell ", + 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 invariant in the adjacent Mustfile.a2ml as a hard gate.", + 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 - status | [| 'declared, 'verified, 'failing |] | default = 'declared, - severity | [| 'critical, 'high, 'medium |] | default = 'critical, - notes | String | optional, - fix | String | optional, - }, + 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 + 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 - report_format = "a2ml", # emit a2ml report, not json - emit_summary = true, - }, + run = + base.run_defaults + & { + on_any_fail = "exit-nonzero", # hard gate + report_format = "a2ml", # emit a2ml report, not json + emit_summary = true, + }, } diff --git a/.machine_readable/contractiles/trust/trust.ncl b/.machine_readable/contractiles/trust/trust.ncl index 21b335cce..2b836242e 100644 --- a/.machine_readable/contractiles/trust/trust.ncl +++ b/.machine_readable/contractiles/trust/trust.ncl @@ -14,75 +14,81 @@ # # 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 = 'Kennel, - trust_level = "verification + authorised-probe", - allow_network = false, # verifications are offline by default - allow_filesystem_write = false, # trust writes NOTHING - allow_subprocess = true, - authorised_probes_only = true, # probe section must explicitly list allowed targets - }, - metadata = { - name = "trust-runner", - version = "1.0.0", - description = "Security + provenance verifications plus authorised safe-hacking probes. All probes are scoped to the repo under test; never hits external systems.", - paired_xfile = "Trustfile.a2ml", - author = "Jonathan D.A. Jewell ", + pedigree = + base.pedigree_schema + & { + contractile_verb = "trust", + semantics = "security + provenance + safe-hacking", + security = { + leash = 'Kennel, + trust_level = "verification + authorised-probe", + allow_network = false, # verifications are offline by default + allow_filesystem_write = false, # trust writes NOTHING + allow_subprocess = true, + authorised_probes_only = true, # probe section must explicitly list allowed targets + }, + metadata = { + name = "trust-runner", + version = "1.0.0", + description = "Security + provenance verifications plus authorised safe-hacking probes. All probes are scoped to the repo under test; never hits external systems.", + paired_xfile = "Trustfile.a2ml", + author = "Jonathan D.A. Jewell ", + }, }, - }, schema = { verifications | Array { - id | String, - 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 (from base.severity_core) - severity | [| 'critical, 'high, 'medium, 'low |] | default = 'high, - notes | String | optional, - }, + id | String, + 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 (from base.severity_core) + severity | [| 'critical, 'high, 'medium, 'low |] | default = 'high, + notes | String | optional, + }, # Safe-hacking + testing section (added 2026-04-17 per user direction). # Each probe here is an ACTIVELY EXECUTED test — fuzz runs, chaos probes, # auth-bypass attempts, injection tests. 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 |] - | default = [], - probes - | Array { - id | String, - class | [| 'fuzz, 'property_test, 'chaos, 'auth_bypass, 'injection, 'timing |], - 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 = [] }, + scope | String, # e.g. "this-repo-only" / "localhost" + allowed_probe_classes + | Array [| 'fuzz, 'property_test, 'chaos, 'auth_bypass, 'injection, 'timing |] + | default + = [], + probes + | Array { + id | String, + class | [| 'fuzz, 'property_test, 'chaos, 'auth_bypass, 'injection, 'timing |], + 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 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, - }, + 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, + }, } diff --git a/1-formats/a2ml/agentic/ncl/lib/os_detect.ncl b/1-formats/a2ml/agentic/ncl/lib/os_detect.ncl index e3ba89f55..0b3bd15d6 100644 --- a/1-formats/a2ml/agentic/ncl/lib/os_detect.ncl +++ b/1-formats/a2ml/agentic/ncl/lib/os_detect.ncl @@ -1,26 +1,30 @@ # ncl/lib/os_detect.ncl # The "Omniscience" Module for the Rhodium Standard - { # Core detection logic via environment and shell introspection env = { - os = %{ "os" } | default = "linux", - arch = %{ "arch" } | default = "x86_64", - is_immutable = %{ "rhodium_immutable" } | default = "false", + os | String | default = "linux", + arch | String | default = "x86_64", + is_immutable | String | default = "false", }, # Logic to determine the target type - target_type = - if env.os == "minix" then 'Edge_ASIC - else if env.is_immutable == "true" then 'Kinoite_Layered - else if env.os == "darwin" then 'Apple_Darwin - else 'Standard_PC, + target_type = + if env.os == "minix" then + 'Edge_ASIC + else if env.is_immutable == "true" then + 'Kinoite_Layered + else if env.os == "darwin" then + 'Apple_Darwin + else + 'Standard_PC, # Permutation rules for the Mustfile - deployment_priority = match { - 'Edge_ASIC => "static_bin", - 'Kinoite_Layered => "podman_ostree", - 'Standard_PC => "nala_native", - _ => "container_first", - } target_type, + deployment_priority = + match { + 'Edge_ASIC => "static_bin", + 'Kinoite_Layered => "podman_ostree", + 'Standard_PC => "nala_native", + _ => "container_first", + } target_type, } diff --git a/1-formats/a2ml/agentic/ncl/lib/schema.ncl b/1-formats/a2ml/agentic/ncl/lib/schema.ncl index 50b01ab90..506e23954 100644 --- a/1-formats/a2ml/agentic/ncl/lib/schema.ncl +++ b/1-formats/a2ml/agentic/ncl/lib/schema.ncl @@ -10,10 +10,11 @@ Deployment = { priority_route | [| 'podman, 'nala, 'ostree, 'native |] | default = 'podman, targets | Array String, - cloud_mounts | Array { - name | String, - path | String, - protocol | [| 'rclone, 'fuse, 'nfs |], - }, + cloud_mounts + | Array { + name | String, + path | String, + protocol | [| 'rclone, 'fuse, 'nfs |], + }, }, } diff --git a/1-formats/a2ml/neurosym/ncl/lib/os_detect.ncl b/1-formats/a2ml/neurosym/ncl/lib/os_detect.ncl index e3ba89f55..0b3bd15d6 100644 --- a/1-formats/a2ml/neurosym/ncl/lib/os_detect.ncl +++ b/1-formats/a2ml/neurosym/ncl/lib/os_detect.ncl @@ -1,26 +1,30 @@ # ncl/lib/os_detect.ncl # The "Omniscience" Module for the Rhodium Standard - { # Core detection logic via environment and shell introspection env = { - os = %{ "os" } | default = "linux", - arch = %{ "arch" } | default = "x86_64", - is_immutable = %{ "rhodium_immutable" } | default = "false", + os | String | default = "linux", + arch | String | default = "x86_64", + is_immutable | String | default = "false", }, # Logic to determine the target type - target_type = - if env.os == "minix" then 'Edge_ASIC - else if env.is_immutable == "true" then 'Kinoite_Layered - else if env.os == "darwin" then 'Apple_Darwin - else 'Standard_PC, + target_type = + if env.os == "minix" then + 'Edge_ASIC + else if env.is_immutable == "true" then + 'Kinoite_Layered + else if env.os == "darwin" then + 'Apple_Darwin + else + 'Standard_PC, # Permutation rules for the Mustfile - deployment_priority = match { - 'Edge_ASIC => "static_bin", - 'Kinoite_Layered => "podman_ostree", - 'Standard_PC => "nala_native", - _ => "container_first", - } target_type, + deployment_priority = + match { + 'Edge_ASIC => "static_bin", + 'Kinoite_Layered => "podman_ostree", + 'Standard_PC => "nala_native", + _ => "container_first", + } target_type, } diff --git a/1-formats/a2ml/neurosym/ncl/lib/schema.ncl b/1-formats/a2ml/neurosym/ncl/lib/schema.ncl index 50b01ab90..506e23954 100644 --- a/1-formats/a2ml/neurosym/ncl/lib/schema.ncl +++ b/1-formats/a2ml/neurosym/ncl/lib/schema.ncl @@ -10,10 +10,11 @@ Deployment = { priority_route | [| 'podman, 'nala, 'ostree, 'native |] | default = 'podman, targets | Array String, - cloud_mounts | Array { - name | String, - path | String, - protocol | [| 'rclone, 'fuse, 'nfs |], - }, + cloud_mounts + | Array { + name | String, + path | String, + protocol | [| 'rclone, 'fuse, 'nfs |], + }, }, } diff --git a/1-formats/k9/capabilities.ncl b/1-formats/k9/capabilities.ncl index 502b1bd2b..6aa9ba43c 100644 --- a/1-formats/k9/capabilities.ncl +++ b/1-formats/k9/capabilities.ncl @@ -7,7 +7,6 @@ # pedigree `policy.capabilities` grant. Capabilities are checked by the leash # (leash.ncl) before any Hunt-level action: an action that needs a capability # not present in the grant is refused, even at Hunt level. - { # ───────────────────────────────────────────────────────────── # The closed CORE capability set @@ -17,14 +16,14 @@ # removing a core capability is a BREAKING change to the K9 spec (MAJOR bump). # Each name is ".". core = [ - "fs.read", # read named filesystem paths - "fs.write", # write named filesystem paths - "net.fetch", # outbound network fetch to named hosts - "process.spawn", # spawn a child process - "container.run", # run a container image - "secret.read", # read a named secret - "deploy.apply", # apply a deployment - "rollback.apply", # apply a rollback + "fs.read", # read named filesystem paths + "fs.write", # write named filesystem paths + "net.fetch", # outbound network fetch to named hosts + "process.spawn", # spawn a child process + "container.run", # run a container image + "secret.read", # read a named secret + "deploy.apply", # apply a deployment + "rollback.apply", # apply a rollback ], # ───────────────────────────────────────────────────────────── @@ -51,8 +50,10 @@ # # A single capability name is valid iff it is a core name or a well-formed # extension name. - Capability = std.contract.from_predicate (fun name => - is_core name || is_extension name), + Capability = + std.contract.from_predicate (fun name => + is_core name || is_extension name + ), # A grant is an explicit allow-list of capability names. DEFAULT-DENY is # expressed by the empty grant: it permits nothing. diff --git a/1-formats/k9/leash.ncl b/1-formats/k9/leash.ncl index 9b5630797..f88855b8c 100644 --- a/1-formats/k9/leash.ncl +++ b/1-formats/k9/leash.ncl @@ -5,7 +5,6 @@ # 'Kennel - Pure data, no execution, safe anywhere # 'Yard - Nickel evaluation only, no I/O side effects # 'Hunt - Full triad execution, requires cryptographic handshake - let SecurityLevel = [| 'Kennel, 'Yard, 'Hunt |] in { @@ -111,14 +110,20 @@ let SecurityLevel = [| 'Kennel, 'Yard, 'Hunt |] in # Detect required security level from component content detect_level = fun component => - let has_recipes = std.record.has_field "recipes" component - && component.recipes != {} in - let has_network = std.record.has_field "security" component + let has_recipes = + std.record.has_field "recipes" component + && component.recipes != {} + in + let has_network = + std.record.has_field "security" component && std.record.has_field "allow_network" component.security - && component.security.allow_network == true in - let has_fs_write = std.record.has_field "security" component + && component.security.allow_network == true + in + let has_fs_write = + std.record.has_field "security" component && std.record.has_field "allow_filesystem_write" component.security - && component.security.allow_filesystem_write == true in + && component.security.allow_filesystem_write == true + in if has_network || has_fs_write || has_recipes then 'Hunt @@ -131,9 +136,12 @@ let SecurityLevel = [| 'Kennel, 'Yard, 'Hunt |] in check_level = fun component requested_level => let detected = detect_level component in let level_order = fun l => - if l == 'Kennel then 0 - else if l == 'Yard then 1 - else 2 + if l == 'Kennel then + 0 + else if l == 'Yard then + 1 + else + 2 in let detected_ord = level_order detected in let requested_ord = level_order requested_level in @@ -215,7 +223,8 @@ let SecurityLevel = [| 'Kennel, 'Yard, 'Hunt |] in { permitted = false, enforced_level = 'Yard, - reason = "Hunt requires all five preconditions; unmet: " + reason = + "Hunt requires all five preconditions; unmet: " ++ std.string.join ", " unmet, unmet_preconditions = unmet, }, diff --git a/1-formats/k9/pedigree.ncl b/1-formats/k9/pedigree.ncl index 779876608..720fba1ea 100644 --- a/1-formats/k9/pedigree.ncl +++ b/1-formats/k9/pedigree.ncl @@ -18,7 +18,6 @@ # * The contractiles family is must / trust / dust / intend # (a different estate concept; NOT part of K9). # "must" appears in both but means different things; never conflate them. - let SecurityLevel = [| 'Kennel, 'Yard, 'Hunt |] in let Architecture = [| 'Linux, 'Minix, 'MacOS, 'Android, 'PC, 'ASIC, 'Unknown |] in diff --git a/1-formats/k9/register.ncl b/1-formats/k9/register.ncl index 81b7ee78b..ddbeca919 100644 --- a/1-formats/k9/register.ncl +++ b/1-formats/k9/register.ncl @@ -3,7 +3,6 @@ # # Generates OS-specific MIME type registration based on target environment. # Used by: just register - let OS = [| 'Linux, 'Minix, 'MacOS |] in { @@ -70,7 +69,7 @@ let OS = [| 'Linux, 'Minix, 'MacOS |] in }, Minix = { dest = "/etc/mime.types.d/k9.xml", - update_cmd = "true", # Minix uses static mapping + update_cmd = "true", # Minix uses static mapping user_dest = "~/.mime/k9.xml", user_update = "true", }, diff --git a/rhodium-standard-repositories/satellites/palimpsest-license/config.ncl b/rhodium-standard-repositories/satellites/palimpsest-license/config.ncl index d8ef86c05..ca2fcea83 100644 --- a/rhodium-standard-repositories/satellites/palimpsest-license/config.ncl +++ b/rhodium-standard-repositories/satellites/palimpsest-license/config.ncl @@ -11,7 +11,6 @@ # ============================================================================= # Type Definitions # ============================================================================= - let ConsentStatus = std.enum.TagOrString in let TraumaCategory = std.enum.TagOrString in @@ -20,63 +19,71 @@ let ConsentType = { ni_systems : [| 'permitted, 'prohibited, 'explicit_only, 'unknown |], commercial : [| 'permitted, 'prohibited, 'conditional |], research : [| 'permitted, 'prohibited, 'conditional |], -} in +} +in let EmotionalLineage = { - present : Bool, - context | optional : String, - trauma_markers | optional : Array String, - cultural_origin | optional : String, - ancestors | optional : Array String, - descendants | optional : Array String, -} in + present | Bool, + context | optional | String, + trauma_markers | optional | Array String, + cultural_origin | optional | String, + ancestors | optional | Array String, + descendants | optional | Array String, +} +in let WorkMetadata = { - id : String, - title : String, - creator : String, - version : String, - created : String, - license_version : String, - consent : ConsentType, - emotional_lineage | optional : EmotionalLineage, - consent_registry_url | optional : String, -} in + id | String, + title | String, + creator | String, + version | String, + created | String, + license_version | String, + consent | ConsentType, + emotional_lineage | optional | EmotionalLineage, + consent_registry_url | optional | String, +} +in let ProjectConfig = { - name : String, - version : String, - default_consent : ConsentType, - metadata_format : [| 'json_ld, 'xml, 'yaml |], - citation_formats : Array String, - consent_registry : { - enabled : Bool, - url | optional : String, - api_key | optional : String, - }, - build : { - ocaml : { - enabled : Bool, - melange : Bool, - ocanren : Bool, + name | String, + version | String, + default_consent | ConsentType, + metadata_format | [| 'json_ld, 'xml, 'yaml |], + citation_formats | Array String, + consent_registry + | { + enabled | Bool, + url | optional | String, + api_key | optional | String, }, - container : { - enabled : Bool, - base_image : String, + build + | { + ocaml + | { + enabled | Bool, + melange | Bool, + ocanren | Bool, + }, + container + | { + enabled | Bool, + base_image | String, + }, + packages + | { + guix | Bool, + nix | Bool, + void | Bool, + }, }, - packages : { - guix : Bool, - nix : Bool, - void : Bool, - }, - }, -} in +} +in # ============================================================================= # Default Configuration # ============================================================================= - -let default_config : ProjectConfig = { +let default_config | ProjectConfig = { name = "palimpsest", version = "0.4.0", @@ -112,18 +119,19 @@ let default_config : ProjectConfig = { void = false, }, }, -} in +} +in # ============================================================================= # Environment-Specific Overrides # ============================================================================= - let dev_overrides = { consent_registry = { enabled = true, url = "http://localhost:8080", }, -} in +} +in let prod_overrides = { consent_registry = { @@ -131,7 +139,8 @@ let prod_overrides = { url = "https://consent.palimpsestlicense.org", # api_key loaded from environment }, -} in +} +in let ci_overrides = { build = { @@ -140,12 +149,12 @@ let ci_overrides = { base_image = "cgr.dev/chainguard/wolfi-base:latest", }, }, -} in +} +in # ============================================================================= # Validation Functions # ============================================================================= - let validate_consent | ConsentType -> Bool = fun consent => consent.ai_training != 'unknown && consent.ni_systems != 'unknown @@ -163,18 +172,20 @@ in # ============================================================================= # Generate JSON-LD metadata block -let to_json_ld | WorkMetadata -> _ = fun work => { - "@context" = "https://palimpsestlicense.org/context", - "@type" = "CreativeWork", - "@id" = work.id, - "name" = work.title, - "creator" = work.creator, - "license" = "https://palimpsestlicense.org/v%{work.license_version}", - "palimpsest:consent" = { - "palimpsest:aiTraining" = std.enum.to_tag work.consent.ai_training, - "palimpsest:niSystems" = std.enum.to_tag work.consent.ni_systems, - }, -} in +let to_json_ld | WorkMetadata -> _ = fun work => + { + "@context" = "https://palimpsestlicense.org/context", + "@type" = "CreativeWork", + "@id" = work.id, + "name" = work.title, + "creator" = work.creator, + "license" = "https://palimpsestlicense.org/v%{work.license_version}", + "palimpsest:consent" = { + "palimpsest:aiTraining" = std.string.from_enum work.consent.ai_training, + "palimpsest:niSystems" = std.string.from_enum work.consent.ni_systems, + }, + } +in # Generate OSCOLA citation let to_oscola | WorkMetadata -> String = fun work => @@ -184,8 +195,7 @@ in # ============================================================================= # Example Work Metadata # ============================================================================= - -let example_work : WorkMetadata = { +let example_work | WorkMetadata = { id = "urn:palimpsest:work:example-001", title = "Example Protected Work", creator = "Jane Doe", @@ -205,12 +215,27 @@ let example_work : WorkMetadata = { cultural_origin = "Social justice movement", }, consent_registry_url = "https://consent.palimpsestlicense.org/verify/example-001", -} in +} +in # ============================================================================= # Exports # ============================================================================= +# A Nickel record is RECURSIVE: inside a record literal a field name shadows any +# outer binding of the same name, so `validate_work = validate_work` below would +# be a self-reference, not a reference to the `let` above it -- `nickel export` +# died with "infinite recursion" on exactly that. Bind distinct aliases here so +# the exported field names can stay as they are (they are the public API). +let schema_ConsentType = ConsentType in +let schema_EmotionalLineage = EmotionalLineage in +let schema_WorkMetadata = WorkMetadata in +let schema_ProjectConfig = ProjectConfig in +let helper_to_json_ld = to_json_ld in +let helper_to_oscola = to_oscola in +let helper_validate_consent = validate_consent in +let helper_validate_work = validate_work in + { # Main configuration config = default_config, @@ -224,18 +249,18 @@ let example_work : WorkMetadata = { # Type schemas (for documentation/validation) schemas = { - ConsentType = ConsentType, - EmotionalLineage = EmotionalLineage, - WorkMetadata = WorkMetadata, - ProjectConfig = ProjectConfig, + ConsentType = schema_ConsentType, + EmotionalLineage = schema_EmotionalLineage, + WorkMetadata = schema_WorkMetadata, + ProjectConfig = schema_ProjectConfig, }, # Helper functions helpers = { - to_json_ld = to_json_ld, - to_oscola = to_oscola, - validate_consent = validate_consent, - validate_work = validate_work, + to_json_ld = helper_to_json_ld, + to_oscola = helper_to_oscola, + validate_consent = helper_validate_consent, + validate_work = helper_validate_work, }, # Example data diff --git a/rhodium-standard-repositories/satellites/palimpsest-license/config/config.ncl b/rhodium-standard-repositories/satellites/palimpsest-license/config/config.ncl index d8ef86c05..ca2fcea83 100644 --- a/rhodium-standard-repositories/satellites/palimpsest-license/config/config.ncl +++ b/rhodium-standard-repositories/satellites/palimpsest-license/config/config.ncl @@ -11,7 +11,6 @@ # ============================================================================= # Type Definitions # ============================================================================= - let ConsentStatus = std.enum.TagOrString in let TraumaCategory = std.enum.TagOrString in @@ -20,63 +19,71 @@ let ConsentType = { ni_systems : [| 'permitted, 'prohibited, 'explicit_only, 'unknown |], commercial : [| 'permitted, 'prohibited, 'conditional |], research : [| 'permitted, 'prohibited, 'conditional |], -} in +} +in let EmotionalLineage = { - present : Bool, - context | optional : String, - trauma_markers | optional : Array String, - cultural_origin | optional : String, - ancestors | optional : Array String, - descendants | optional : Array String, -} in + present | Bool, + context | optional | String, + trauma_markers | optional | Array String, + cultural_origin | optional | String, + ancestors | optional | Array String, + descendants | optional | Array String, +} +in let WorkMetadata = { - id : String, - title : String, - creator : String, - version : String, - created : String, - license_version : String, - consent : ConsentType, - emotional_lineage | optional : EmotionalLineage, - consent_registry_url | optional : String, -} in + id | String, + title | String, + creator | String, + version | String, + created | String, + license_version | String, + consent | ConsentType, + emotional_lineage | optional | EmotionalLineage, + consent_registry_url | optional | String, +} +in let ProjectConfig = { - name : String, - version : String, - default_consent : ConsentType, - metadata_format : [| 'json_ld, 'xml, 'yaml |], - citation_formats : Array String, - consent_registry : { - enabled : Bool, - url | optional : String, - api_key | optional : String, - }, - build : { - ocaml : { - enabled : Bool, - melange : Bool, - ocanren : Bool, + name | String, + version | String, + default_consent | ConsentType, + metadata_format | [| 'json_ld, 'xml, 'yaml |], + citation_formats | Array String, + consent_registry + | { + enabled | Bool, + url | optional | String, + api_key | optional | String, }, - container : { - enabled : Bool, - base_image : String, + build + | { + ocaml + | { + enabled | Bool, + melange | Bool, + ocanren | Bool, + }, + container + | { + enabled | Bool, + base_image | String, + }, + packages + | { + guix | Bool, + nix | Bool, + void | Bool, + }, }, - packages : { - guix : Bool, - nix : Bool, - void : Bool, - }, - }, -} in +} +in # ============================================================================= # Default Configuration # ============================================================================= - -let default_config : ProjectConfig = { +let default_config | ProjectConfig = { name = "palimpsest", version = "0.4.0", @@ -112,18 +119,19 @@ let default_config : ProjectConfig = { void = false, }, }, -} in +} +in # ============================================================================= # Environment-Specific Overrides # ============================================================================= - let dev_overrides = { consent_registry = { enabled = true, url = "http://localhost:8080", }, -} in +} +in let prod_overrides = { consent_registry = { @@ -131,7 +139,8 @@ let prod_overrides = { url = "https://consent.palimpsestlicense.org", # api_key loaded from environment }, -} in +} +in let ci_overrides = { build = { @@ -140,12 +149,12 @@ let ci_overrides = { base_image = "cgr.dev/chainguard/wolfi-base:latest", }, }, -} in +} +in # ============================================================================= # Validation Functions # ============================================================================= - let validate_consent | ConsentType -> Bool = fun consent => consent.ai_training != 'unknown && consent.ni_systems != 'unknown @@ -163,18 +172,20 @@ in # ============================================================================= # Generate JSON-LD metadata block -let to_json_ld | WorkMetadata -> _ = fun work => { - "@context" = "https://palimpsestlicense.org/context", - "@type" = "CreativeWork", - "@id" = work.id, - "name" = work.title, - "creator" = work.creator, - "license" = "https://palimpsestlicense.org/v%{work.license_version}", - "palimpsest:consent" = { - "palimpsest:aiTraining" = std.enum.to_tag work.consent.ai_training, - "palimpsest:niSystems" = std.enum.to_tag work.consent.ni_systems, - }, -} in +let to_json_ld | WorkMetadata -> _ = fun work => + { + "@context" = "https://palimpsestlicense.org/context", + "@type" = "CreativeWork", + "@id" = work.id, + "name" = work.title, + "creator" = work.creator, + "license" = "https://palimpsestlicense.org/v%{work.license_version}", + "palimpsest:consent" = { + "palimpsest:aiTraining" = std.string.from_enum work.consent.ai_training, + "palimpsest:niSystems" = std.string.from_enum work.consent.ni_systems, + }, + } +in # Generate OSCOLA citation let to_oscola | WorkMetadata -> String = fun work => @@ -184,8 +195,7 @@ in # ============================================================================= # Example Work Metadata # ============================================================================= - -let example_work : WorkMetadata = { +let example_work | WorkMetadata = { id = "urn:palimpsest:work:example-001", title = "Example Protected Work", creator = "Jane Doe", @@ -205,12 +215,27 @@ let example_work : WorkMetadata = { cultural_origin = "Social justice movement", }, consent_registry_url = "https://consent.palimpsestlicense.org/verify/example-001", -} in +} +in # ============================================================================= # Exports # ============================================================================= +# A Nickel record is RECURSIVE: inside a record literal a field name shadows any +# outer binding of the same name, so `validate_work = validate_work` below would +# be a self-reference, not a reference to the `let` above it -- `nickel export` +# died with "infinite recursion" on exactly that. Bind distinct aliases here so +# the exported field names can stay as they are (they are the public API). +let schema_ConsentType = ConsentType in +let schema_EmotionalLineage = EmotionalLineage in +let schema_WorkMetadata = WorkMetadata in +let schema_ProjectConfig = ProjectConfig in +let helper_to_json_ld = to_json_ld in +let helper_to_oscola = to_oscola in +let helper_validate_consent = validate_consent in +let helper_validate_work = validate_work in + { # Main configuration config = default_config, @@ -224,18 +249,18 @@ let example_work : WorkMetadata = { # Type schemas (for documentation/validation) schemas = { - ConsentType = ConsentType, - EmotionalLineage = EmotionalLineage, - WorkMetadata = WorkMetadata, - ProjectConfig = ProjectConfig, + ConsentType = schema_ConsentType, + EmotionalLineage = schema_EmotionalLineage, + WorkMetadata = schema_WorkMetadata, + ProjectConfig = schema_ProjectConfig, }, # Helper functions helpers = { - to_json_ld = to_json_ld, - to_oscola = to_oscola, - validate_consent = validate_consent, - validate_work = validate_work, + to_json_ld = helper_to_json_ld, + to_oscola = helper_to_oscola, + validate_consent = helper_validate_consent, + validate_work = helper_validate_work, }, # Example data diff --git a/rhodium-standard-repositories/satellites/palimpsest-license/config/infra.ncl b/rhodium-standard-repositories/satellites/palimpsest-license/config/infra.ncl index eba00ff81..fa07be08a 100644 --- a/rhodium-standard-repositories/satellites/palimpsest-license/config/infra.ncl +++ b/rhodium-standard-repositories/satellites/palimpsest-license/config/infra.ncl @@ -2,7 +2,6 @@ # Nickel configuration for reproducible infrastructure-as-code # Version: 0.4.0 # SPDX-License-Identifier: Palimpsest-0.4 OR MIT - { # ============================================================================ # PROJECT METADATA @@ -37,7 +36,7 @@ build = { # Build system selector - system = "just", # Primary: just, Secondary: nix, make + system = "just", # Primary: just, Secondary: nix, make # Build targets targets = { @@ -53,7 +52,7 @@ command = "just test", description = "Run comprehensive test suite", dependencies = ["test-haskell", "test-rescript", "test-integration"], - coverage_threshold = 80, # Target for Silver RSR + coverage_threshold = 80, # Target for Silver RSR }, build = { @@ -61,7 +60,7 @@ description = "Build all components", dependencies = ["build-styles", "build-haskell", "build-rescript"], reproducible = true, - hermetic = false, # TODO: Enable for Silver RSR + hermetic = false, # TODO: Enable for Silver RSR }, deploy = { @@ -81,8 +80,8 @@ # Environment variables env_vars = { NODE_ENV = "production", - SOURCE_DATE_EPOCH = "1609459200", # 2021-01-01 for reproducibility - LANG = "en_GB.UTF-8", # British English + SOURCE_DATE_EPOCH = "1609459200", # 2021-01-01 for reproducibility + LANG = "en_GB.UTF-8", # British English }, }, @@ -93,7 +92,7 @@ source = "styles/scss/", output = "styles/css/", format = "compressed", - source_maps = false, # Disable for reproducibility + source_maps = false, # Disable for reproducibility }, # Haskell binaries @@ -129,7 +128,7 @@ schedule = "weekly", tools = ["npm audit", "cabal outdated"], fail_on = ["critical", "high"], - auto_fix = false, # Manual review required + auto_fix = false, # Manual review required }, # SPDX license validation @@ -169,7 +168,7 @@ # Code signing signing = { - enabled = false, # TODO: Enable for production + enabled = false, # TODO: Enable for production gpg_key_id = "", sign_commits = false, sign_tags = true, @@ -184,7 +183,7 @@ # Offline-first compliance offline_first = { enabled = true, - allow_network = false, # Build must succeed without network + allow_network = false, # Build must succeed without network cache_dependencies = true, }, @@ -192,7 +191,7 @@ cdn = { provider = "cloudflare", domains = ["cdn.palimpsest.license"], - cache_ttl = 86400, # 24 hours + cache_ttl = 86400, # 24 hours compress = true, }, @@ -339,7 +338,7 @@ dependencies = { # Node.js dependencies npm = { - production = [], # Documentation project, no production deps + production = [], # Documentation project, no production deps development = [ { name = "eslint", version = "^8.0.0" }, { name = "prettier", version = "^3.0.0" }, @@ -381,9 +380,9 @@ # SBOM generation sbom = { enabled = true, - format = "cyclonedx", # or "spdx" + format = "cyclonedx", # or "spdx" output = "sbom.json", - include = ["npm", "cabal", "nix"], + "include" = ["npm", "cabal", "nix"], }, }, @@ -420,7 +419,7 @@ # End-to-end tests e2e = { - enabled = false, # Not applicable for documentation project + enabled = false, # Not applicable for documentation project }, # Performance tests @@ -461,7 +460,7 @@ # Container registry container_registry = { - provider = "ghcr.io", # GitHub Container Registry + provider = "ghcr.io", # GitHub Container Registry namespace = "palimpsest-license", repository = "palimpsest-license", tags = ["latest", "v0.4.0"], @@ -486,7 +485,7 @@ ], checksum_algorithm = "sha256", signing = { - enabled = false, # TODO: Enable for production + enabled = false, # TODO: Enable for production method = "gpg", }, }, @@ -499,7 +498,7 @@ compliance = { # RSR (Rhodium Standard Repository) compliance rsr = { - tier = "bronze", # Current: Bronze, Target: Silver + tier = "bronze", # Current: Bronze, Target: Silver categories = { documentation = { score = 10, max = 10 }, well_known = { score = 10, max = 10 }, @@ -533,9 +532,9 @@ term_length = "2 years", staggered_terms = true, voting_threshold = { - minor_changes = 4, # 4/7 votes - major_changes = 5, # 5/7 votes - constitutional = 6, # 6/7 votes + minor_changes = 4, # 4/7 votes + major_changes = 5, # 5/7 votes + constitutional = 6, # 6/7 votes }, }, }, @@ -554,12 +553,12 @@ # Metrics metrics = { - enabled = false, # Not applicable for static site + enabled = false, # Not applicable for static site }, # Error tracking error_tracking = { - enabled = false, # Not applicable for documentation + enabled = false, # Not applicable for documentation }, # Uptime monitoring diff --git a/rhodium-standard-repositories/satellites/rsr-deployer/ncl/lib/os_detect.ncl b/rhodium-standard-repositories/satellites/rsr-deployer/ncl/lib/os_detect.ncl index e3ba89f55..0b3bd15d6 100644 --- a/rhodium-standard-repositories/satellites/rsr-deployer/ncl/lib/os_detect.ncl +++ b/rhodium-standard-repositories/satellites/rsr-deployer/ncl/lib/os_detect.ncl @@ -1,26 +1,30 @@ # ncl/lib/os_detect.ncl # The "Omniscience" Module for the Rhodium Standard - { # Core detection logic via environment and shell introspection env = { - os = %{ "os" } | default = "linux", - arch = %{ "arch" } | default = "x86_64", - is_immutable = %{ "rhodium_immutable" } | default = "false", + os | String | default = "linux", + arch | String | default = "x86_64", + is_immutable | String | default = "false", }, # Logic to determine the target type - target_type = - if env.os == "minix" then 'Edge_ASIC - else if env.is_immutable == "true" then 'Kinoite_Layered - else if env.os == "darwin" then 'Apple_Darwin - else 'Standard_PC, + target_type = + if env.os == "minix" then + 'Edge_ASIC + else if env.is_immutable == "true" then + 'Kinoite_Layered + else if env.os == "darwin" then + 'Apple_Darwin + else + 'Standard_PC, # Permutation rules for the Mustfile - deployment_priority = match { - 'Edge_ASIC => "static_bin", - 'Kinoite_Layered => "podman_ostree", - 'Standard_PC => "nala_native", - _ => "container_first", - } target_type, + deployment_priority = + match { + 'Edge_ASIC => "static_bin", + 'Kinoite_Layered => "podman_ostree", + 'Standard_PC => "nala_native", + _ => "container_first", + } target_type, } diff --git a/rhodium-standard-repositories/satellites/rsr-deployer/ncl/lib/schema.ncl b/rhodium-standard-repositories/satellites/rsr-deployer/ncl/lib/schema.ncl index 50b01ab90..506e23954 100644 --- a/rhodium-standard-repositories/satellites/rsr-deployer/ncl/lib/schema.ncl +++ b/rhodium-standard-repositories/satellites/rsr-deployer/ncl/lib/schema.ncl @@ -10,10 +10,11 @@ Deployment = { priority_route | [| 'podman, 'nala, 'ostree, 'native |] | default = 'podman, targets | Array String, - cloud_mounts | Array { - name | String, - path | String, - protocol | [| 'rclone, 'fuse, 'nfs |], - }, + cloud_mounts + | Array { + name | String, + path | String, + protocol | [| 'rclone, 'fuse, 'nfs |], + }, }, } From 50563a423128f59c7c8f2c9bbc4929f81aa80042 Mon Sep 17 00:00:00 2001 From: "Jonathan D.A. Jewell" <6759885+hyperpolymath@users.noreply.github.com> Date: Tue, 22 Sep 2026 21:23:28 +0100 Subject: [PATCH 2/2] chore(registry): regenerate REGISTRY.a2ml for the Nickel reformat `.machine_readable/REGISTRY.a2ml` is a DERIVED artefact: `build-registry.sh` regenerates it from every tracked file under a spec home, and a gate fails the build when the committed copy no longer matches the tree. The preceding commit reformatted 20 `.ncl` files, 7 of them under `.machine_readable/contractiles/` and 4 under `1-formats/k9/`, so the registry went stale by construction. Generated, not hand-edited: bash scripts/build-registry.sh # "Wrote ... (33 specs)." This is the whole of the cure for BOTH reds on this PR, which shared one root cause rather than being two faults: - `Registry + topology in sync` -- `build-registry.sh --check` -> rc=1 - `Repo self-tests` -- 2 of 53 test files, namely * `build-registry-test.sh` (2 of 9 controls; both clone the COMMITTED tree, so they stay red until this artefact is committed, not merely regenerated in the working tree) * `wave3-scorecards-test.sh` (3 "claimed PASS but check exited 1" entries, every one of whose `check:` line is literally `bash scripts/build-registry.sh --check`) Measured after regenerating: `--check` rc=0, `wave3-scorecards-test.sh` 9 passed / 0 failed. `build-registry-test.sh` needs this commit to exist before its clone can see the cure. The generator is proven deterministic by its own suite (two generations byte-identical, no embedded timestamp), so this artefact is reproducible by anyone running the command above. Co-Authored-By: Claude Opus 5 Claude-Session: https://claude.ai/code/session_01Ji1bq3TypfycfUPAR7hSxR --- .machine_readable/REGISTRY.a2ml | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) diff --git a/.machine_readable/REGISTRY.a2ml b/.machine_readable/REGISTRY.a2ml index 92b9171bd..8967b9b15 100644 --- a/.machine_readable/REGISTRY.a2ml +++ b/.machine_readable/REGISTRY.a2ml @@ -45,7 +45,7 @@ name = "K9 Self-Validating Components" stream = "foundation" home = "1-formats/k9/" canonical_doc = "1-formats/k9/README.adoc" -source_hash = "sha256:536014a24a3928eafa441726511566705a4605877bc15866b3a91fb80d727b0f" +source_hash = "sha256:19b4ac92f44aa5e5c133755e8e2e4c2d18383cb64cccd4f31d576ba14e020be5" route = "the K9 specification, security analysis and adoption guidance (implementations live in hyperpolymath/k9-ecosystem)" [[spec]] @@ -90,7 +90,7 @@ name = "AGENTIC.a2ml spec" stream = "foundation" home = "1-formats/a2ml/agentic/" canonical_doc = "1-formats/a2ml/agentic/README.adoc" -source_hash = "sha256:b86efb14f342b7d152e31f098be8929c0b9ca02efe61d901f749ec999409e299" +source_hash = "sha256:f118e927f79664c0c79e249e2538a52563f9d63b828eba65611c379d37253175" route = "AI-agent operational gating / entropy budgets" [[spec]] @@ -99,7 +99,7 @@ name = "NEUROSYM.a2ml spec" stream = "foundation" home = "1-formats/a2ml/neurosym/" canonical_doc = "1-formats/a2ml/neurosym/README.adoc" -source_hash = "sha256:11362cc1e57ad7fdd29e4f2a57316c7df7f4d51bff4963d607dd560810318e0b" +source_hash = "sha256:9bfe5e975ccb49ee2a78c8baf6c3bbe962756f2946f0795af2cab7b76a51c5a9" route = "symbolic semantics / proof obligations" [[spec]] @@ -207,7 +207,7 @@ name = "RSR — Rhodium Standard Repositories" stream = "governance" home = "rhodium-standard-repositories/" canonical_doc = "rhodium-standard-repositories/README.adoc" -source_hash = "sha256:7e07d81d2373e8ce2820f1a3db13550626582ca9992854327a8b3b39e0d366c3" +source_hash = "sha256:5e9282daf9273d89ddd58af8af87c2a1d73e95fde6a43409ebccca7b1d4878b9" route = "the repository-compliance standard every repo is graded against" [[spec]]