Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
379 changes: 44 additions & 335 deletions .github/hooks/validate-k9.sh

Large diffs are not rendered by default.

68 changes: 35 additions & 33 deletions .github/workflows/dogfood-gate.yml
Original file line number Diff line number Diff line change
Expand Up @@ -83,51 +83,53 @@ jobs:
k9-validate:
name: Validate K9 contracts
runs-on: ubuntu-latest
timeout-minutes: 10
timeout-minutes: 15
outputs:
validation: ${{ steps.validate.outcome }}
env:
NICKEL_VERSION: '1.18.0'
NICKEL_SHA256: '9cba4dd65ae9915ec61f73033aafcff307a377665a83fd8f530df086763318cb'

steps:
- name: Checkout repository
uses: actions/checkout@v7.0.1
with:
persist-credentials: false

- name: Check for K9 files
id: detect
- name: Checkout canonical K9 validator and contract
uses: actions/checkout@v7.0.1
with:
repository: metadatastician/standards
ref: e064539116c2dcfdf282a4749dbd58c69961c7e6
path: .standards-k9
sparse-checkout: 1-formats/k9
sparse-checkout-cone-mode: true
persist-credentials: false

- name: Install sha256-verified Nickel CLI
run: |
COUNT=$(find . \( -name '*.k9' -o -name '*.k9.ncl' \) -not -path './.git/*' | wc -l)
CONFIG_COUNT=$(find . \( -name '*.toml' -o -name '*.yaml' -o -name '*.yml' -o -name '*.json' \) \
-not -path './.git/*' -not -path './node_modules/*' -not -path './.deno/*' \
-not -name 'package-lock.json' -not -name 'Cargo.lock' -not -name 'deno.lock' | wc -l)
echo "k9_count=$COUNT" >> "$GITHUB_OUTPUT"
echo "config_count=$CONFIG_COUNT" >> "$GITHUB_OUTPUT"
if [ "$COUNT" -eq 0 ] && [ "$CONFIG_COUNT" -gt 0 ]; then
echo "::warning::Found $CONFIG_COUNT config files but no K9 contracts. Run k9iser to generate contracts."
fi
set -euo pipefail
URL="https://github.com/tweag/nickel/releases/download/${NICKEL_VERSION}/nickel-x86_64-linux"
curl -sSfL --proto '=https' --proto-redir '=https' --tlsv1.2 --retry 3 -o "$RUNNER_TEMP/nickel" "$URL"
echo "${NICKEL_SHA256} ${RUNNER_TEMP}/nickel" | sha256sum -c -
chmod +x "$RUNNER_TEMP/nickel"
echo "$RUNNER_TEMP" >> "$GITHUB_PATH"

- name: Validate K9 contracts
- name: Validate K9 contracts (L0–L2)
id: validate
if: steps.detect.outputs.k9_count > 0
env:
INPUT_STRICT: "true"
run: bash .github/hooks/validate-k9.sh
- name: Write summary
run: |
K9_COUNT="${{ steps.detect.outputs.k9_count }}"
CFG_COUNT="${{ steps.detect.outputs.config_count }}"
if [ "$K9_COUNT" -eq 0 ]; then
cat <<'EOF' >> "$GITHUB_STEP_SUMMARY"
## K9 Contract Validation

:warning: **No K9 contracts found.** Structural K9 validation was not run.

Generate contracts with: `k9iser generate .`
EOF
else
echo "## K9 Contract Validation" >> "$GITHUB_STEP_SUMMARY"
echo "" >> "$GITHUB_STEP_SUMMARY"
echo "Checked structural fields in **${K9_COUNT}** K9 contract(s). The repository contains **${CFG_COUNT}** configuration file(s); this hook does not compare their contents with the contracts." >> "$GITHUB_STEP_SUMMARY"
echo "This is not Nickel evaluation, cryptographic signature verification or Hunt deployment authorization." >> "$GITHUB_STEP_SUMMARY"
set -euo pipefail
rm -rf "$RUNNER_TEMP/standards-k9"
mv "$PWD/.standards-k9" "$RUNNER_TEMP/standards-k9"
if [ ! -f "$RUNNER_TEMP/standards-k9/1-formats/k9/tools/k9-validate.sh" ]; then
echo "::error::K9 validator is missing from the standards checkout"
exit 1
fi
if ! command -v nickel >/dev/null 2>&1; then
echo "::error::nickel is required for K9 layer L2 validation"
exit 1
fi
STANDARDS_DIR="$RUNNER_TEMP/standards-k9" bash .github/hooks/validate-k9.sh

# ---------------------------------------------------------------------------
# Job 3: Empty-linter — invisible character detection
Expand Down
23 changes: 12 additions & 11 deletions .machine_readable/contractiles/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -30,9 +30,10 @@ coherence manifest:
actual invariants / trust actions / tolerances / intents).
* `<verb>.ncl` — the paired Nickel *runner* (pedigree + schema + run
policy). Imports `../_base.ncl`.
* `<verb>.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`.
* `<verb>.k9.ncl` — the *K9 component* (K9 contract v1.0.0, D173): a
declaration evaluated at 'Yard. It records evidence sinks and
session-open/close negotiation; the paired runner is the execution
surface, so this record spawns nothing. Imports `../_base.ncl`.
* `<verb>.manifest.a2ml` — the *trident coherence manifest* (file roles,
cross-refs, signature, history).

Expand Down Expand Up @@ -125,14 +126,14 @@ 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 `<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.
*Validation.* Every `<verb>.k9.ncl` passes the canonical validator
(`metadatastician/standards` `1-formats/k9/tools/k9-validate.sh --strict`,
layers L0–L2), run by the `k9-validate` job in `dogfood-gate.yml` through
`.github/hooks/validate-k9.sh`. The former import of an estate-wide
`../k9/template-hunt.k9.ncl` base never resolved and has been removed, as
rsr-template-repo did. Echidna's templates for new components are
`../svc/k9/template-{kennel,yard,hunt}.k9.ncl.in`: they carry unfilled
placeholders, so they are not components and are not validated.
====

== Fill-in / maintenance rules
Expand Down
21 changes: 17 additions & 4 deletions .machine_readable/contractiles/adjust/adjust.k9.ncl
Original file line number Diff line number Diff line change
Expand Up @@ -22,12 +22,24 @@ K9!
# erosion causing gradual parameter drift). Where must flags "broken
# now", adjust flags "drifting toward broken".

let base_k9 = import "../k9/template-hunt.k9.ncl" in
#
# 2026-11-09 (K9 contract v1.0.0, D173, standards#1058): this record is a
# DECLARATION evaluated at 'Yard. The probes it lists are run by the paired
# runner (adjust.ncl and the scripts/run-*.sh runners), which is the execution
# surface; this file spawns nothing. A 'Hunt record needs signature_required,
# a signature block and a verified signature (K9-S008/S009/C001); no
# contractile carries one, so 'Hunt here was a claim the gate could never honour.
# Mirrors rsr-template-repo's contractiles (WO-9).

let base = import "../_base.ncl" in

{
pedigree = base_k9.pedigree_schema & {
pedigree = base.pedigree_schema & {
contractile_verb = "adjust",
schema_version = "1.0.0",
component_type = "contractile:adjust",
semantics = "drift tolerances + corrective actions",
side_effects = [],
paired_xfile = "../adjust/Adjustfile.a2ml",
paired_runner = "../adjust/adjust.ncl",

Expand All @@ -48,12 +60,13 @@ let base = import "../_base.ncl" in
trust_level = "tolerance measurement + declared auto-fix",
allow_network = false,
allow_filesystem_write_conditional = true, # auto_fix_when_available may edit
allow_subprocess = true,
allow_subprocess = false, # the paired runner spawns; this record does not
probe_scope = 'measurement_plus_declared_fix,
},
},

variance_schema = {
# A record SHAPE (contract-only fields, no values): kept out of `nickel export`.
variance_schema | not_exported = {
entry_id | String,
reason | String,
approved_by | String,
Expand Down
32 changes: 21 additions & 11 deletions .machine_readable/contractiles/bust/bust.k9.ncl
Original file line number Diff line number Diff line change
Expand Up @@ -23,16 +23,28 @@ K9!
# (invariant) and trust (cleanliness) as the third blocking verb in the
# (Hunt, blocking) triple.

let base_k9 = import "../k9/template-hunt.k9.ncl" in
#
# 2026-11-09 (K9 contract v1.0.0, D173, standards#1058): this record is a
# DECLARATION evaluated at 'Yard. The probes it lists are run by the paired
# runner (bust.ncl and the scripts/run-*.sh runners), which is the execution
# surface; this file spawns nothing. A 'Hunt record needs signature_required,
# a signature block and a verified signature (K9-S008/S009/C001); no
# contractile carries one, so 'Hunt here was a claim the gate could never honour.
# Mirrors rsr-template-repo's contractiles (WO-9).

let base = import "../_base.ncl" in

{
pedigree = base_k9.pedigree_schema & {
pedigree = base.pedigree_schema & {
contractile_verb = "bust",
schema_version = "1.0.0",
component_type = "contractile:bust",
semantics = "hard-stop / expiry / must-not-run declarations",
side_effects = [],
paired_xfile = "../bust/Bustfile.a2ml",
paired_runner = "../bust/bust.ncl",

tier = 'Hunt,
tier = 'Yard,
authority = 'blocking,

metadata = {
Expand All @@ -45,20 +57,18 @@ let base = import "../_base.ncl" in
},

security = {
leash = 'Hunt,
leash = 'Yard,
signature_required = false,
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,
allow_filesystem_write = false, # the runner's drills write transient state (tmp dirs, test DBs); this record does not
allow_subprocess = false, # the paired runner spawns; this record does not
injection_scope = 'system_under_test_only,
# Hunt-tier files must declare whether a cryptographic signature
# is required. Bust trident runs in CI on every PR; signatures
# are enforced at the trident-runner layer, not in-band.
signature_required = true,
},
},

variance_schema = {
# A record SHAPE (contract-only fields, no values): kept out of `nickel export`.
variance_schema | not_exported = {
entry_id | String,
reason | String,
approved_by | String,
Expand Down
21 changes: 17 additions & 4 deletions .machine_readable/contractiles/dust/dust.k9.ncl
Original file line number Diff line number Diff line change
Expand Up @@ -30,12 +30,24 @@ K9!
# 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
#
# 2026-11-09 (K9 contract v1.0.0, D173, standards#1058): this record is a
# DECLARATION evaluated at 'Yard. The probes it lists are run by the paired
# runner (dust.ncl and the scripts/run-*.sh runners), which is the execution
# surface; this file spawns nothing. A 'Hunt record needs signature_required,
# a signature block and a verified signature (K9-S008/S009/C001); no
# contractile carries one, so 'Hunt here was a claim the gate could never honour.
# Mirrors rsr-template-repo's contractiles (WO-9).

let base = import "../_base.ncl" in

{
pedigree = base_k9.pedigree_schema & {
pedigree = base.pedigree_schema & {
contractile_verb = "dust",
schema_version = "1.0.0",
component_type = "contractile:dust",
semantics = "rollback / recovery / deprecation / audit-trail preservation",
side_effects = [],
paired_xfile = "../dust/Dustfile.a2ml",
paired_runner = "../dust/dust.ncl",

Expand All @@ -60,7 +72,7 @@ let base = import "../_base.ncl" in
# 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,
allow_subprocess = false, # the paired runner spawns; this record does not
destructive_action_gating = {
default_mode = 'dry_run,
requires_flag = "--apply",
Expand All @@ -80,7 +92,8 @@ let base = import "../_base.ncl" in
},
},

variance_schema = {
# A record SHAPE (contract-only fields, no values): kept out of `nickel export`.
variance_schema | not_exported = {
entry_id | String,
reason | String,
approved_by | String,
Expand Down
27 changes: 20 additions & 7 deletions .machine_readable/contractiles/intend/intend.k9.ncl
Original file line number Diff line number Diff line change
Expand Up @@ -33,12 +33,24 @@ K9!
# * Evidence sinks: VeriSimDB (queryable) + descriptiles/DRIFT.a2ml (repo-local).
# * Failure-mode defenses cross-referenced to the AI failure catalog.

let base_k9 = import "../k9/template-hunt.k9.ncl" in
#
# 2026-11-09 (K9 contract v1.0.0, D173, standards#1058): this record is a
# DECLARATION evaluated at 'Yard. The probes it lists are run by the paired
# runner (intend.ncl and the scripts/run-*.sh runners), which is the execution
# surface; this file spawns nothing. A 'Hunt record needs signature_required,
# a signature block and a verified signature (K9-S008/S009/C001); no
# contractile carries one, so 'Hunt here was a claim the gate could never honour.
# Mirrors rsr-template-repo's contractiles (WO-9).

let base = import "../_base.ncl" in

{
pedigree = base_k9.pedigree_schema & {
pedigree = base.pedigree_schema & {
contractile_verb = "intend",
schema_version = "1.0.0",
component_type = "contractile:intend",
semantics = "north-star (commitments + aspirations)",
side_effects = [],
paired_xfile = "../intend/Intentfile.a2ml",
paired_runner = "../intend/intend.ncl",

Expand All @@ -48,7 +60,7 @@ let base = import "../_base.ncl" in
# 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,
tier = 'Yard,
authority = 'reporting,

metadata = {
Expand All @@ -65,12 +77,12 @@ let base = import "../_base.ncl" in
},

security = {
leash = 'Hunt,
signature_required = true,
leash = 'Yard,
signature_required = false,
trust_level = "subprocess + filesystem-read",
allow_network = false,
allow_filesystem_write = false, # evidence sinks are indirected
allow_subprocess = true,
allow_subprocess = false, # the paired runner spawns; this record does not
},
},

Expand All @@ -83,7 +95,8 @@ let base = import "../_base.ncl" in
# Per user 2026-04-18: variances are structural, not magic-comment
# markers — markers are gameable.
# -------------------------------------------------------------------
variance_schema = {
# A record SHAPE (contract-only fields, no values): kept out of `nickel export`.
variance_schema | not_exported = {
entry_id | String, # which intent/wish id the variance applies to
reason | String,
approved_by | String,
Expand Down
Loading
Loading