diff --git a/.github/workflows/k9-contractile.yml b/.github/workflows/k9-contractile.yml index db20bb2c8..7abffb152 100644 --- a/.github/workflows/k9-contractile.yml +++ b/.github/workflows/k9-contractile.yml @@ -200,19 +200,31 @@ jobs: fi exit $rc - - name: K9 corpus conformance (ratcheted) + - name: K9 corpus conformance (ratcheted, L2) + env: + # MIGRATION-1058 M7's first step. The hook's own default stays L1: a + # pre-commit hook that fails because a laptop lacks a toolchain + # teaches people to pass --no-verify, which is how a gate gets + # deleted. CI has the pinned binary, so here the corpus is checked at + # L2 as well. The six contractiles and the two axel config files that + # M1/M4 fixed had never been evaluated against K9.Component, and the + # closed record contracts in k9_contract.ncl rejected exactly the + # extension fields §5.5 permits. Ledger entries stay advisory, so the + # four svc templates and the rsr checklist are reported, not failed, + # until their own items land. + K9_VALIDATE_LAYER: all run: | set -uo pipefail # The whole tracked corpus, through the same gate the pre-commit hook # uses, so CI and the hook cannot disagree about what a K9 file is. - # .machine_readable/k9-contract-debt.txt grandfathers the 25 files + # .machine_readable/k9-contract-debt.txt grandfathers the files # that predate the contract; it is shrink-only (a conforming file # left in it fails), and it does not protect a file this PR touches. set +e out="$(bash .githooks/validate-k9.sh 2>&1)"; rc=$? set -e { - echo '## K9 corpus conformance' + echo '## K9 corpus conformance (L2)' echo '```' printf '%s\n' "$out" echo '```' diff --git a/.machine_readable/REGISTRY.a2ml b/.machine_readable/REGISTRY.a2ml index 0f4ca3c25..552cabc9b 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:19b4ac92f44aa5e5c133755e8e2e4c2d18383cb64cccd4f31d576ba14e020be5" +source_hash = "sha256:e35752dba0b50684e97c37a4634f6fe08e175ef80b2f44f754b5726cb6c2d157" route = "the K9 specification, security analysis and adoption guidance (implementations live in hyperpolymath/k9-ecosystem)" [[spec]] @@ -153,7 +153,7 @@ name = "AXEL Protocol" stream = "protocol" home = "2-protocols/axel/" canonical_doc = "2-protocols/axel/README.adoc" -source_hash = "sha256:84005883477e12b0c748bc9e7d68cbb309c199e4509fbfaed27b0454e773a88d" +source_hash = "sha256:dbfe6d40af030e4dd23575d7f01dc86d1557b1d395534af73aa1ab0ac133c59c" route = "age-gating + explicit-content enforcement" [[spec]] @@ -216,7 +216,7 @@ name = "Session Management Standards" stream = "governance" home = "3-practice/session-management-standards/" canonical_doc = "3-practice/session-management-standards/README.adoc" -source_hash = "sha256:beea95b19ff9565abf30d34b758bfb669c5140fc5eef5046a494eafe20a7a91f" +source_hash = "sha256:9e3e5f7bc1469e0736359e3e85cba5311b8ed87056d137d86a98e81c4e3c4b5a" route = "continuity / verify / handover protocols" [[spec]] @@ -288,7 +288,7 @@ name = "FFP — Form-Fill Provenance" stream = "foundation" home = "1-formats/sub-specs/form-fill-provenance/" canonical_doc = "1-formats/sub-specs/form-fill-provenance/README.adoc" -source_hash = "sha256:e3b0c44298fc1c149afbf4c8996fb92427ae41e4649b934ca495991b7852b855" +source_hash = "sha256:d6e979cdf67dc045e0cf7fefc77e72fa1f2bc4424dd142f68e2f772875449124" route = "whether a PDF form was machine-filled or printed blank for hand completion, and what a print path must record" [[spec]] diff --git a/.machine_readable/contractiles/adjust/adjust.k9.ncl b/.machine_readable/contractiles/adjust/adjust.k9.ncl index 4974ba7f8..f89bda538 100644 --- a/.machine_readable/contractiles/adjust/adjust.k9.ncl +++ b/.machine_readable/contractiles/adjust/adjust.k9.ncl @@ -22,11 +22,22 @@ 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 -let base = import "../_base.ncl" in +# §11.4/K9-S013 — `pedigree_schema` lives in ../_base.ncl. There is no k9/ +# subdirectory under .machine_readable/contractiles/, so the old +# `import "../k9/template-hunt.k9.ncl"` never resolved — and the pedigree a +# reviewer read was not the pedigree a host would evaluate. +let base = import "../_base.ncl" in { - pedigree = base_k9.pedigree_schema & { + pedigree = base.pedigree_schema & { + # §5.2/§6.2 — schema_version, and the reserved-prefix component_type. The + # `contractile:` prefix is the whole of the disambiguation between this + # family's verbs (must/trust/dust/intend/...) and K9's own execution triad + # (must/just/nickel): the K9 contract never names a contractile verb, it + # only ever sees this string. + schema_version = "1.0.0", + component_type = "contractile:adjust", + semantics = "drift-tolerance-and-corrective-action", contractile_verb = "adjust", paired_xfile = "../adjust/Adjustfile.a2ml", paired_runner = "../adjust/adjust.ncl", @@ -49,11 +60,27 @@ let base = import "../_base.ncl" in allow_network = false, allow_filesystem_write_conditional = true, # auto_fix_when_available may edit allow_subprocess = true, + # §8.3/§8.4 — the grant. Each flag above is a REQUEST for a capability, + # and a request the grant does not cover is a contradiction inside the + # pedigree, not a preference (K9-S007). process.spawn pays for + # allow_subprocess; fs.write pays for the conditional write path, which + # K9 can only express as the whole capability. At 'Yard the leash is + # still the cap: §7.3 leaves a Yard component unable to write or spawn, + # so these entries describe the request, not a permission in force. + capabilities = [ + "fs.write", + "process.spawn", + ], probe_scope = 'measurement_plus_declared_fix, }, }, - variance_schema = { + # A contract, not payload. The fields below are declarations with no + # definition, and Nickel's export refuses to serialize a record containing + # one ("missing field definition for `entry_id`"). `not_exported` says what + # is true: this block documents the variance shape and is not data to + # serialize. Reading `variance_schema` as a contract is unaffected. + variance_schema | not_exported = { entry_id | String, reason | String, approved_by | String, diff --git a/.machine_readable/contractiles/bust/bust.k9.ncl b/.machine_readable/contractiles/bust/bust.k9.ncl index cef8427cc..facd1fc14 100644 --- a/.machine_readable/contractiles/bust/bust.k9.ncl +++ b/.machine_readable/contractiles/bust/bust.k9.ncl @@ -23,11 +23,22 @@ K9! # past their expiry), and the "it works, ship it" pattern where a # caller silently starts using a must-not-run API. -let base_k9 = import "../k9/template-hunt.k9.ncl" in -let base = import "../_base.ncl" in +# §11.4/K9-S013 — `pedigree_schema` lives in ../_base.ncl. There is no k9/ +# subdirectory under .machine_readable/contractiles/, so the old +# `import "../k9/template-hunt.k9.ncl"` never resolved — and the pedigree a +# reviewer read was not the pedigree a host would evaluate. +let base = import "../_base.ncl" in { - pedigree = base_k9.pedigree_schema & { + pedigree = base.pedigree_schema & { + # §5.2/§6.2 — schema_version, and the reserved-prefix component_type. The + # `contractile:` prefix is the whole of the disambiguation between this + # family's verbs (must/trust/dust/intend/...) and K9's own execution triad + # (must/just/nickel): the K9 contract never names a contractile verb, it + # only ever sees this string. + schema_version = "1.0.0", + component_type = "contractile:bust", + semantics = "hard-stop-and-expiry-declaration", contractile_verb = "bust", paired_xfile = "../bust/Bustfile.a2ml", paired_runner = "../bust/bust.ncl", @@ -50,11 +61,46 @@ let base = import "../_base.ncl" in allow_network = false, allow_filesystem_write = false, allow_subprocess = true, + signature_required = true, + # §8.3/§8.4 — the grant. allow_subprocess is a REQUEST for a capability, + # and a request the grant does not cover is a contradiction inside the + # pedigree, not a preference (K9-S007). + capabilities = [ + "process.spawn", + ], probe_scope = 'read_only, }, + + # §6.5/K9-S010 — mandatory at 'Hunt, and none of it may be a placeholder: + # §9 requires a dry_run precondition, i.e. a plan that was produced AND + # reviewed, and a reviewer cannot review a plan for a component that + # shells out while describing none of what it does. + side_effects = [ + "spawns read-only expiry and state-check probes as child processes (probe_scope = 'read_only; no mutation, no network)", + "records trigger outcomes in the host's evidence sinks (contractile_executions, bust_triggers_history); the drift-log append is indirected by the host, not performed by this component", + "a triggered hard stop fails the gate and blocks the merge — the gate is this component's effect, and no filesystem write is performed by it", + ], + + # §10.1/K9-S009 — a 'Hunt component MUST carry a signature block. This is + # the claim the file-format requires, and it is not a verified signature: + # with no external verifier the verdict is 'Present_Unverified (§10.2), + # and 'Present_Unverified does not satisfy the Hunt `signature` + # precondition (§10.4). So the file is conforming and still NOT authorised + # to run until the signing ceremony replaces this block. + signature = { + algorithm = "Ed25519", + key_id = "bust-k9", + payload_hash = "sha256:0000000000000000000000000000000000000000000000000000000000000000", + signature = "NOT-A-REAL-SIGNATURE", + }, }, - variance_schema = { + # A contract, not payload. The fields below are declarations with no + # definition, and Nickel's export refuses to serialize a record containing + # one ("missing field definition for `entry_id`"). `not_exported` says what + # is true: this block documents the variance shape and is not data to + # serialize. Reading `variance_schema` as a contract is unaffected. + variance_schema | not_exported = { entry_id | String, reason | String, approved_by | String, diff --git a/.machine_readable/contractiles/dust/dust.k9.ncl b/.machine_readable/contractiles/dust/dust.k9.ncl index 6c51e54f3..0227c1577 100644 --- a/.machine_readable/contractiles/dust/dust.k9.ncl +++ b/.machine_readable/contractiles/dust/dust.k9.ncl @@ -25,11 +25,22 @@ K9! # rollback path; anything retired must preserve the evidence of its # previous existence). -let base_k9 = import "../k9/template-hunt.k9.ncl" in -let base = import "../_base.ncl" in +# §11.4/K9-S013 — `pedigree_schema` lives in ../_base.ncl. There is no k9/ +# subdirectory under .machine_readable/contractiles/, so the old +# `import "../k9/template-hunt.k9.ncl"` never resolved — and the pedigree a +# reviewer read was not the pedigree a host would evaluate. +let base = import "../_base.ncl" in { - pedigree = base_k9.pedigree_schema & { + pedigree = base.pedigree_schema & { + # §5.2/§6.2 — schema_version, and the reserved-prefix component_type. The + # `contractile:` prefix is the whole of the disambiguation between this + # family's verbs (must/trust/dust/intend/...) and K9's own execution triad + # (must/just/nickel): the K9 contract never names a contractile verb, it + # only ever sees this string. + schema_version = "1.0.0", + component_type = "contractile:dust", + semantics = "recovery-and-retirement", contractile_verb = "dust", paired_xfile = "../dust/Dustfile.a2ml", paired_runner = "../dust/dust.ncl", @@ -55,6 +66,17 @@ let base = import "../_base.ncl" in # --apply flag + per-item approval. Default is dry-run. allow_filesystem_write_conditional = true, allow_subprocess = true, + # §8.3/§8.4 — the grant. Each flag above is a REQUEST for a capability, + # and a request the grant does not cover is a contradiction inside the + # pedigree, not a preference (K9-S007). process.spawn pays for + # allow_subprocess; fs.write pays for the conditional write path, which + # K9 can only express as the whole capability. At 'Yard the leash is + # still the cap: §7.3 leaves a Yard component unable to write or spawn, + # so these entries describe the request, not a permission in force. + capabilities = [ + "fs.write", + "process.spawn", + ], destructive_action_gating = { default_mode = 'dry_run, requires_flag = "--apply", @@ -64,7 +86,12 @@ let base = import "../_base.ncl" in }, }, - variance_schema = { + # A contract, not payload. The fields below are declarations with no + # definition, and Nickel's export refuses to serialize a record containing + # one ("missing field definition for `entry_id`"). `not_exported` says what + # is true: this block documents the variance shape and is not data to + # serialize. Reading `variance_schema` as a contract is unaffected. + variance_schema | not_exported = { entry_id | String, reason | String, approved_by | String, diff --git a/.machine_readable/contractiles/intend/intend.k9.ncl b/.machine_readable/contractiles/intend/intend.k9.ncl index 66f6e7d9d..253d42bac 100644 --- a/.machine_readable/contractiles/intend/intend.k9.ncl +++ b/.machine_readable/contractiles/intend/intend.k9.ncl @@ -27,11 +27,22 @@ 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 -let base = import "../_base.ncl" in +# §11.4/K9-S013 — `pedigree_schema` lives in ../_base.ncl. There is no k9/ +# subdirectory under .machine_readable/contractiles/, so the old +# `import "../k9/template-hunt.k9.ncl"` never resolved — and the pedigree a +# reviewer read was not the pedigree a host would evaluate. +let base = import "../_base.ncl" in { - pedigree = base_k9.pedigree_schema & { + pedigree = base.pedigree_schema & { + # §5.2/§6.2 — schema_version, and the reserved-prefix component_type. The + # `contractile:` prefix is the whole of the disambiguation between this + # family's verbs (must/trust/dust/intend/...) and K9's own execution triad + # (must/just/nickel): the K9 contract never names a contractile verb, it + # only ever sees this string. + schema_version = "1.0.0", + component_type = "contractile:intend", + semantics = "commitment-and-aspiration", contractile_verb = "intend", paired_xfile = "../intend/Intentfile.a2ml", paired_runner = "../intend/intend.ncl", @@ -64,6 +75,36 @@ let base = import "../_base.ncl" in allow_network = false, allow_filesystem_write = false, # evidence sinks are indirected allow_subprocess = true, + signature_required = true, + # §8.3/§8.4 — the grant. allow_subprocess is a REQUEST for a capability, + # and a request the grant does not cover is a contradiction inside the + # pedigree, not a preference (K9-S007). + capabilities = [ + "process.spawn", + ], + }, + + # §6.5/K9-S010 — mandatory at 'Hunt, and none of it may be a placeholder: + # §9 requires a dry_run precondition, i.e. a plan that was produced AND + # reviewed, and a reviewer cannot review a plan for a component that + # shells out while describing none of what it does. + side_effects = [ + "spawns read-only probes for tracked intents as child processes (no mutation, no network)", + "emits drift-log and receipt entries to the host's evidence sinks at session close; the sink writes are indirected by the host, not performed by this component", + "never blocks: an unmet intent or wish is reported as drift, not as a failed gate", + ], + + # §10.1/K9-S009 — a 'Hunt component MUST carry a signature block. This is + # the claim the file-format requires, and it is not a verified signature: + # with no external verifier the verdict is 'Present_Unverified (§10.2), + # and 'Present_Unverified does not satisfy the Hunt `signature` + # precondition (§10.4). So the file is conforming and still NOT authorised + # to run until the signing ceremony replaces this block. + signature = { + algorithm = "Ed25519", + key_id = "intend-k9", + payload_hash = "sha256:0000000000000000000000000000000000000000000000000000000000000000", + signature = "NOT-A-REAL-SIGNATURE", }, }, @@ -75,8 +116,14 @@ let base = import "../_base.ncl" in # drift, logged to the drift log. # Per user 2026-04-18: variances are structural, not magic-comment # markers — markers are gameable. + # + # A contract, not payload. The fields below are declarations with no + # definition, and Nickel's export refuses to serialize a record containing + # one ("missing field definition for `entry_id`"). `not_exported` says what + # is true: this block documents the variance shape and is not data to + # serialize. Reading `variance_schema` as a contract is unaffected. # ------------------------------------------------------------------- - variance_schema = { + variance_schema | not_exported = { entry_id | String, # which intent/wish id the variance applies to reason | String, approved_by | String, diff --git a/.machine_readable/contractiles/must/must.k9.ncl b/.machine_readable/contractiles/must/must.k9.ncl index 4287e9432..0eed24445 100644 --- a/.machine_readable/contractiles/must/must.k9.ncl +++ b/.machine_readable/contractiles/must/must.k9.ncl @@ -28,11 +28,22 @@ K9! # fabrication of success "facts" — invariants ground truth against # marketing copy) 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 +# §11.4/K9-S013 — `pedigree_schema` lives in ../_base.ncl. There is no k9/ +# subdirectory under .machine_readable/contractiles/, so the old +# `import "../k9/template-hunt.k9.ncl"` never resolved — and the pedigree a +# reviewer read was not the pedigree a host would evaluate. +let base = import "../_base.ncl" in { - pedigree = base_k9.pedigree_schema & { + pedigree = base.pedigree_schema & { + # §5.2/§6.2 — schema_version, and the reserved-prefix component_type. The + # `contractile:` prefix is the whole of the disambiguation between this + # family's verbs (must/trust/dust/intend/...) and K9's own execution triad + # (must/just/nickel): the K9 contract never names a contractile verb, it + # only ever sees this string. + schema_version = "1.0.0", + component_type = "contractile:must", + semantics = "invariant-assertion", contractile_verb = "must", paired_xfile = "../must/Mustfile.a2ml", paired_runner = "../must/must.ncl", @@ -58,6 +69,13 @@ let base = import "../_base.ncl" in allow_network = false, allow_filesystem_write = false, allow_subprocess = true, + signature_required = true, + # §8.3/§8.4 — the grant. allow_subprocess is a REQUEST for a capability, + # and a request the grant does not cover is a contradiction inside the + # pedigree, not a preference (K9-S007). + capabilities = [ + "process.spawn", + ], probe_scope = 'read_only, # must probes NEVER mutate probe_kinds_allowed = [ 'file_existence, @@ -74,14 +92,43 @@ let base = import "../_base.ncl" in 'exploit_attempt, # that's trust's safe_hacking territory ], }, + + # §6.5/K9-S010 — mandatory at 'Hunt, and none of it may be a placeholder: + # §9 requires a dry_run precondition, i.e. a plan that was produced AND + # reviewed, and a reviewer cannot review a plan for a component that + # shells out while describing none of what it does. + side_effects = [ + "spawns read-only probe commands as child processes (grep, test, file and schema checks); probe_scope = 'read_only and probe_kinds_denied excludes mutation, network calls, external APIs and exploit attempts", + "records per-invariant outcomes and their trend in the host's evidence sinks (contractile_executions, must_invariant_history); the DRIFT.a2ml append is indirected by the host, not performed by this component", + "a failed invariant fails the gate and blocks the merge — the gate is this component's effect, and no filesystem write is performed by it", + ], + + # §10.1/K9-S009 — a 'Hunt component MUST carry a signature block. This is + # the claim the file-format requires, and it is not a verified signature: + # with no external verifier the verdict is 'Present_Unverified (§10.2), + # and 'Present_Unverified does not satisfy the Hunt `signature` + # precondition (§10.4). So the file is conforming and still NOT authorised + # to run until the signing ceremony replaces this block. + signature = { + algorithm = "Ed25519", + key_id = "must-k9", + payload_hash = "sha256:0000000000000000000000000000000000000000000000000000000000000000", + signature = "NOT-A-REAL-SIGNATURE", + }, }, # ------------------------------------------------------------------- # 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. + # + # A contract, not payload. The fields below are declarations with no + # definition, and Nickel's export refuses to serialize a record containing + # one ("missing field definition for `entry_id`"). `not_exported` says what + # is true: this block documents the variance shape and is not data to + # serialize. Reading `variance_schema` as a contract is unaffected. # ------------------------------------------------------------------- - variance_schema = { + variance_schema | not_exported = { entry_id | String, # which invariant id the variance applies to reason | String, approved_by | String, # maintainer or above for critical-severity diff --git a/.machine_readable/contractiles/trust/trust.k9.ncl b/.machine_readable/contractiles/trust/trust.k9.ncl index 06574709f..dddcbf30f 100644 --- a/.machine_readable/contractiles/trust/trust.k9.ncl +++ b/.machine_readable/contractiles/trust/trust.k9.ncl @@ -43,11 +43,22 @@ K9! # * Failure-mode defenses cross-referenced — trust carries the most # defenses of any verb because the threat surface is widest. -let base_k9 = import "../k9/template-hunt.k9.ncl" in -let base = import "../_base.ncl" in +# §11.4/K9-S013 — `pedigree_schema` lives in ../_base.ncl. There is no k9/ +# subdirectory under .machine_readable/contractiles/, so the old +# `import "../k9/template-hunt.k9.ncl"` never resolved — and the pedigree a +# reviewer read was not the pedigree a host would evaluate. +let base = import "../_base.ncl" in { - pedigree = base_k9.pedigree_schema & { + pedigree = base.pedigree_schema & { + # §5.2/§6.2 — schema_version, and the reserved-prefix component_type. The + # `contractile:` prefix is the whole of the disambiguation between this + # family's verbs (must/trust/dust/intend/...) and K9's own execution triad + # (must/just/nickel): the K9 contract never names a contractile verb, it + # only ever sees this string. + schema_version = "1.0.0", + component_type = "contractile:trust", + semantics = "security-verification-and-authorised-probe", contractile_verb = "trust", paired_xfile = "../trust/Trustfile.a2ml", paired_runner = "../trust/trust.ncl", @@ -76,9 +87,39 @@ let base = import "../_base.ncl" in allow_network = false, # verifications offline by default allow_filesystem_write = false, # evidence sinks are indirected allow_subprocess = true, + signature_required = true, + # §8.3/§8.4 — the grant. allow_subprocess is a REQUEST for a capability, + # and a request the grant does not cover is a contradiction inside the + # pedigree, not a preference (K9-S007). + capabilities = [ + "process.spawn", + ], authorised_probes_only = true, # probe section explicitly lists allowed targets + probe classes probe_scope_enforcement = 'this_repo_only, # probes NEVER hit external systems }, + + # §6.5/K9-S010 — mandatory at 'Hunt, and none of it may be a placeholder: + # §9 requires a dry_run precondition, i.e. a plan that was produced AND + # reviewed, and a reviewer cannot review a plan for a component that + # shells out while describing none of what it does. + side_effects = [ + "spawns verification probes as child processes, including the authorised active probes (fuzz/injection/auth-bypass) — scoped to this repository only by probe_scope_enforcement = 'this_repo_only and authorised_probes_only = true", + "records verification outcomes in the host's evidence sinks (contractile_executions); the drift-log append is indirected by the host, not performed by this component", + "a failed verification fails the gate and blocks the merge (HARD GATE) — the gate is this component's effect, and no filesystem write is performed by it", + ], + + # §10.1/K9-S009 — a 'Hunt component MUST carry a signature block. This is + # the claim the file-format requires, and it is not a verified signature: + # with no external verifier the verdict is 'Present_Unverified (§10.2), + # and 'Present_Unverified does not satisfy the Hunt `signature` + # precondition (§10.4). So the file is conforming and still NOT authorised + # to run until the signing ceremony replaces this block. + signature = { + algorithm = "Ed25519", + key_id = "trust-k9", + payload_hash = "sha256:0000000000000000000000000000000000000000000000000000000000000000", + signature = "NOT-A-REAL-SIGNATURE", + }, }, # ------------------------------------------------------------------- @@ -88,8 +129,14 @@ let base = import "../_base.ncl" in # variances on trust entries are SIGNIFICANTLY more consequential than # variances on intend (reporting) entries — variance approver MUST # be the repo maintainer or above for critical-severity entries. + # + # A contract, not payload. The fields below are declarations with no + # definition, and Nickel's export refuses to serialize a record containing + # one ("missing field definition for `entry_id`"). `not_exported` says what + # is true: this block documents the variance shape and is not data to + # serialize. Reading `variance_schema` as a contract is unaffected. # ------------------------------------------------------------------- - variance_schema = { + variance_schema | not_exported = { entry_id | String, # which verification / probe id reason | String, approved_by | String, # maintainer or above for critical entries diff --git a/.machine_readable/k9-contract-debt.txt b/.machine_readable/k9-contract-debt.txt index ec198368b..603e47cea 100644 --- a/.machine_readable/k9-contract-debt.txt +++ b/.machine_readable/k9-contract-debt.txt @@ -18,27 +18,20 @@ # One repo-relative path per line. '#' comments and blanks ignored. # Baseline 2026-10-03, produced by: # 1-formats/k9/tools/k9-validate.sh --layer L1 --json -# Count: 17. -# Eight entries have been removed since the baseline: the six contractiles and -# the two axel config files. The axel pair conforms as of #D (leash moved to -# pedigree.security.leash; adding the K9! sentinel alone had left them failing -# K9-S001). The six contractile entries were dropped ahead of the fix — those -# files still fail K9-S002/S003/S007/S013, so the hook reports them as unlisted -# violations and no commit passes until #A lands. +# Count: 5 (25 non-conforming at the 2026-10-03 baseline; 20 entries removed +# since, and every one of the 20 now conforms on its own terms). +# +# Removed in two batches: +# * 8 — the six contractile components and the two axel config files — dropped +# when the K9! sentinel alone left them failing (a sentinel is not a +# pedigree). M1/#A gave the six a resolvable pedigree, a component_type and +# the §8.4 grant; M4/#D moved the axel pair's leash under +# pedigree.security. Both sets conform as of 2026-10-05. +# * 12 — the session-management PROTOCOL.k9 stubs, renamed to PROTOCOL.yaml +# by M2/#B: plain YAML off the reserved suffix, so they are no longer K9 +# files at all. .machine_readable/svc/k9/examples/setup-repo.k9.ncl .machine_readable/svc/k9/template-hunt.k9.ncl .machine_readable/svc/k9/template-kennel.k9.ncl .machine_readable/svc/k9/template-yard.k9.ncl -3-practice/session-management-standards/continuity/checkpoint-before-major-change/PROTOCOL.k9 -3-practice/session-management-standards/continuity/emergency-termination/PROTOCOL.k9 -3-practice/session-management-standards/continuity/planned-session-close/PROTOCOL.k9 -3-practice/session-management-standards/continuity/recovery-operation/PROTOCOL.k9 -3-practice/session-management-standards/continuity/repo-intake/PROTOCOL.k9 -3-practice/session-management-standards/handover/collaborative-transfer/PROTOCOL.k9 -3-practice/session-management-standards/handover/full-transfer/PROTOCOL.k9 -3-practice/session-management-standards/handover/human-transfer/PROTOCOL.k9 -3-practice/session-management-standards/handover/model-transfer/PROTOCOL.k9 -3-practice/session-management-standards/verify/maintenance-sweep/PROTOCOL.k9 -3-practice/session-management-standards/verify/release-audit/PROTOCOL.k9 -3-practice/session-management-standards/verify/substantial-completion/PROTOCOL.k9 rhodium-standard-repositories/rsr-compliance-checklist.k9.ncl diff --git a/.machine_readable/scorecards/session-management-standards.scorecard.a2ml b/.machine_readable/scorecards/session-management-standards.scorecard.a2ml index 812e2e395..1c1c4b24c 100644 --- a/.machine_readable/scorecards/session-management-standards.scorecard.a2ml +++ b/.machine_readable/scorecards/session-management-standards.scorecard.a2ml @@ -6,12 +6,12 @@ [scorecard] spec_id = "session-management-standards" version = "1.0.0" -assessed_date = "2026-07-03" +assessed_date = "2026-10-05" assessor = "estate-audit" [[must]] id = "M1" -text = "Every protocol directory MUST contain all four artefacts of the Protocol File Contract: CHECKLIST.adoc, PROTOCOL.k9, STATE-template.a2ml, and EXAMPLE-FILLED-IN.adoc (README.adoc, 'Protocol File Contract')." +text = "Every protocol directory MUST contain all four artefacts of the Protocol File Contract: CHECKLIST.adoc, PROTOCOL.yaml, STATE-template.a2ml, and EXAMPLE-FILLED-IN.adoc (README.adoc, 'Protocol File Contract')." system = "none — no script enforces the file contract; verified manually via `find`." status = "fail" effects = "Only continuity/planned-session-close/ has an EXAMPLE-FILLED-IN.adoc (11 of the 12 protocol directories lack one: repo-intake, checkpoint-before-major-change, emergency-termination, recovery-operation, all four handover/* protocols, and all three verify/* protocols). Downstream repos and any assistant trying to follow a worked example for these 11 protocols have nothing concrete to copy from." @@ -21,21 +21,22 @@ id = "M2" text = "Every protocol MUST include a 'Verification' block per the format defined in SHARED-CONTINUITY-CORE.adoc ('Every protocol must include a Verification block specifying the mandatory checks required before the protocol declares success')." system = "none" status = "fail" -effects = "The 8 stub-status protocols (repo-intake, checkpoint-before-major-change, recovery-operation, collaborative-transfer, full-transfer, human-transfer, model-transfer, maintenance-sweep, release-audit — all with 52-line CHECKLIST.adoc and 21-line PROTOCOL.k9) contain no Verification block. Consuming repos cannot mechanically confirm these protocols were executed correctly." +effects = "The 8 stub-status protocols (repo-intake, checkpoint-before-major-change, recovery-operation, collaborative-transfer, full-transfer, human-transfer, model-transfer, maintenance-sweep, release-audit — all with 52-line CHECKLIST.adoc and 23-line PROTOCOL.yaml) contain no Verification block. Consuming repos cannot mechanically confirm these protocols were executed correctly." [[must]] id = "M3" text = "Every protocol MUST capture the full 'Enriched 25-Field Continuity Core' defined in SHARED-CONTINUITY-CORE.adoc across meta-attributes, core sections, commits table, verifications table, and discipline-check audit." system = "none" status = "fail" -effects = "Stub protocols' PROTOCOL.k9/STATE-template.a2ml (e.g. continuity/repo-intake) list only 11 abbreviated fields (goal, current_task, last_completed_action, next_intended_action, repository, branch, head_commit, files_of_interest, known_blockers, residual_risks, recommended_next_protocol) — missing protocol-version, session-id/timestamps, commits-made table, verifications-run table, decisions/deferred, and the discipline-check audit. Downstream repos relying on the '25-field' continuity guarantee get materially incomplete state for 8 of 12 protocol families." +effects = "Stub protocols' PROTOCOL.yaml/STATE-template.a2ml (e.g. continuity/repo-intake) list only 11 abbreviated fields (goal, current_task, last_completed_action, next_intended_action, repository, branch, head_commit, files_of_interest, known_blockers, residual_risks, recommended_next_protocol) — missing protocol-version, session-id/timestamps, commits-made table, verifications-run table, decisions/deferred, and the discipline-check audit. Downstream repos relying on the '25-field' continuity guarantee get materially incomplete state for 8 of 12 protocol families." [[must]] id = "M4" -text = "Every artefact in this directory (PROTOCOL.k9, STATE-template.a2ml, *.adoc) MUST carry a valid SPDX-License-Identifier header per project-wide licensing convention." -system = "none — licence/SPDX compliance is a manual/governance judgement, not automated for this directory (no reuse-lint or SPDX CI job targets session-management-standards)." +text = "Every artefact in this directory (PROTOCOL.yaml, STATE-template.a2ml, *.adoc) MUST carry a valid SPDX-License-Identifier header per project-wide licensing convention." +system = "none — licence/SPDX compliance is a manual/governance judgement, not automated for this directory (no reuse-lint or SPDX CI job targets session-management-standards; `.githooks/validate-spdx.sh` covers the *.yaml files, but only from the optional pre-commit hook, and *.a2ml is outside its extension list)." status = "manual-only" -effects = "22 of 42 files (all PROTOCOL.k9 and STATE-template.a2ml files outside planned-session-close) have no SPDX header, e.g. continuity/repo-intake/PROTOCOL.k9 and handover/full-transfer/STATE-template.a2ml. Provenance/licence status for these files is ambiguous for anyone mirroring or redistributing them." +evidence = "All 12 PROTOCOL.yaml files carry `# SPDX-License-Identifier: MPL-2.0` (the 11 added in the 2026-10-05 #B rename, when the files left the `.k9` suffix and entered the SPDX hook's *.yaml extension scope)." +effects = "19 of 51 tracked files lack an SPDX header: all 11 STATE-template.a2ml files outside planned-session-close, plus 8 of the 9 templates/*.adoc files (e.g. handover/full-transfer/STATE-template.a2ml and templates/SESSION_SUMMARY.adoc). The 12 PROTOCOL files the previous audit counted are fixed; provenance/licence status for the remaining 19 is ambiguous for anyone mirroring or redistributing them." [[must]] id = "M5" @@ -48,10 +49,12 @@ effects = "n/a — this is currently sound, so no downstream impact." [[should]] id = "S1" -text = "PROTOCOL.k9 files SHOULD conform to the repository's existing K9 configuration grammar (K9! magic number, pedigree block with name/version, security-level field) since a generic validator for that grammar exists in this monorepo." -system = "1-formats/k9/actions/validate/validate-k9.sh — checks .k9 files for 'K9!' magic-number line 1, pedigree block, valid security level (kennel/yard/hunt), and SPDX header. Exists but is not wired to 3-practice/session-management-standards/." -status = "fail" -effects = "All 12 PROTOCOL.k9 files here begin with a plain '# ...' comment, not the 'K9!' magic number, and have no pedigree/security-level fields, so they would fail validate-k9.sh if it were ever run repo-wide. This is a naming collision risk: '.k9' files that don't follow the actual K9 grammar could confuse tooling or reviewers who expect the k9-svc schema." +text = "Machine protocol contracts under this standard SHOULD stay off the reserved `.k9`/`.k9.ncl` suffixes unless they conform to a registered K9 dialect (K9-CONTRACT-SPEC.adoc §4.2, 'Extension reservation'); plain YAML contracts use `.yaml`." +system = "1-formats/k9/tools/k9-validate.sh — the canonical K9 validator; `.githooks/validate-k9.sh` scans every tracked `*.k9`/`*.k9.ncl` through it. 3-practice/session-management-standards/ contains none." +status = "pass" +evidence = "All 12 PROTOCOL.k9 stubs were renamed to PROTOCOL.yaml by migration item M2 / issue #B (ruled 2026-10-05) and left `.machine_readable/k9-contract-debt.txt` (17 entries → 5); the S1 check below grounds the pass." +check = "test -z \"$(git ls-files '3-practice/session-management-standards/*.k9' '3-practice/session-management-standards/*.k9.ncl')\"" +effects = "n/a — resolved. Before the rename the 12 files produced an unclaimed-dialect error (K9-E005 ×12) and 11 missing-SPDX errors (K9-E004 ×11) against a suffix their bodies did not honour; renaming removes the false format promise for every reviewer, `file(1)` and future validator." [[should]] id = "S2" @@ -71,10 +74,10 @@ effects = "GUIDE.adoc only describes the binding pattern in prose ('Justfile ali [[should]] id = "S4" -text = "Protocols whose CHECKLIST.adoc has been substantially expanded (emergency-termination at 523 lines, substantial-completion at 300 lines) SHOULD have correspondingly expanded PROTOCOL.k9/STATE-template.a2ml machine artefacts, not stub-level ones." +text = "Protocols whose CHECKLIST.adoc has been substantially expanded (emergency-termination at 523 lines, substantial-completion at 300 lines) SHOULD have correspondingly expanded PROTOCOL.yaml/STATE-template.a2ml machine artefacts, not stub-level ones." system = "none" status = "fail" -effects = "continuity/emergency-termination/PROTOCOL.k9 is only 21 lines and its STATE-template.a2ml only 20 lines despite the checklist being fully written out; same pattern for verify/substantial-completion (21/20 lines). Any tooling meant to machine-drive these two protocols from PROTOCOL.k9/STATE-template.a2ml would be working from stub-level data despite the checklists implying full protocol maturity — a documentation/implementation mismatch." +effects = "continuity/emergency-termination/PROTOCOL.yaml is only 23 lines and its STATE-template.a2ml only 20 lines despite the checklist being fully written out; same pattern for verify/substantial-completion (23/20 lines). Any tooling meant to machine-drive these two protocols from PROTOCOL.yaml/STATE-template.a2ml would be working from stub-level data despite the checklists implying full protocol maturity — a documentation/implementation mismatch." [[could]] id = "C1" diff --git a/0-canon/COMPLIANCE-DASHBOARD.adoc b/0-canon/COMPLIANCE-DASHBOARD.adoc index 8ec4d1f28..5aed164be 100644 --- a/0-canon/COMPLIANCE-DASHBOARD.adoc +++ b/0-canon/COMPLIANCE-DASHBOARD.adoc @@ -43,7 +43,7 @@ ____ | `+component-readiness-grades+` | ❌ gap | 2/5 | 2/4 | 0/3 | 66% | 4/4 | 2026-07-03 | `+toolchain-readiness-grades+` | ❌ gap | 1/5 | 2/4 | 0/3 | 83% | 3/3 | 2026-07-03 | `+rhodium-standard-repositories+` | ❌ gap | 1/3 | 1/2 | 0/1 | 50% | 2/2 | 2026-07-03 -| `+session-management-standards+` | ❌ gap | 1/5 | 1/4 | 0/3 | 41% | 2/2 | 2026-07-03 +| `+session-management-standards+` | ❌ gap | 1/5 | 2/4 | 0/3 | 41% | 3/3 | 2026-10-05 | `+did-you-actually-do-that+` | ✅ met | 5/5 | 2/3 | 0/2 | 90% | 7/7 | 2026-07-03 | `+ensaid-config+` | ❌ gap | 0/5 | 0/3 | 0/3 | 90% | – | 2026-07-03 | `+accessibility+` | ❌ gap | 2/5 | 0/5 | 0/3 | 100% | 2/2 | 2026-07-03 @@ -60,7 +60,7 @@ ____ * *Specs with a scorecard:* 29 / 29 * *MUST requirements:* 38 passing / 141 total (73 failing) * *Estate systems coverage:* 67% of 324 graded requirements have a mechanical check -* *Grounded passes:* 69 / 69 (100%) pass rows carry an executable `+check+` run by `+--verify+` +* *Grounded passes:* 70 / 70 (100%) pass rows carry an executable `+check+` run by `+--verify+` == How this dashboard stays honest diff --git a/1-formats/k9/SPEC.adoc b/1-formats/k9/SPEC.adoc index 1cd39cefb..538a43652 100644 --- a/1-formats/k9/SPEC.adoc +++ b/1-formats/k9/SPEC.adoc @@ -40,7 +40,7 @@ Specification v1.0.0] — *this document is the format overview; conformance is specified there* |Contract file |link:spec/contract/k9_contract.ncl[`spec/contract/k9_contract.ncl`] |Canonical validator |link:tools/k9-validate.sh[`tools/k9-validate.sh`] -|Conformance suite |link:tools/fixtures/[`tools/fixtures/`] (5 positive, 21 negative) +|Conformance suite |link:tools/fixtures/[`tools/fixtures/`] (6 positive, 21 negative) |=== [IMPORTANT] diff --git a/1-formats/k9/spec/K9-CONTRACT-SPEC.adoc b/1-formats/k9/spec/K9-CONTRACT-SPEC.adoc index 0486e8479..489c71ebf 100644 --- a/1-formats/k9/spec/K9-CONTRACT-SPEC.adoc +++ b/1-formats/k9/spec/K9-CONTRACT-SPEC.adoc @@ -46,7 +46,7 @@ that file disagree, *this prose wins and the file is a bug*. |Schema major |`1` |Contract file |link:{contract-file}[`spec/contract/k9_contract.ncl`] |Canonical validator |link:../tools/k9-validate.sh[`tools/k9-validate.sh`] -|Conformance suite |link:../tools/fixtures/[`tools/fixtures/`] (5 positive, 21 negative) +|Conformance suite |link:../tools/fixtures/[`tools/fixtures/`] (6 positive, 21 negative) |Ruling |D173 (standards#1058, 2026-09-30) |Supersedes |the prose-only contract description in link:../SPEC.adoc[`SPEC.adoc`] §L2 and §The Active Pedigree |=== @@ -240,12 +240,13 @@ file that carries one of them and belongs to none of them is *unclaimed* (K9-E005) and MUST be reported, not skipped. This is not pedantry. Twelve files under -`3-practice/session-management-standards/` are named `PROTOCOL.k9` and contain +`3-practice/session-management-standards/` were named `PROTOCOL.k9` and held YAML stubs for a session-management protocol that is neither K9 SVC nor k9-coordination. A reviewer, `file(1)`, an editor mode and every future validator all read the suffix as a format promise. Reserving the suffix is -what makes that promise mean something; renaming those files is migration item -M4. +what makes that promise mean something; those files are now `PROTOCOL.yaml` +(migration item M2 of link:MIGRATION-1058.adoc[`MIGRATION-1058.adoc`], applied +2026-10-05). === K9-E005 — the discriminator @@ -343,6 +344,17 @@ fields — the estate's contractiles use `probe_scope` and *weaken* a rule in this document. An unknown field that grants access is not an extension, it is a vulnerability. +The machine-readable form carries that row as a `, ..` tail on `Component`, +`Pedigree`, `Metadata`, `Security` and `Signature` in +link:contract/k9_contract.ncl[`k9_contract.ncl`] — and it has to be written +down there, because a Nickel record contract is *closed* by default. Without +the tail, a component carrying a legitimate extension field is blamed with +"extra field" by a file whose own first rule is that this prose wins. The +positive control link:../tools/fixtures/valid/extension-fields.k9.ncl[ +`fixtures/valid/extension-fields.k9.ncl`] fails at L2 if any of those tails is +removed. (The host-side `HuntEvidence` record stays closed on purpose: its +fields are evidence the host supplies, not a component's own business.) + == The component pedigree === K9-S001 — the pedigree block @@ -716,9 +728,13 @@ MIME handler, not a reader's classifier — can tell the file is a component at all. A licence in such a file is *unenforceable*, which is worse than no licence, because a reviewer reading the file sees one and believes it. -This is the live state of all six -`.machine_readable/contractiles/*/*.k9.ncl` files, which carry `pedigree` -blocks (several at `'Hunt`) and no envelope. Migration item M2. +*Resolved 2026-10-05 (MIGRATION-1058 M1, #A).* The `K9!` envelope already +carried line 1 of all six `.machine_readable/contractiles/*/*.k9.ncl` files +when this was checked against the tree, so K9-S012 had no live instance; the +paragraph above describes the revision before the envelope was added. What the +six were still missing — `component_type`, the §8.4 grant for their `allow_*` +flags, and the §6.5/§9 material for the four `'Hunt` verbs — is recorded in +MIGRATION-1058 M1. === K9-S014 — no stray leash claims @@ -742,11 +758,12 @@ so an unresolvable import means the pedigree a reviewer read is not the pedigree a host would evaluate — and the reviewer's reading is the one that decides whether the component is trusted. -All six contractile `.k9.ncl` files import `"../k9/template-hunt.k9.ncl"`, a -path that does not exist (`.machine_readable/contractiles/` has no `k9/` -subdirectory), and then reference `base_k9.pedigree_schema`, a field the real -template does not define — it is `_base.ncl` that defines `pedigree_schema`. -Migration item M2. +*Fixed 2026-10-05 (MIGRATION-1058 M1, #A).* All six contractile `.k9.ncl` +files imported `"../k9/template-hunt.k9.ncl"`, a path that does not exist +(`.machine_readable/contractiles/` has no `k9/` subdirectory), and referenced +`base_k9.pedigree_schema`, a field the real template did not define. They now +merge `base.pedigree_schema` from `../_base.ncl`, the file that defines it, and +the validator reports all six conforming at L1. === Library leash @@ -902,7 +919,7 @@ hidden; review is the control, as it is for `.machine_readable/inline-python-all [source] ---- 1-formats/k9/tools/fixtures/ -├── valid/ 5 positive controls +├── valid/ 6 positive controls └── invalid/ 21 negative controls (19 assertable without a toolchain, 2 needing Nickel) ---- @@ -933,7 +950,7 @@ while the rule under test does nothing. # built-in assertions: the mirrors, the arithmetic, the extractor, the strip 1-formats/k9/tools/k9-validate.sh --self-test -# the corpus: 5 positive + 21 negative +# the corpus: 6 positive + 21 negative 1-formats/k9/tools/k9-validate.sh --fixtures 1-formats/k9/tools/fixtures # with Nickel on PATH, every format layer must actually run diff --git a/1-formats/k9/spec/MIGRATION-1058.adoc b/1-formats/k9/spec/MIGRATION-1058.adoc index f065661cf..393a8c13f 100644 --- a/1-formats/k9/spec/MIGRATION-1058.adoc +++ b/1-formats/k9/spec/MIGRATION-1058.adoc @@ -15,7 +15,7 @@ contract for `application/vnd.k9`, with link:contract/k9_contract.ncl[`contract/k9_contract.ncl`] as its machine-readable form, link:../tools/k9-validate.sh[`tools/k9-validate.sh`] as the canonical validator, and link:../tools/fixtures/[`tools/fixtures/`] as the -conformance suite (5 positive controls, 21 negative). +conformance suite (6 positive controls, 21 negative). Landed with it: @@ -154,6 +154,64 @@ a dangling import a conformance failure rather than a style note. files, twelve rule instances, and it closes the only place in the repo where a `'Hunt` pedigree is invisible to every tool. +==== M1 — resolved (2026-10-05) + +All six `.machine_readable/contractiles/*/*.k9.ncl` files pass the canonical +validator at L1 with 0 findings, so K9-S013 has no instance here. Three +corrections to the paragraph above, from reading the tree rather than the +report: + +* The `K9!` envelope was already line 1 of all six, so "add `K9!` as line 1" + was already done and **K9-S012 does not fire**. What was missing was + everything the envelope makes enforceable: `component_type`, the §8.4 grant + for each `allow_*` flag, and — for the four `'Hunt` verbs — a signature + block, `signature_required`, and non-empty `side_effects`. +* The import now merges `base.pedigree_schema` from `../_base.ncl`, which is + where `pedigree_schema` actually lives; no contractile-specific base was + created, because `_base.ncl` is already the shared base for the + `.ncl` runners. `variance_schema` is marked `not_exported`: its fields + are declarations with no definitions, and Nickel's export refuses a record + containing one. +* The ledger had already lost its six contractile entries (the header's + "6 contractiles" removal), which is why the hook reported all six as hard + FAILs until they conformed. This change removes nothing from the ledger and + re-lists nothing; it makes the earlier removal true. + +*Naming resolution, stated rather than implied* (the issue's second ask): the +K9 contract never names a contractile verb. A contractile component is an +ordinary K9 component whose `component_type` begins `contractile:` — that +prefix is the entire tie between the contractiles family +(must/trust/dust/intend) and K9's execution triad (must/just/nickel), and it is +the only name K9 machinery ever sees. DEED translation renames nothing this +specification depends on, and the contractile family keeps its verbs. + +*Leash and grant:* adjust and dust are `'Yard` and request `fs.write` and +`process.spawn`; at `'Yard` the leash is the cap (§7.3 — pure evaluation), so +those entries record the *request*, not a permission in force. Recording them +is still mandatory: §8.4/K9-S007 makes a flag whose capability the grant does +not cover a contradiction inside the pedigree, not a preference. bust, intend, +must and trust are `'Hunt`; their signature blocks are claims, so the §10.2 +verdict stays `'Present_Unverified` and §10.4 leaves all four unauthorised to +run until the signing ceremony replaces the placeholder key. + +*Verification* (branch `arena/01a10a3b-standards`, run `37262876162`): +`--self-test` all assertions passed; `--strict --fixtures` 5 positive, 21 +negative, 0 needing nickel, 0 failures; L1 over the six: `pass`, 0 findings; +the corpus report falls 8 → 2 violations (the remainder are the Axel files of +M4/#D, untouched here) with 0 stale ledger entries. + +*Contract-side follow-up, done in M7's first step (below):* the corpus step ran +at L1, so the six were never evaluated against `K9.Component`. §5.5 permits the +extension fields they carry — `pedigree.semantics`, `pedigree.contractile_verb`, +`pedigree.tier`, `pedigree.authority`, `pedigree.variance_schema`, +`pedigree.execution`, `pedigree.failure_mode_defenses`, `security.probe_scope`, +`security.probe_kinds_*`, `security.allow_filesystem_write_conditional` — but +`Component`, `Pedigree`, `Metadata`, `Security` and `Signature` in +`k9_contract.ncl` were closed record contracts, so L2 would have blamed every +one of them ("extra field"). Those five records now take a `, ..` tail, and the +new positive control `fixtures/valid/extension-fields.k9.ncl` fails at L2 if any +of them closes again. + === M2 — Twelve `PROTOCOL.k9` stubs claim a reserved suffix *Files:* `3-practice/session-management-standards/*/*/PROTOCOL.k9` @@ -175,10 +233,21 @@ are ordinary structured data. machine-consumed as K9, give it the `K9!` envelope and either the Nickel component shape or a registered dialect of its own. -*Note:* the same scorecard references +*Ruling — outcome (a), applied 2026-10-05 (issue #B).* These are ordinary +structured data: plain YAML contracts for session-management protocols that no +K9 host loads. Option (b) would mean inventing a dialect so the naming +collision could keep its name — the suffix exists to promise a format, and the +honest move is to stop promising one. All 12 files are now `PROTOCOL.yaml`; +their 12 ledger entries are gone (17 → 5); the 11 files that lacked an SPDX +header gained `MPL-2.0` headers as they entered the `*.yaml` extension scope of +`.githooks/validate-spdx.sh`; and the scorecard's S1 is re-graded against +`1-formats/k9/tools/k9-validate.sh`, with the reservation rule as the +requirement. + +*Note (fixed):* the scorecard used to reference `1-formats/k9/actions/validate/validate-k9.sh`, a path that does not exist -(`ls` confirms no `1-formats/k9/actions/`). Whatever is chosen, that reference -should be repointed at `1-formats/k9/tools/k9-validate.sh`. +(`ls` confirms no `1-formats/k9/actions/`). Its S1 entry now points at +`1-formats/k9/tools/k9-validate.sh`. === M3 — Three templates ship placeholders and one example is unsigned @@ -257,16 +326,15 @@ the report: leash without deleting that envelope yields K9-S001, and deleting both demotes them to libraries and discards the declared level entirely. -Not fixed here, and recorded for M7/#F instead: §6.4 permits unknown top-level -fields, but `Component` in `contract/k9_contract.ncl` is a record contract with -no `, ..`, and Nickel forbids additional fields in record contracts by default -(1.18.0 manual, "Open record contracts"); the manual defines `optional` as "not -mandatory" and nothing more, so it does not open a record — `, ..` alone does -(1.18.0 `doc/manual/syntax.md`, field metadata). If that reading holds, K9-N001 will -report `extra field` for every component that keeps its payload at top level — -`burble-metadata.k9.ncl` (`rsr`) and both `examples/` files as much as these -two. That is an estate-wide L2 reconciliation, not an S014 question, and no -`nickel` binary was available to run it here. +Resolved in M7's first step (below): §6.4/§5.5 permit unknown top-level +fields, but `Component` in `contract/k9_contract.ncl` was a record contract +with no `, ..`, and Nickel forbids additional fields in record contracts by +default (1.18.0 manual, "Open record contracts"); the manual defines `optional` +as "not mandatory" and nothing more, so it does not open a record — `, ..` +alone does (1.18.0 `doc/manual/syntax.md`, field metadata). Against a closed +`Component`, K9-N001 would have reported `extra field` for every component that +keeps its payload at top level — `burble-metadata.k9.ncl` and both `examples/` +files as much as these two. The record now takes a `, ..` tail. === M5 — `rsr-compliance-checklist.k9.ncl` is not a single Nickel term @@ -334,6 +402,32 @@ M7 is therefore: raise the corpus step to L2 for the five conforming files, then work the ledger down. Expect each removal to need a real fix, not a relabel. +==== M7 — started (2026-10-05) + +The corpus step in `.github/workflows/k9-contractile.yml` now runs the hook with +`K9_VALIDATE_LAYER=all`, so CI checks the whole corpus at L2 while the +pre-commit hook keeps its L1 default — a hook that fails on a laptop without a +toolchain is a hook people bypass. Two things had to be true first, and both now +are: + +* `k9_contract.ncl`'s record contracts were CLOSED, so the extension fields + §5.5 permits were blamed; M1's and M4's notes above both measured that + against live files. `Component`, `Pedigree`, `Metadata`, `Security` and + `Signature` now carry `, ..`, and the host-side `HuntEvidence` stays closed on + purpose. By this file's own precedence rule — the prose wins, the file is the + bug — that is a conformance fix, not a contract revision, so + `contract_version` stays 1.0.0. +* Nothing tested it. `tools/fixtures/valid/extension-fields.k9.ncl` is a new + positive control shaped like a contractile: extension fields at pedigree, + security, metadata and top level, plus a declaration-only `variance_schema` + marked `not_exported`, so it exercises the *export* path rather than only the + typecheck. The suite is now 6 positive / 21 negative. + +What remains of M7's sentence: the five ledger entries — the four `svc/k9` +files and `rsr-compliance-checklist.k9.ncl` — are *reported* as grandfathered +debt by the L2 run rather than failed (they are unchanged), and working them +down still needs a real fix each: M3 for the four, M5 for the checklist. + Still never run anywhere: `nickel format --check` on a `.k9.ncl` body. `ci-pipeline.yml` excludes that pathspec, and formatting is not reproducible without the binary — so any M1–M6 change that adds a body will be the first diff --git a/1-formats/k9/spec/contract/k9_contract.ncl b/1-formats/k9/spec/contract/k9_contract.ncl index be8736428..252cfdc64 100644 --- a/1-formats/k9/spec/contract/k9_contract.ncl +++ b/1-formats/k9/spec/contract/k9_contract.ncl @@ -72,18 +72,18 @@ let ExtensionPrefix = "x-" in # ───────────────────────────────────────────────────────────────────────── # §5.2 / §8.1 — name shapes, using nothing beyond length and substring # ───────────────────────────────────────────────────────────────────────── - let is_digit = fun c => c == "0" - || c == "1" - || c == "2" - || c == "3" - || c == "4" - || c == "5" - || c == "6" - || c == "7" - || c == "8" - || c == "9" in + || c == "1" + || c == "2" + || c == "3" + || c == "4" + || c == "5" + || c == "6" + || c == "7" + || c == "8" + || c == "9" +in # A numeric dot-triple whose MAJOR segment equals `major`. Walks the string # once. Rejects: non-digits, a leading or trailing dot, an empty segment, a @@ -109,14 +109,15 @@ let is_semver_of = fun major v => seg_len > 0 && go (i + 1) (dots + 1) 0 false s else if is_digit c then (first_zero == false || seg_len == 0) - && go (i + 1) dots (seg_len + 1) (c == "0") s + && go (i + 1) dots (seg_len + 1) (c == "0") s else false in # A dot-triple is at least `want` plus "N.N": three more characters. std.string.length v >= (std.string.length want + 3) - && std.string.substring 0 (std.string.length want) v == want - && go 0 0 0 false v in + && std.string.substring 0 (std.string.length want) v == want + && go 0 0 0 false v +in # ───────────────────────────────────────────────────────────────────────── # The contract record. Nickel records are recursive, so fields below refer to @@ -177,8 +178,7 @@ let is_semver_of = fun major v => # pedigree cannot crash on it. leash_of = fun pedigree => if std.record.has_field "security" pedigree - && std.record.has_field "leash" pedigree.security - then + && std.record.has_field "leash" pedigree.security then pedigree.security.leash else 'Kennel, @@ -208,13 +208,13 @@ let is_semver_of = fun major v => # after the vendor segment, i.e. "x-vendor.something", never bare "x-vendor". is_extension = fun name => std.string.length name > std.string.length ExtensionPrefix - && std.string.substring 0 (std.string.length ExtensionPrefix) name - == ExtensionPrefix - && std.array.length (std.string.split "." name) >= 2, + && std.string.substring 0 (std.string.length ExtensionPrefix) name == ExtensionPrefix + && std.array.length (std.string.split "." name) >= 2, - 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 + ), # An explicit allow-list. DEFAULT-DENY is expressed by the empty grant: it # permits nothing, and "nothing" is the correct answer for an absent grant. @@ -241,28 +241,25 @@ let is_semver_of = fun major v => required_capabilities = fun security => ( if std.record.has_field "allow_network" security - && security.allow_network - then + && security.allow_network then ["net.fetch"] else [] ) - @ ( - if std.record.has_field "allow_filesystem_write" security - && security.allow_filesystem_write - then - ["fs.write"] - else - [] - ) - @ ( - if std.record.has_field "allow_subprocess" security - && security.allow_subprocess - then - ["process.spawn"] - else - [] - ), + @ ( + if std.record.has_field "allow_filesystem_write" security + && security.allow_filesystem_write then + ["fs.write"] + else + [] + ) + @ ( + if std.record.has_field "allow_subprocess" security + && security.allow_subprocess then + ["process.spawn"] + else + [] + ), # What the grant fails to cover. Empty iff the pedigree is self-consistent. grant_deficit = fun grant security => @@ -285,6 +282,11 @@ let is_semver_of = fun major v => # The host supplies one Bool of EVIDENCE per precondition. Evidence is # runtime fact, not a static component field — which is why `check_level` # in leash.ncl can never authorise Hunt on its own. + # Host-side, and deliberately CLOSED unlike the component-facing records + # above: §9.2 says the host supplies one Boolean of evidence per + # precondition, so a misspelt evidence field must be an error rather than a + # silently ignored precondition. §5.5's openness is about a *component's* + # fields. HuntEvidence = { signature | Bool, policy | Bool, @@ -299,10 +301,10 @@ let is_semver_of = fun major v => authorize_hunt = fun evidence => let unmet = (if evidence.signature then [] else ["signature"]) - @ (if evidence.policy then [] else ["policy"]) - @ (if evidence.sandbox then [] else ["sandbox"]) - @ (if evidence.dry_run then [] else ["dry_run"]) - @ (if evidence.capability_grant then [] else ["capability_grant"]) + @ (if evidence.policy then [] else ["policy"]) + @ (if evidence.sandbox then [] else ["sandbox"]) + @ (if evidence.dry_run then [] else ["dry_run"]) + @ (if evidence.capability_grant then [] else ["capability_grant"]) in if std.array.length unmet == 0 then { @@ -316,7 +318,7 @@ let is_semver_of = fun major v => enforced_level = 'Yard, reason = "Hunt requires all five preconditions; unmet: " - ++ std.string.join ", " unmet, + ++ std.string.join ", " unmet, unmet_preconditions = unmet, }, @@ -365,6 +367,10 @@ let is_semver_of = fun major v => payload_hash | String | optional, signed_at | String | optional, key_id | String | default = "primary", + # §5.5 — unknown fields are PERMITTED; see the `..` block above `Metadata`. + # A host that reads `signature.public_key` MUST NOT be the reason a future + # `counter_signature` field is rejected. + .. }, # ── §6 The component pedigree, v1 ──────────────────────────────────── @@ -373,16 +379,50 @@ let is_semver_of = fun major v => # actually carry (`.machine_readable/svc/k9/template-*.k9.ncl`), with two # normative tightenings: `component_type` is REQUIRED (§6.2), and the # security flags must be paid for in the capability grant (§8.4). + # + # ── THE `..` TAIL, AND WHY EVERY COMPONENT-FACING RECORD HERE HAS ONE ── + # + # §5.5's table ends with "Unknown fields anywhere | PERMITTED. A reader SHOULD + # report them and MUST NOT consult them to relax any requirement of §8 or §9." + # That is normative prose, and by this file's own first rule the prose wins: + # where the two disagree, this file is the bug. + # + # They disagreed until 2026-10-05. A Nickel record contract is CLOSED by + # default — the sharp edge is recorded in Nickel's own integration suite, + # core/tests/integration/inputs/contracts/record_contract_extra_field.ncl, + # which asserts `EvalError::BlameError` with the message "extra field" and + # advises appending `, ..`. So a component carrying a legitimate extension + # field was blamed by this file while §5.5 permitted the field. The six + # `.machine_readable/contractiles/*/*.k9.ncl` components are the live case + # (they keep `semantics`, `contractile_verb`, `tier`, `authority`, + # `variance_schema`, `execution`, `failure_mode_defenses` in the pedigree; + # `probe_scope`, `probe_kinds_*`, `allow_filesystem_write_conditional` in + # security; `paired_xfile`, `paired_runner` in metadata), and + # `2-protocols/axel/config/*.k9.ncl` keep their payload at top level. + # + # Opening these five records is a CONFORMANCE fix, not a contract revision: + # the specified contract (the prose) never changed, so `contract_version` + # stays 1.0.0. It does not weaken §8 or §9 either — §5.5's own last clause + # forbids that, and the validator enforces the capability arithmetic over the + # extracted facts whatever an extension field claims (K9-S007, K9-S008). + # + # The positive control `tools/fixtures/valid/extension-fields.k9.ncl` exists + # so this cannot silently regress: it fails at L2 the moment any of these + # records closes again. Metadata = { name | String, version | String | default = "0.0.0", description | String | optional, author | String | optional, + # §5.5 — see the block comment above. The estate's contractiles add + # `paired_xfile` / `paired_runner` here. + .. }, Security = { - # §7 — closed set. An unknown tag is a contract violation, not a warning. + # §7 — closed set. An unknown LEASH TAG is a contract violation, not a + # warning; an unknown FIELD in this block is §5.5's business. leash | SecurityLevel, trust_level | String | default = "unset", allow_network | Bool | default = false, @@ -393,6 +433,11 @@ let is_semver_of = fun major v => signature_required | Bool | default = false, # §8.3 — the explicit grant. Default-deny: absent means empty. capabilities | CapabilityGrant | default = [], + # §5.5 — unknown fields are PERMITTED, and the prose names this block's own + # examples: "the estate's contractiles use `probe_scope` and + # `authorised_probes_only`". No conforming reader may let one of them + # weaken §8 or §9 — the grant above is what a host enforces. + .. }, Pedigree = { @@ -412,6 +457,12 @@ let is_semver_of = fun major v => side_effects | Array String | default = [], # §10.1 — the signature claim. Presence is per-leash; see Signature above. signature | Signature | optional, + # §5.5 — unknown fields are PERMITTED. This is the line MIGRATION-1058 M1 + # turned on: the contractile family keeps its own vocabulary in the pedigree + # (`semantics`, `contractile_verb`, `tier`, `authority`, `variance_schema`, + # `execution`, `failure_mode_defenses`), and nothing in this file may force + # those names out of it. + .. }, # The whole component: the pedigree plus the optional Nickel payload. @@ -436,6 +487,12 @@ let is_semver_of = fun major v => config | { _ : Dyn } | optional, recipes | { _ : Dyn } | optional, validation | { _ : Dyn } | optional, + # §5.5 — unknown fields are PERMITTED at top level too, which is the case + # MIGRATION-1058 M4 recorded for whoever first ran the corpus at L2: + # `2-protocols/axel/config/*.k9.ncl` keep their payload (`ci`, `local_dev`, + # `quality`; `metadata.k9.ncl`: `project`, `tech_stack`, `deployment`, + # `features`, `status`) beside `pedigree`. + .. }, # ── §11 Library dialect ────────────────────────────────────────────── @@ -445,5 +502,5 @@ let is_semver_of = fun major v => # no pedigree, no leash claim, no execution licence of any kind. is_library = fun lib => !(std.record.has_field "pedigree" lib) - && !(std.record.has_field "leash" lib), + && !(std.record.has_field "leash" lib), } diff --git a/1-formats/k9/tools/README.adoc b/1-formats/k9/tools/README.adoc index f9402be88..d20b7631b 100644 --- a/1-formats/k9/tools/README.adoc +++ b/1-formats/k9/tools/README.adoc @@ -11,7 +11,7 @@ Prose: link:../spec/K9-CONTRACT-SPEC.adoc[`spec/K9-CONTRACT-SPEC.adoc`] |=== |`k9-validate.sh` |The canonical validator. The only place K9 rules are *executed*; the contract file is where they are *stated*. -|`fixtures/valid/` |5 positive controls. +|`fixtures/valid/` |6 positive controls. |`fixtures/invalid/` |21 negative controls. The load-bearing half. |=== @@ -19,6 +19,10 @@ The local gate (`.githooks/validate-k9.sh`) and the CI gate (`.github/workflows/k9-contractile.yml`) both call `k9-validate.sh`. Neither owns a rule. +The hook's default layer is L1 (it must run on a machine with no toolchain); +the CI corpus step raises it with `K9_VALIDATE_LAYER=all`, because CI installs +the pinned `nickel` binary. Ledger entries stay advisory at either layer. + == Running it [source,bash] diff --git a/1-formats/k9/tools/fixtures/invalid/L0-K9-E005-unclaimed-body.k9.ncl b/1-formats/k9/tools/fixtures/invalid/L0-K9-E005-unclaimed-body.k9.ncl index 44478d97e..a23b566cc 100644 --- a/1-formats/k9/tools/fixtures/invalid/L0-K9-E005-unclaimed-body.k9.ncl +++ b/1-formats/k9/tools/fixtures/invalid/L0-K9-E005-unclaimed-body.k9.ncl @@ -1,8 +1,9 @@ # SPDX-License-Identifier: MPL-2.0 # Negative control §4.4: a `.k9.ncl` suffix with neither a Nickel body nor the # coordination dialect's YAML body. This is the shape of the estate's twelve -# session-management PROTOCOL.k9 stubs, which claim a K9 suffix and belong to -# no K9 dialect. Reserving the suffix is what stops a reviewer (or `file`) +# session-management stubs, which claimed a K9 suffix and belonged to no K9 +# dialect (renamed to PROTOCOL.yaml by migration item M2, 2026-10-05; kept as +# the regression control for this rule). Reserving the suffix is what stops a reviewer (or `file`) # from believing a format promise the body does not keep. protocol: family: "handover" diff --git a/1-formats/k9/tools/fixtures/valid/extension-fields.k9.ncl b/1-formats/k9/tools/fixtures/valid/extension-fields.k9.ncl new file mode 100644 index 000000000..4cd168dd4 --- /dev/null +++ b/1-formats/k9/tools/fixtures/valid/extension-fields.k9.ncl @@ -0,0 +1,76 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# extension-fields.k9.ncl — positive control: §5.5's "Unknown fields anywhere +# | PERMITTED". +# +# WHY THIS CONTROL EXISTS +# Until it did, not one positive control left the contract's declared field +# set, so a whole clause of §5.5 was untested. Nickel's record contracts are +# CLOSED by default — `core/tests/integration/inputs/contracts/ +# record_contract_extra_field.ncl` is the blame control, `EvalError:: +# BlameError` with "extra field" — so `Component`/`Pedigree`/`Security`/ +# `Metadata`/`Signature` in spec/contract/k9_contract.ncl each rejected the +# extension fields the prose permitted, and nothing in CI noticed. The six +# `.machine_readable/contractiles/*/*.k9.ncl` components are the live case of +# that disagreement (MIGRATION-1058 M1), and the contract's own §5.5 note +# explains the fix. This control fails at L2 the moment any of those records +# closes again. +# +# WHAT IT DELIBERATELY KEEPS DOING +# An extension field is DATA, never a grant: `allow_filesystem_write` stays +# false and `capabilities` stays empty, even though +# `allow_filesystem_write_conditional` (a contractile extension field, §5.5) +# says a write path exists. §5.5's last clause is that no reader may consult +# an unknown field to relax §8 or §9 — so nothing here is derivable from it, +# and §7.3 caps 'Yard at evaluation in any case. +{ + pedigree = { + schema_version = "1.0.0", + component_type = "contractile:fixture", + semantics = "extension-field-tolerance", + contractile_verb = "fixture", + tier = 'Yard, + authority = 'advisory, + security = { + leash = 'Yard, + trust_level = "contract-eval-only", + allow_network = false, + allow_filesystem_write = false, + allow_subprocess = false, + capabilities = [], + probe_scope = 'read_only, + probe_kinds_allowed = ['file_existence, 'pattern_presence], + probe_kinds_denied = ['network_call, 'filesystem_mutation], + allow_filesystem_write_conditional = false, + }, + metadata = { + name = "extension-fields", + version = "1.0.0", + description = "Positive control: extension fields at pedigree, security and metadata level.", + paired_xfile = "Examplefile.a2ml", + paired_runner = "example.ncl", + }, + warnings = [ + "Extension fields are permitted and are not consulted for permissions.", + ], + side_effects = [], + # Declaration-only fields, no definitions: `not_exported` says what is true + # (`nickel export` would otherwise abort with "missing field definition"). + # §5.5 permits the block; this asserts the export path too, which is why + # the control must be exported and not merely typechecked. + variance_schema | not_exported = { + entry_id | String, + reason | String, + }, + }, + + # Top level too: §5.5's clause is "anywhere", and these are the names the + # axel config files and the contractiles actually carry. + execution = { + triggers = ['session_close, 'on_demand], + }, + + failure_mode_defenses = [ + "example entry, present only to occupy an undeclared top-level field", + ], +} diff --git a/1-formats/k9/tools/k9-validate.sh b/1-formats/k9/tools/k9-validate.sh index b5875c683..92b97a32e 100755 --- a/1-formats/k9/tools/k9-validate.sh +++ b/1-formats/k9/tools/k9-validate.sh @@ -310,9 +310,10 @@ check_l0() { # and belongs at L1/L2, not to a dialect mismatch. if [ "$HAS_MAGIC" -eq 1 ]; then DIALECT="component"; else DIALECT="library"; fi elif printf '%s' "$body_start" | grep -qE '^[A-Za-z_][A-Za-z0-9_.-]*:'; then - # A `key:` line: the coordination dialect permits one, and so do the - # unclaimed session-management PROTOCOL.k9 stubs. Only the magic line - # separates the two, so with no magic this file claims nothing. + # A `key:` line: the coordination dialect permits one, and so did the + # unclaimed session-management PROTOCOL.k9 stubs (renamed to PROTOCOL.yaml + # by migration item M2). Only the magic line separates the two, so with no + # magic this file claims nothing. if [ "$HAS_MAGIC" -eq 1 ]; then DIALECT="coordination"; else DIALECT="unclaimed"; fi else DIALECT="unclaimed" @@ -870,7 +871,7 @@ EOF fi done done - t "the contract and all 26 fixtures avoid Nickel's reserved words" 0 "$kw_hits" + t "the contract and all 27 fixtures avoid Nickel's reserved words" 0 "$kw_hits" echo if [ $SELFTEST_FAILS -eq 0 ]; then diff --git a/3-practice/session-management-standards/README.adoc b/3-practice/session-management-standards/README.adoc index 56cab7fdf..f18fc904e 100644 --- a/3-practice/session-management-standards/README.adoc +++ b/3-practice/session-management-standards/README.adoc @@ -97,7 +97,12 @@ Each protocol directory (e.g., `continuity/planned-session-close/`) should contain: 1. CHECKLIST.adoc (e.g., `continuity/planned-session-close/CHECKLIST.adoc`) -2. PROTOCOL.k9 (e.g., `continuity/planned-session-close/PROTOCOL.k9`) +2. PROTOCOL.yaml (e.g., `continuity/planned-session-close/PROTOCOL.yaml`) — the + machine-readable contract, plain YAML. Deliberately *not* `PROTOCOL.k9`: the + `.k9`/`.k9.ncl` suffixes are reserved to K9 contract dialects + (link:../../1-formats/k9/spec/K9-CONTRACT-SPEC.adoc[K9-CONTRACT-SPEC.adoc] §4.2, + 'Extension reservation'), and these files conform to neither K9 SVC nor + `k9-coordination`. 3. STATE-template.a2ml (e.g., `continuity/planned-session-close/STATE-template.a2ml`) 4. EXAMPLE-FILLED-IN.adoc (e.g., `continuity/planned-session-close/EXAMPLE-FILLED-IN.adoc`) 5. optional local helper scripts (e.g., `run.sh`, `verify.sh`, `recover.sh`) diff --git a/3-practice/session-management-standards/continuity/checkpoint-before-major-change/PROTOCOL.k9 b/3-practice/session-management-standards/continuity/checkpoint-before-major-change/PROTOCOL.yaml similarity index 80% rename from 3-practice/session-management-standards/continuity/checkpoint-before-major-change/PROTOCOL.k9 rename to 3-practice/session-management-standards/continuity/checkpoint-before-major-change/PROTOCOL.yaml index 13964f904..d8379a644 100644 --- a/3-practice/session-management-standards/continuity/checkpoint-before-major-change/PROTOCOL.k9 +++ b/3-practice/session-management-standards/continuity/checkpoint-before-major-change/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "continuity" diff --git a/3-practice/session-management-standards/continuity/emergency-termination/PROTOCOL.k9 b/3-practice/session-management-standards/continuity/emergency-termination/PROTOCOL.yaml similarity index 80% rename from 3-practice/session-management-standards/continuity/emergency-termination/PROTOCOL.k9 rename to 3-practice/session-management-standards/continuity/emergency-termination/PROTOCOL.yaml index df69fb209..f9840331f 100644 --- a/3-practice/session-management-standards/continuity/emergency-termination/PROTOCOL.k9 +++ b/3-practice/session-management-standards/continuity/emergency-termination/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "continuity" diff --git a/3-practice/session-management-standards/continuity/planned-session-close/PROTOCOL.k9 b/3-practice/session-management-standards/continuity/planned-session-close/PROTOCOL.yaml similarity index 100% rename from 3-practice/session-management-standards/continuity/planned-session-close/PROTOCOL.k9 rename to 3-practice/session-management-standards/continuity/planned-session-close/PROTOCOL.yaml diff --git a/3-practice/session-management-standards/continuity/recovery-operation/PROTOCOL.k9 b/3-practice/session-management-standards/continuity/recovery-operation/PROTOCOL.yaml similarity index 80% rename from 3-practice/session-management-standards/continuity/recovery-operation/PROTOCOL.k9 rename to 3-practice/session-management-standards/continuity/recovery-operation/PROTOCOL.yaml index 5dc1a8020..e74c07c31 100644 --- a/3-practice/session-management-standards/continuity/recovery-operation/PROTOCOL.k9 +++ b/3-practice/session-management-standards/continuity/recovery-operation/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "continuity" diff --git a/3-practice/session-management-standards/continuity/repo-intake/PROTOCOL.k9 b/3-practice/session-management-standards/continuity/repo-intake/PROTOCOL.yaml similarity index 79% rename from 3-practice/session-management-standards/continuity/repo-intake/PROTOCOL.k9 rename to 3-practice/session-management-standards/continuity/repo-intake/PROTOCOL.yaml index dcb3c5f29..06dd502d3 100644 --- a/3-practice/session-management-standards/continuity/repo-intake/PROTOCOL.k9 +++ b/3-practice/session-management-standards/continuity/repo-intake/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "continuity" diff --git a/3-practice/session-management-standards/handover/collaborative-transfer/PROTOCOL.k9 b/3-practice/session-management-standards/handover/collaborative-transfer/PROTOCOL.yaml similarity index 80% rename from 3-practice/session-management-standards/handover/collaborative-transfer/PROTOCOL.k9 rename to 3-practice/session-management-standards/handover/collaborative-transfer/PROTOCOL.yaml index 8352af26c..9e8fb58d8 100644 --- a/3-practice/session-management-standards/handover/collaborative-transfer/PROTOCOL.k9 +++ b/3-practice/session-management-standards/handover/collaborative-transfer/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "handover" diff --git a/3-practice/session-management-standards/handover/full-transfer/PROTOCOL.k9 b/3-practice/session-management-standards/handover/full-transfer/PROTOCOL.yaml similarity index 79% rename from 3-practice/session-management-standards/handover/full-transfer/PROTOCOL.k9 rename to 3-practice/session-management-standards/handover/full-transfer/PROTOCOL.yaml index 27a1e8a17..a5a45d2c8 100644 --- a/3-practice/session-management-standards/handover/full-transfer/PROTOCOL.k9 +++ b/3-practice/session-management-standards/handover/full-transfer/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "handover" diff --git a/3-practice/session-management-standards/handover/human-transfer/PROTOCOL.k9 b/3-practice/session-management-standards/handover/human-transfer/PROTOCOL.yaml similarity index 79% rename from 3-practice/session-management-standards/handover/human-transfer/PROTOCOL.k9 rename to 3-practice/session-management-standards/handover/human-transfer/PROTOCOL.yaml index 8cf4f0cd5..d396236b9 100644 --- a/3-practice/session-management-standards/handover/human-transfer/PROTOCOL.k9 +++ b/3-practice/session-management-standards/handover/human-transfer/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "handover" diff --git a/3-practice/session-management-standards/handover/model-transfer/PROTOCOL.k9 b/3-practice/session-management-standards/handover/model-transfer/PROTOCOL.yaml similarity index 79% rename from 3-practice/session-management-standards/handover/model-transfer/PROTOCOL.k9 rename to 3-practice/session-management-standards/handover/model-transfer/PROTOCOL.yaml index 5a28d14e4..ebde7e416 100644 --- a/3-practice/session-management-standards/handover/model-transfer/PROTOCOL.k9 +++ b/3-practice/session-management-standards/handover/model-transfer/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "handover" diff --git a/3-practice/session-management-standards/verify/maintenance-sweep/PROTOCOL.k9 b/3-practice/session-management-standards/verify/maintenance-sweep/PROTOCOL.yaml similarity index 80% rename from 3-practice/session-management-standards/verify/maintenance-sweep/PROTOCOL.k9 rename to 3-practice/session-management-standards/verify/maintenance-sweep/PROTOCOL.yaml index 6e4460d66..d8287288c 100644 --- a/3-practice/session-management-standards/verify/maintenance-sweep/PROTOCOL.k9 +++ b/3-practice/session-management-standards/verify/maintenance-sweep/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "verify" diff --git a/3-practice/session-management-standards/verify/release-audit/PROTOCOL.k9 b/3-practice/session-management-standards/verify/release-audit/PROTOCOL.yaml similarity index 79% rename from 3-practice/session-management-standards/verify/release-audit/PROTOCOL.k9 rename to 3-practice/session-management-standards/verify/release-audit/PROTOCOL.yaml index 9eb521fc8..9f292f66f 100644 --- a/3-practice/session-management-standards/verify/release-audit/PROTOCOL.k9 +++ b/3-practice/session-management-standards/verify/release-audit/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "verify" diff --git a/3-practice/session-management-standards/verify/substantial-completion/CHECKLIST.adoc b/3-practice/session-management-standards/verify/substantial-completion/CHECKLIST.adoc index 8c4909c45..bd335e2c6 100644 --- a/3-practice/session-management-standards/verify/substantial-completion/CHECKLIST.adoc +++ b/3-practice/session-management-standards/verify/substantial-completion/CHECKLIST.adoc @@ -224,8 +224,9 @@ with session management and state capture capabilities. == Machine Protocol -See link:PROTOCOL.k9[PROTOCOL.k9] for the K9 coordination format that -implements this checklist in machine-readable form. +See link:PROTOCOL.yaml[PROTOCOL.yaml] for the machine-readable protocol +contract that implements this checklist (plain YAML — the `.k9` suffix is +reserved to K9 contract dialects). == Session State Template diff --git a/3-practice/session-management-standards/verify/substantial-completion/PROTOCOL.k9 b/3-practice/session-management-standards/verify/substantial-completion/PROTOCOL.yaml similarity index 80% rename from 3-practice/session-management-standards/verify/substantial-completion/PROTOCOL.k9 rename to 3-practice/session-management-standards/verify/substantial-completion/PROTOCOL.yaml index c1861fae7..b10a878ab 100644 --- a/3-practice/session-management-standards/verify/substantial-completion/PROTOCOL.k9 +++ b/3-practice/session-management-standards/verify/substantial-completion/PROTOCOL.yaml @@ -1,3 +1,5 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) # STUB: Session-management machine protocol contract protocol: family: "verify"