Skip to content

fix(k9): make L2 real — seven defects the first Nickel runs found (#1058, D173) - #1145

Merged
hyperpolymath merged 5 commits into
mainfrom
arena/01a10407-standards
Oct 4, 2026
Merged

hyperpolymath merged 5 commits into
mainfrom
arena/01a10407-standards

Conversation

@hyperpolymath

@hyperpolymath hyperpolymath commented Oct 4, 2026 •

Copy link
Copy Markdown
Owner

K9-SVC contractile validation is green. Run 37169542241, reconfirmed on 76b841e:

K9 normative contract typecheck  ok
K9 contract self-test            ok   30 assertions
K9 conformance fixtures          ok   5 positive, 21 negative, 0 failures
K9 corpus conformance            ok   5 conforming, 25 grandfathered

This is the first time any tool in this estate has run Nickel over a K9 file. It found seven defects, every one of them in #1143's own code, and not one was visible without a nickel binary — which is not obtainable in the sandbox where this was written.

Defect Effect
Record used as a type Not a Nickel type. The contract did not typecheck, so every L2 verdict was void and all five positive controls failed.
Any used as its replacement Also not a type; the dynamic type is Dyn. Same failure, one run later.
let doc = … in the L2 driver doc is a Nickel keyword (metadata, x | doc "…"). Parse error at the identifier.
default = { … } in two fixtures default is the default-value marker keyword. Nickel's Ident admits only or, as, include.
L2 ran only nickel typecheck Documented as "typechecks the program but does not run it". Contracts apply when a value flows, so the predicate behind schema_version never fired and both L2 negative controls were accepted.
Attribution grepped free text A control's filename contains its rule id, so "rejected by K9-N001" matched the path and succeeded no matter which rule fired — a gate that could not fail, in the suite built to prevent them.
std.string.substring i 1 s 1.18.0's substring is (start, end, str), not a length. It returned "" past index 0, so is_semver_of rejected "1.0.0" and every component control with it.

Four are the same lesson: the contract and driver were written against Nickel as remembered rather than as shipped. Three are gates that reported success without doing the work.

What changed here

  • { _ : Any } → { _ : Dyn }; doc → k9_doc; default → default_recipe.
  • L2 now evaluates (nickel export --format json) as well as typechecks — spec §12.2.
  • Attribution reads structured findings and requires an error whose rule and layer match.
  • A K9 normative contract typecheck step ahead of the fixtures, so a broken contract reports as a broken contract instead of five "non-conforming" files.
  • Failures publish as per-line annotations and a PR comment, because the log blob host is unreachable from here.
  • self-test: 24 → 30 assertions, including a Nickel-keyword guard over the contract and all 26 fixtures (verified in both directions: planting doc = "planted" turns it red).

Ground truth came from the 1.18.0 source tarball over codeload, which is reachable: parser/src/lexer.rs for the keyword list and the Ident production, core/stdlib/std.ncl for all nine std signatures this contract uses.

Still open

The corpus step is pinned to --layer L1 so it can run in a pre-commit hook, so the 25 grandfathered files have never been through Nickel. nickel format --check has never run on a .k9.ncl body anywhere. Both are M7 in spec/MIGRATION-1058.adoc, which now records measured results instead of predictions.

main is red on the K9 gate. #1144 merged at 9c971da, one commit before this
fix, so `{ _ : Any }` is what shipped — and `Any` is not a Nickel type. The
annotations #1144 added name it: `unbound identifier 'Any'` at
k9_contract.ncl:422:20. The dynamic type is `Dyn`. `Record` was wrong before
it; both names were asserted from memory in a sandbox with no nickel binary,
and both voided every L2 verdict downstream, because a contract that does not
typecheck cannot judge anything.

The static audit meant to catch the first one did not: it scanned `| T`
positions only, and `{ _ : Any }` puts its type after a colon. It now also
scans `_ : T` and `Array T`, and its builtin whitelist is narrowed to the four
types this repo's CI-passing .ncl actually uses. It reports exactly one
identifier it cannot evidence from the repo: `Dyn`, lines 428-430.

k9-contractile.yml gains a `K9 normative contract typecheck` step ahead of the
fixtures. `nickel typecheck` stops at the first error, and a broken contract
presents as five non-conforming positive controls rather than one broken
contract — that misdirection cost two runs to see through.

Unverified here: whether `Dyn` typechecks. Nothing else in this repo uses it,
so the workflow run of this commit is the evidence.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
@coderabbitai

coderabbitai Bot commented Oct 4, 2026 •

Copy link
Copy Markdown
Contributor

Warning

Review limit reached

You've used all free OSS reviews for now. Wait for the free limit to reset to keep reviewing this public repository.

Next included review available in 9 minutes.

Check out review usage here.

View limit details

Limit details: You’ve used the included review currently available.

Learn how review limits work.

Review configuration:

⚙️ Run configuration
  • Configuration used: Organization UI
  • Review profile: ASSERTIVE
  • Plan: Advanced
  • Run ID: 441cf21b-2827-4686-9539-2ebec37e6d92
📥 Commits

Reviewing files that changed from the base of the PR and between 2f8c3ee and 76b841e.

📒 Files selected for processing (8)
  • .github/workflows/k9-contractile.yml
  • 1-formats/k9/spec/K9-CONTRACT-SPEC.adoc
  • 1-formats/k9/spec/MIGRATION-1058.adoc
  • 1-formats/k9/spec/contract/k9_contract.ncl
  • 1-formats/k9/tools/README.adoc
  • 1-formats/k9/tools/fixtures/invalid/L1-K9-S011-recipes-at-yard.k9.ncl
  • 1-formats/k9/tools/fixtures/valid/hunt-fully-granted.k9.ncl
  • 1-formats/k9/tools/k9-validate.sh
  • Autopilot · Keep fixing CodeRabbit findings and required CI, and resolving merge conflicts

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

K9 contract conformance

run https://github.com/hyperpolymath/standards/actions/runs/37168907268

K9 normative contract typecheck

k9_contract.ncl typechecks

K9 contract self-test

== the bash mirrors cannot drift from the normative contract ==
ok   leash_levels mirrors k9_contract.ncl
ok   core_capabilities mirrors k9_contract.ncl
ok   contract_version mirrors k9_contract.ncl
ok   schema_major mirrors k9_contract.ncl
== capability arithmetic (§8) ==
ok   capability_ok fs.read accepted
ok   capability_ok rollback.apply accepted
ok   capability_ok x-acme.gpu.alloc accepted
ok   capability_ok x-acme rejected
ok   capability_ok x-.gpu rejected
ok   capability_ok fs.delete rejected
ok   capability_ok  rejected
== the extractor ==
ok   extracts pedigree.security.leash
ok   extracts pedigree.component_type
ok   extracts pedigree.metadata.name
ok   pedigree leash is not reported as top-level leash
ok   required_capabilities for a quiet component
ok   required_capabilities follows allow_network
== the envelope strip keeps line numbers (§3.6) ==
ok   line 1 becomes a comment
ok   line count is preserved
ok   schema_version stays on line 5
== L3: signature presence is not verification (§10) ==
ok   no verifier -> K9-C001 is SKIPPED, never a pass
ok   the skip states presence does not authorise 'Hunt
ok   verifier accepts -> verdict 'Verified, no K9-C001 finding
ok   verifier refuses -> K9-C001 error, verdict 'Rejected
== the fixture runner's attribution cannot be fooled by a filename ==
ok   every extracted finding is well-formed rule+layer
ok   the rule that really fired is attributed
ok   a rule named only in the filename is NOT attributed
ok   K9-C001 is present as a skipped finding
ok   and that same finding is NOT extractable as a rejection

self-test: all assertions passed

K9 conformance fixtures

== positive controls (must pass) ==
ERROR   K9-N001 [L2] 1-formats/k9/tools/fixtures/valid/extension-capability.k9.ncl: component does not satisfy K9.Component: error: unexpected token   ┌─ /home/runner/work/standards/standards/1-formats/k9/tools/fixtures/valid/.k9-validate.7812.24092.driver.ncl:2:5   │ 2 │ let doc = import "./.k9-validate.7812.24092.body.ncl" in   │     ^^^
FAIL extension-capability.k9.ncl should conform (exit 1)
ERROR   K9-N001 [L2] 1-formats/k9/tools/fixtures/valid/hunt-fully-granted.k9.ncl: component does not satisfy K9.Component: error: unexpected token   ┌─ /home/runner/work/standards/standards/1-formats/k9/tools/fixtures/valid/.k9-validate.7812.19979.driver.ncl:2:5   │ 2 │ let doc = import "./.k9-validate.7812.19979.body.ncl" in   │     ^^^
SKIPPED K9-C001 [L3] 1-formats/k9/tools/fixtures/valid/hunt-fully-granted.k9.ncl: signature block present but no verifier ran (set K9_SIG_VERIFIER); verdict is 'Present_Unverified, which does NOT authorise 'Hunt
FAIL hunt-fully-granted.k9.ncl should conform (exit 1)
ERROR   K9-N001 [L2] 1-formats/k9/tools/fixtures/valid/kennel-data.k9.ncl: component does not satisfy K9.Component: error: unexpected token   ┌─ /home/runner/work/standards/standards/1-formats/k9/tools/fixtures/valid/.k9-validate.7812.2928.driver.ncl:2:5   │ 2 │ let doc = import "./.k9-validate.7812.2928.body.ncl" in   │     ^^^
FAIL kennel-data.k9.ncl should conform (exit 1)
ok   library-base.ncl
ERROR   K9-N001 [L2] 1-formats/k9/tools/fixtures/valid/yard-typed-config.k9.ncl: component does not satisfy K9.Component: error: unexpected token   ┌─ /home/runner/work/standards/standards/1-formats/k9/tools/fixtures/valid/.k9-validate.7812.799.driver.ncl:2:5   │ 2 │ let doc = import "./.k9-validate.7812.799.body.ncl" in   │     ^^^
FAIL yard-typed-config.k9.ncl should conform (exit 1)

== negative controls (must fail, by the named rule) ==
ok   L0-K9-E001-bad-magic.k9.ncl (rejected by K9-E001 at L0)
ok   L0-K9-E002-nul-byte.k9.ncl (rejected by K9-E002 at L0)
ok   L0-K9-E003-crlf.k9.ncl (rejected by K9-E003 at L0)
ok   L0-K9-E004-no-spdx.k9.ncl (rejected by K9-E004 at L0)
ok   L0-K9-E005-unclaimed-body.k9.ncl (rejected by K9-E005 at L0)
ok   L0-K9-S012-library-with-pedigree.ncl (rejected by K9-S012 at L0)
ok   L0-K9-S014-stray-leash.ncl (rejected by K9-S014 at L0)
ok   L1-K9-S001-no-pedigree.k9.ncl (rejected by K9-S001 at L1)
ok   L1-K9-S002-wrong-major.k9.ncl (rejected by K9-S002 at L1)
ok   L1-K9-S003-todo-component-type.k9.ncl (rejected by K9-S003 at L1)
ok   L1-K9-S004-unknown-leash.k9.ncl (rejected by K9-S004 at L1)
ok   L1-K9-S005-missing-name.k9.ncl (rejected by K9-S005 at L1)
ok   L1-K9-S006-unknown-capability.k9.ncl (rejected by K9-S006 at L1)
ok   L1-K9-S007-ungranted-flag.k9.ncl (rejected by K9-S007 at L1)
ok   L1-K9-S008-hunt-signature-not-required.k9.ncl (rejected by K9-S008 at L1)
ok   L1-K9-S009-hunt-no-signature-block.k9.ncl (rejected by K9-S009 at L1)
ok   L1-K9-S010-hunt-empty-side-effects.k9.ncl (rejected by K9-S010 at L1)
ok   L1-K9-S011-recipes-at-yard.k9.ncl (rejected by K9-S011 at L1)
ok   L1-K9-S013-dangling-import.k9.ncl (rejected by K9-S013 at L1)
ok   L2-K9-N001-two-segment-version.k9.ncl (rejected by K9-N001 at L2)
ok   L2-K9-N001-wrong-field-type.k9.ncl (rejected by K9-N001 at L2)

fixtures: 5 positive, 21 negative (0 needing nickel), 4 failure(s)

`Dyn` typechecks — the new contract step passed, so the contract itself is
sound. What was left was the L2 driver, and the fixture bodies it feeds.

  unexpected token
    ┌─ .k9-validate.7812.799.driver.ncl:2:5
  2 │ let doc = import "./.k9-validate.7812.799.body.ncl" in
    │     ^^^

Nickel's lexer reserves `doc` — it is the metadata keyword in `x | doc "..."`.
Its `Ident` production admits exactly three contextual keywords, `or`, `as` and
`include`; everything else on the keyword list is unusable as a binding or field
name. `let k9_doc = ...` replaces it.

The same list caught a second violation the CI run had not reached yet: two
fixtures declare a recipe field named `default`, which is the default-value
marker keyword. Renamed to `default_recipe`.

self-test gains a guard over the contract and all 26 fixtures, keyword list
taken from nickel 1.18.0's parser/src/lexer.rs. `| default = x` is Nickel's
marker and is deliberately not a hit, so the contract's 11 defaulted fields
still pass. Verified in both directions: planting `doc = "planted"` in a
positive fixture turns the self-test red (exit 1); removing it returns exit 0.
30 assertions.

Ground truth for all of this came from the 1.18.0 source tarball via codeload,
which is reachable; the release binary is not, so these were untestable locally.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

K9 contract conformance

run https://github.com/hyperpolymath/standards/actions/runs/37169159595

K9 normative contract typecheck

k9_contract.ncl typechecks

K9 contract self-test

== the bash mirrors cannot drift from the normative contract ==
ok   leash_levels mirrors k9_contract.ncl
ok   core_capabilities mirrors k9_contract.ncl
ok   contract_version mirrors k9_contract.ncl
ok   schema_major mirrors k9_contract.ncl
== capability arithmetic (§8) ==
ok   capability_ok fs.read accepted
ok   capability_ok rollback.apply accepted
ok   capability_ok x-acme.gpu.alloc accepted
ok   capability_ok x-acme rejected
ok   capability_ok x-.gpu rejected
ok   capability_ok fs.delete rejected
ok   capability_ok  rejected
== the extractor ==
ok   extracts pedigree.security.leash
ok   extracts pedigree.component_type
ok   extracts pedigree.metadata.name
ok   pedigree leash is not reported as top-level leash
ok   required_capabilities for a quiet component
ok   required_capabilities follows allow_network
== the envelope strip keeps line numbers (§3.6) ==
ok   line 1 becomes a comment
ok   line count is preserved
ok   schema_version stays on line 5
== L3: signature presence is not verification (§10) ==
ok   no verifier -> K9-C001 is SKIPPED, never a pass
ok   the skip states presence does not authorise 'Hunt
ok   verifier accepts -> verdict 'Verified, no K9-C001 finding
ok   verifier refuses -> K9-C001 error, verdict 'Rejected
== the fixture runner's attribution cannot be fooled by a filename ==
ok   every extracted finding is well-formed rule+layer
ok   the rule that really fired is attributed
ok   a rule named only in the filename is NOT attributed
ok   K9-C001 is present as a skipped finding
ok   and that same finding is NOT extractable as a rejection
== no Nickel reserved word is used as an identifier ==
ok   the contract and all 26 fixtures avoid Nickel's reserved words

self-test: all assertions passed

K9 conformance fixtures

== positive controls (must pass) ==
ok   extension-capability.k9.ncl
ok   hunt-fully-granted.k9.ncl
ok   kennel-data.k9.ncl
ok   library-base.ncl
ok   yard-typed-config.k9.ncl

== negative controls (must fail, by the named rule) ==
ok   L0-K9-E001-bad-magic.k9.ncl (rejected by K9-E001 at L0)
ok   L0-K9-E002-nul-byte.k9.ncl (rejected by K9-E002 at L0)
ok   L0-K9-E003-crlf.k9.ncl (rejected by K9-E003 at L0)
ok   L0-K9-E004-no-spdx.k9.ncl (rejected by K9-E004 at L0)
ok   L0-K9-E005-unclaimed-body.k9.ncl (rejected by K9-E005 at L0)
ok   L0-K9-S012-library-with-pedigree.ncl (rejected by K9-S012 at L0)
ok   L0-K9-S014-stray-leash.ncl (rejected by K9-S014 at L0)
ok   L1-K9-S001-no-pedigree.k9.ncl (rejected by K9-S001 at L1)
ok   L1-K9-S002-wrong-major.k9.ncl (rejected by K9-S002 at L1)
ok   L1-K9-S003-todo-component-type.k9.ncl (rejected by K9-S003 at L1)
ok   L1-K9-S004-unknown-leash.k9.ncl (rejected by K9-S004 at L1)
ok   L1-K9-S005-missing-name.k9.ncl (rejected by K9-S005 at L1)
ok   L1-K9-S006-unknown-capability.k9.ncl (rejected by K9-S006 at L1)
ok   L1-K9-S007-ungranted-flag.k9.ncl (rejected by K9-S007 at L1)
ok   L1-K9-S008-hunt-signature-not-required.k9.ncl (rejected by K9-S008 at L1)
ok   L1-K9-S009-hunt-no-signature-block.k9.ncl (rejected by K9-S009 at L1)
ok   L1-K9-S010-hunt-empty-side-effects.k9.ncl (rejected by K9-S010 at L1)
ok   L1-K9-S011-recipes-at-yard.k9.ncl (rejected by K9-S011 at L1)
ok   L1-K9-S013-dangling-import.k9.ncl (rejected by K9-S013 at L1)
FAIL L2-K9-N001-two-segment-version.k9.ncl was ACCEPTED — the gate did not fire
FAIL L2-K9-N001-wrong-field-type.k9.ncl was ACCEPTED — the gate did not fire

fixtures: 5 positive, 21 negative (0 needing nickel), 2 failure(s)

All five positive controls pass now. The two L2 negative controls were
ACCEPTED, which is the finding that matters:

  FAIL L2-K9-N001-wrong-field-type.k9.ncl was ACCEPTED — the gate did not fire
  FAIL L2-K9-N001-two-segment-version.k9.ncl was ACCEPTED — the gate did not fire

`nickel typecheck` is described by Nickel's own CLI as "typechecks the program
but does not run it". A Nickel contract (`|`) is applied when a value flows
through it, and a predicate contract has no static type to reason about — so
`schema_version | std.contract.from_predicate (is_semver_of schema_major)`
never fired, and `schema_version = "1.0"` sailed through. Same for
`allow_network = "yes"`.

check_l2 now runs `nickel export --format json` on the driver as well, since
evaluation is what applies the contracts. Those two fixtures are the pair
written specifically to prove L2 sees what L1 cannot; a validator that
typechecked only was reporting the authority of a layer it was not performing
— the exact defect class §12.3 is about, in this PR's own code.

Libraries stay typecheck-only: they are imported rather than evaluated as
components and may legitimately hold functions, which do not serialise.

Spec gains §12.2 "L2 is two Nickel invocations, not one" and the K9-N001 row
now names both. Cross-references re-verified by simulation: 87 numbered
sections, 29 distinct §refs, none unresolved.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

K9 contract conformance

run https://github.com/hyperpolymath/standards/actions/runs/37169337108

K9 normative contract typecheck

k9_contract.ncl typechecks

K9 contract self-test

== the bash mirrors cannot drift from the normative contract ==
ok   leash_levels mirrors k9_contract.ncl
ok   core_capabilities mirrors k9_contract.ncl
ok   contract_version mirrors k9_contract.ncl
ok   schema_major mirrors k9_contract.ncl
== capability arithmetic (§8) ==
ok   capability_ok fs.read accepted
ok   capability_ok rollback.apply accepted
ok   capability_ok x-acme.gpu.alloc accepted
ok   capability_ok x-acme rejected
ok   capability_ok x-.gpu rejected
ok   capability_ok fs.delete rejected
ok   capability_ok  rejected
== the extractor ==
ok   extracts pedigree.security.leash
ok   extracts pedigree.component_type
ok   extracts pedigree.metadata.name
ok   pedigree leash is not reported as top-level leash
ok   required_capabilities for a quiet component
ok   required_capabilities follows allow_network
== the envelope strip keeps line numbers (§3.6) ==
ok   line 1 becomes a comment
ok   line count is preserved
ok   schema_version stays on line 5
== L3: signature presence is not verification (§10) ==
ok   no verifier -> K9-C001 is SKIPPED, never a pass
ok   the skip states presence does not authorise 'Hunt
ok   verifier accepts -> verdict 'Verified, no K9-C001 finding
ok   verifier refuses -> K9-C001 error, verdict 'Rejected
== the fixture runner's attribution cannot be fooled by a filename ==
ok   every extracted finding is well-formed rule+layer
ok   the rule that really fired is attributed
ok   a rule named only in the filename is NOT attributed
ok   K9-C001 is present as a skipped finding
ok   and that same finding is NOT extractable as a rejection
== no Nickel reserved word is used as an identifier ==
ok   the contract and all 26 fixtures avoid Nickel's reserved words

self-test: all assertions passed

K9 conformance fixtures

== positive controls (must pass) ==
ERROR   K9-N001 [L2] 1-formats/k9/tools/fixtures/valid/extension-capability.k9.ncl: component violates the K9.Component contract: error: contract broken by the value of `schema_version`     ┌─ /home/runner/work/standards/standards/1-formats/k9/spec/contract/k9_contract.ncl:394:22     │ 394 │     schema_version | std.contract.from_predicate (is_semver_of schema_major),     │                      ------------------------------------------------------- expected type 
FAIL extension-capability.k9.ncl should conform (exit 1)
ERROR   K9-N001 [L2] 1-formats/k9/tools/fixtures/valid/hunt-fully-granted.k9.ncl: component violates the K9.Component contract: error: contract broken by the value of `schema_version`     ┌─ /home/runner/work/standards/standards/1-formats/k9/spec/contract/k9_contract.ncl:394:22     │ 394 │     schema_version | std.contract.from_predicate (is_semver_of schema_major),     │                      ------------------------------------------------------- expected type 
SKIPPED K9-C001 [L3] 1-formats/k9/tools/fixtures/valid/hunt-fully-granted.k9.ncl: signature block present but no verifier ran (set K9_SIG_VERIFIER); verdict is 'Present_Unverified, which does NOT authorise 'Hunt
FAIL hunt-fully-granted.k9.ncl should conform (exit 1)
ERROR   K9-N001 [L2] 1-formats/k9/tools/fixtures/valid/kennel-data.k9.ncl: component violates the K9.Component contract: error: contract broken by the value of `schema_version`     ┌─ /home/runner/work/standards/standards/1-formats/k9/spec/contract/k9_contract.ncl:394:22     │ 394 │     schema_version | std.contract.from_predicate (is_semver_of schema_major),     │                      ------------------------------------------------------- expected type 
FAIL kennel-data.k9.ncl should conform (exit 1)
ok   library-base.ncl
ERROR   K9-N001 [L2] 1-formats/k9/tools/fixtures/valid/yard-typed-config.k9.ncl: component violates the K9.Component contract: error: contract broken by the value of `schema_version`     ┌─ /home/runner/work/standards/standards/1-formats/k9/spec/contract/k9_contract.ncl:394:22     │ 394 │     schema_version | std.contract.from_predicate (is_semver_of schema_major),     │                      ------------------------------------------------------- expected type 
FAIL yard-typed-config.k9.ncl should conform (exit 1)

== negative controls (must fail, by the named rule) ==
ok   L0-K9-E001-bad-magic.k9.ncl (rejected by K9-E001 at L0)
ok   L0-K9-E002-nul-byte.k9.ncl (rejected by K9-E002 at L0)
ok   L0-K9-E003-crlf.k9.ncl (rejected by K9-E003 at L0)
ok   L0-K9-E004-no-spdx.k9.ncl (rejected by K9-E004 at L0)
ok   L0-K9-E005-unclaimed-body.k9.ncl (rejected by K9-E005 at L0)
ok   L0-K9-S012-library-with-pedigree.ncl (rejected by K9-S012 at L0)
ok   L0-K9-S014-stray-leash.ncl (rejected by K9-S014 at L0)
ok   L1-K9-S001-no-pedigree.k9.ncl (rejected by K9-S001 at L1)
ok   L1-K9-S002-wrong-major.k9.ncl (rejected by K9-S002 at L1)
ok   L1-K9-S003-todo-component-type.k9.ncl (rejected by K9-S003 at L1)
ok   L1-K9-S004-unknown-leash.k9.ncl (rejected by K9-S004 at L1)
ok   L1-K9-S005-missing-name.k9.ncl (rejected by K9-S005 at L1)
ok   L1-K9-S006-unknown-capability.k9.ncl (rejected by K9-S006 at L1)
ok   L1-K9-S007-ungranted-flag.k9.ncl (rejected by K9-S007 at L1)
ok   L1-K9-S008-hunt-signature-not-required.k9.ncl (rejected by K9-S008 at L1)
ok   L1-K9-S009-hunt-no-signature-block.k9.ncl (rejected by K9-S009 at L1)
ok   L1-K9-S010-hunt-empty-side-effects.k9.ncl (rejected by K9-S010 at L1)
ok   L1-K9-S011-recipes-at-yard.k9.ncl (rejected by K9-S011 at L1)
ok   L1-K9-S013-dangling-import.k9.ncl (rejected by K9-S013 at L1)
ok   L2-K9-N001-two-segment-version.k9.ncl (rejected by K9-N001 at L2)
ok   L2-K9-N001-wrong-field-type.k9.ncl (rejected by K9-N001 at L2)

fixtures: 5 positive, 21 negative (0 needing nickel), 4 failure(s)

With L2 evaluating, the contracts fire — and `is_semver_of` rejected "1.0.0",
failing all four component positive controls:

  contract broken by the value of `schema_version`
    ┌─ k9_contract.ncl:394:22

Nickel 1.18.0's core/stdlib/std.ncl documents
`substring start end str` — the slice from `start` (included) to `end`
(excluded). The character walk called `substring i 1 s`, which returns "1" at
i=0 by coincidence and "" at every later index, so the walk fell out at the
second character and no version string could pass.

Now `substring i (i + 1) s`. The other two calls are `substring 0 (length x) v`
prefix slices, which the end-index reading makes correct as written.

All nine std functions this contract uses were checked against 1.18.0's
std.ncl, and all 23 call sites against those signatures:

  substring : Number -> Number -> String -> String
  split : String -> String -> Array String
  join : String -> Array String -> String
  string.length : String -> Number
  array.{length,any,filter}, record.has_field, contract.from_predicate

The stdlib was read from the 1.18.0 source tarball over codeload, which is
reachable; the release binary is not, so none of this was testable locally.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

K9 contract conformance

run https://github.com/hyperpolymath/standards/actions/runs/37169542241

K9 normative contract typecheck

k9_contract.ncl typechecks

K9 contract self-test

== the bash mirrors cannot drift from the normative contract ==
ok   leash_levels mirrors k9_contract.ncl
ok   core_capabilities mirrors k9_contract.ncl
ok   contract_version mirrors k9_contract.ncl
ok   schema_major mirrors k9_contract.ncl
== capability arithmetic (§8) ==
ok   capability_ok fs.read accepted
ok   capability_ok rollback.apply accepted
ok   capability_ok x-acme.gpu.alloc accepted
ok   capability_ok x-acme rejected
ok   capability_ok x-.gpu rejected
ok   capability_ok fs.delete rejected
ok   capability_ok  rejected
== the extractor ==
ok   extracts pedigree.security.leash
ok   extracts pedigree.component_type
ok   extracts pedigree.metadata.name
ok   pedigree leash is not reported as top-level leash
ok   required_capabilities for a quiet component
ok   required_capabilities follows allow_network
== the envelope strip keeps line numbers (§3.6) ==
ok   line 1 becomes a comment
ok   line count is preserved
ok   schema_version stays on line 5
== L3: signature presence is not verification (§10) ==
ok   no verifier -> K9-C001 is SKIPPED, never a pass
ok   the skip states presence does not authorise 'Hunt
ok   verifier accepts -> verdict 'Verified, no K9-C001 finding
ok   verifier refuses -> K9-C001 error, verdict 'Rejected
== the fixture runner's attribution cannot be fooled by a filename ==
ok   every extracted finding is well-formed rule+layer
ok   the rule that really fired is attributed
ok   a rule named only in the filename is NOT attributed
ok   K9-C001 is present as a skipped finding
ok   and that same finding is NOT extractable as a rejection
== no Nickel reserved word is used as an identifier ==
ok   the contract and all 26 fixtures avoid Nickel's reserved words

self-test: all assertions passed

K9 conformance fixtures

== positive controls (must pass) ==
ok   extension-capability.k9.ncl
ok   hunt-fully-granted.k9.ncl
ok   kennel-data.k9.ncl
ok   library-base.ncl
ok   yard-typed-config.k9.ncl

== negative controls (must fail, by the named rule) ==
ok   L0-K9-E001-bad-magic.k9.ncl (rejected by K9-E001 at L0)
ok   L0-K9-E002-nul-byte.k9.ncl (rejected by K9-E002 at L0)
ok   L0-K9-E003-crlf.k9.ncl (rejected by K9-E003 at L0)
ok   L0-K9-E004-no-spdx.k9.ncl (rejected by K9-E004 at L0)
ok   L0-K9-E005-unclaimed-body.k9.ncl (rejected by K9-E005 at L0)
ok   L0-K9-S012-library-with-pedigree.ncl (rejected by K9-S012 at L0)
ok   L0-K9-S014-stray-leash.ncl (rejected by K9-S014 at L0)
ok   L1-K9-S001-no-pedigree.k9.ncl (rejected by K9-S001 at L1)
ok   L1-K9-S002-wrong-major.k9.ncl (rejected by K9-S002 at L1)
ok   L1-K9-S003-todo-component-type.k9.ncl (rejected by K9-S003 at L1)
ok   L1-K9-S004-unknown-leash.k9.ncl (rejected by K9-S004 at L1)
ok   L1-K9-S005-missing-name.k9.ncl (rejected by K9-S005 at L1)
ok   L1-K9-S006-unknown-capability.k9.ncl (rejected by K9-S006 at L1)
ok   L1-K9-S007-ungranted-flag.k9.ncl (rejected by K9-S007 at L1)
ok   L1-K9-S008-hunt-signature-not-required.k9.ncl (rejected by K9-S008 at L1)
ok   L1-K9-S009-hunt-no-signature-block.k9.ncl (rejected by K9-S009 at L1)
ok   L1-K9-S010-hunt-empty-side-effects.k9.ncl (rejected by K9-S010 at L1)
ok   L1-K9-S011-recipes-at-yard.k9.ncl (rejected by K9-S011 at L1)
ok   L1-K9-S013-dangling-import.k9.ncl (rejected by K9-S013 at L1)
ok   L2-K9-N001-two-segment-version.k9.ncl (rejected by K9-N001 at L2)
ok   L2-K9-N001-wrong-field-type.k9.ncl (rejected by K9-N001 at L2)

fixtures: 5 positive, 21 negative (0 needing nickel), 0 failure(s)

K9 corpus conformance

[validate-k9] debt .machine_readable/contractiles/adjust/adjust.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/contractiles/bust/bust.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/contractiles/dust/dust.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/contractiles/intend/intend.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/contractiles/must/must.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/contractiles/trust/trust.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/svc/k9/examples/setup-repo.k9.ncl (fail) — K9-S007 K9-S009 K9-S010 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/svc/k9/template-hunt.k9.ncl (fail) — K9-S003 K9-S005 K9-S007 K9-S009 K9-S010 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/svc/k9/template-kennel.k9.ncl (fail) — K9-S003 K9-S005 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/svc/k9/template-yard.k9.ncl (fail) — K9-S003 K9-S005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 2-protocols/axel/config/ci.k9.ncl (fail) — K9-S014 (grandfathered; touching it makes it blocking)
[validate-k9] debt 2-protocols/axel/config/metadata.k9.ncl (fail) — K9-S014 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/continuity/checkpoint-before-major-change/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/continuity/emergency-termination/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/continuity/planned-session-close/PROTOCOL.k9 (error) — K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/continuity/recovery-operation/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/continuity/repo-intake/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/handover/collaborative-transfer/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/handover/full-transfer/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/handover/human-transfer/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/handover/model-transfer/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/verify/maintenance-sweep/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/verify/release-audit/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/verify/substantial-completion/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt rhodium-standard-repositories/rsr-compliance-checklist.k9.ncl (fail) — K9-S004 K9-S005 K9-S014 (grandfathered; touching it makes it blocking)
[validate-k9] 5 conforming, 25 grandfathered (layer L1, contract v1.0.0)
[validate-k9] lexical layers only — Nickel semantics (L2) and signature verification (L3) were not checked here

M7 said L2 had never run. It has now, in CI, and the result is recorded rather
than left as a prediction: contract typecheck ok, 30-assertion self-test ok,
fixtures 5 positive / 21 negative / 0 failures, corpus 5 conforming and 25
grandfathered.

The corpus step is pinned to --layer L1 so it can run in a pre-commit hook, so
the 25 grandfathered files have still never been through Nickel and M5's
parse-error prediction is still a prediction. M7 is restated as that remaining
work rather than as an unknown.

A table records the seven defects the first Nickel runs found, all of them in
this work's own code. Four are the same lesson — the contract and its driver
were written against Nickel as remembered rather than Nickel as shipped. Three
are gates that reported success without doing the work.

The README's L2-controls section now says what they caught: L2 typechecked
only, so both were accepted, and they are why L2 evaluates as well.

Co-authored-by: arena-agent <297053741+arena-agent@users.noreply.github.com>
@github-actions

github-actions Bot commented Oct 4, 2026

Copy link
Copy Markdown
Contributor

K9 contract conformance

run https://github.com/hyperpolymath/standards/actions/runs/37169726396

K9 normative contract typecheck

k9_contract.ncl typechecks

K9 contract self-test

== the bash mirrors cannot drift from the normative contract ==
ok   leash_levels mirrors k9_contract.ncl
ok   core_capabilities mirrors k9_contract.ncl
ok   contract_version mirrors k9_contract.ncl
ok   schema_major mirrors k9_contract.ncl
== capability arithmetic (§8) ==
ok   capability_ok fs.read accepted
ok   capability_ok rollback.apply accepted
ok   capability_ok x-acme.gpu.alloc accepted
ok   capability_ok x-acme rejected
ok   capability_ok x-.gpu rejected
ok   capability_ok fs.delete rejected
ok   capability_ok  rejected
== the extractor ==
ok   extracts pedigree.security.leash
ok   extracts pedigree.component_type
ok   extracts pedigree.metadata.name
ok   pedigree leash is not reported as top-level leash
ok   required_capabilities for a quiet component
ok   required_capabilities follows allow_network
== the envelope strip keeps line numbers (§3.6) ==
ok   line 1 becomes a comment
ok   line count is preserved
ok   schema_version stays on line 5
== L3: signature presence is not verification (§10) ==
ok   no verifier -> K9-C001 is SKIPPED, never a pass
ok   the skip states presence does not authorise 'Hunt
ok   verifier accepts -> verdict 'Verified, no K9-C001 finding
ok   verifier refuses -> K9-C001 error, verdict 'Rejected
== the fixture runner's attribution cannot be fooled by a filename ==
ok   every extracted finding is well-formed rule+layer
ok   the rule that really fired is attributed
ok   a rule named only in the filename is NOT attributed
ok   K9-C001 is present as a skipped finding
ok   and that same finding is NOT extractable as a rejection
== no Nickel reserved word is used as an identifier ==
ok   the contract and all 26 fixtures avoid Nickel's reserved words

self-test: all assertions passed

K9 conformance fixtures

== positive controls (must pass) ==
ok   extension-capability.k9.ncl
ok   hunt-fully-granted.k9.ncl
ok   kennel-data.k9.ncl
ok   library-base.ncl
ok   yard-typed-config.k9.ncl

== negative controls (must fail, by the named rule) ==
ok   L0-K9-E001-bad-magic.k9.ncl (rejected by K9-E001 at L0)
ok   L0-K9-E002-nul-byte.k9.ncl (rejected by K9-E002 at L0)
ok   L0-K9-E003-crlf.k9.ncl (rejected by K9-E003 at L0)
ok   L0-K9-E004-no-spdx.k9.ncl (rejected by K9-E004 at L0)
ok   L0-K9-E005-unclaimed-body.k9.ncl (rejected by K9-E005 at L0)
ok   L0-K9-S012-library-with-pedigree.ncl (rejected by K9-S012 at L0)
ok   L0-K9-S014-stray-leash.ncl (rejected by K9-S014 at L0)
ok   L1-K9-S001-no-pedigree.k9.ncl (rejected by K9-S001 at L1)
ok   L1-K9-S002-wrong-major.k9.ncl (rejected by K9-S002 at L1)
ok   L1-K9-S003-todo-component-type.k9.ncl (rejected by K9-S003 at L1)
ok   L1-K9-S004-unknown-leash.k9.ncl (rejected by K9-S004 at L1)
ok   L1-K9-S005-missing-name.k9.ncl (rejected by K9-S005 at L1)
ok   L1-K9-S006-unknown-capability.k9.ncl (rejected by K9-S006 at L1)
ok   L1-K9-S007-ungranted-flag.k9.ncl (rejected by K9-S007 at L1)
ok   L1-K9-S008-hunt-signature-not-required.k9.ncl (rejected by K9-S008 at L1)
ok   L1-K9-S009-hunt-no-signature-block.k9.ncl (rejected by K9-S009 at L1)
ok   L1-K9-S010-hunt-empty-side-effects.k9.ncl (rejected by K9-S010 at L1)
ok   L1-K9-S011-recipes-at-yard.k9.ncl (rejected by K9-S011 at L1)
ok   L1-K9-S013-dangling-import.k9.ncl (rejected by K9-S013 at L1)
ok   L2-K9-N001-two-segment-version.k9.ncl (rejected by K9-N001 at L2)
ok   L2-K9-N001-wrong-field-type.k9.ncl (rejected by K9-N001 at L2)

fixtures: 5 positive, 21 negative (0 needing nickel), 0 failure(s)

K9 corpus conformance

[validate-k9] debt .machine_readable/contractiles/adjust/adjust.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/contractiles/bust/bust.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/contractiles/dust/dust.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/contractiles/intend/intend.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/contractiles/must/must.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/contractiles/trust/trust.k9.ncl (fail) — K9-S012 K9-S013 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/svc/k9/examples/setup-repo.k9.ncl (fail) — K9-S007 K9-S009 K9-S010 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/svc/k9/template-hunt.k9.ncl (fail) — K9-S003 K9-S005 K9-S007 K9-S009 K9-S010 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/svc/k9/template-kennel.k9.ncl (fail) — K9-S003 K9-S005 (grandfathered; touching it makes it blocking)
[validate-k9] debt .machine_readable/svc/k9/template-yard.k9.ncl (fail) — K9-S003 K9-S005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 2-protocols/axel/config/ci.k9.ncl (fail) — K9-S014 (grandfathered; touching it makes it blocking)
[validate-k9] debt 2-protocols/axel/config/metadata.k9.ncl (fail) — K9-S014 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/continuity/checkpoint-before-major-change/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/continuity/emergency-termination/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/continuity/planned-session-close/PROTOCOL.k9 (error) — K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/continuity/recovery-operation/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/continuity/repo-intake/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/handover/collaborative-transfer/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/handover/full-transfer/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/handover/human-transfer/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/handover/model-transfer/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/verify/maintenance-sweep/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/verify/release-audit/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt 3-practice/session-management-standards/verify/substantial-completion/PROTOCOL.k9 (error) — K9-E004 K9-E005 (grandfathered; touching it makes it blocking)
[validate-k9] debt rhodium-standard-repositories/rsr-compliance-checklist.k9.ncl (fail) — K9-S004 K9-S005 K9-S014 (grandfathered; touching it makes it blocking)
[validate-k9] 5 conforming, 25 grandfathered (layer L1, contract v1.0.0)
[validate-k9] lexical layers only — Nickel semantics (L2) and signature verification (L3) were not checked here

@sonarqubecloud

sonarqubecloud Bot commented Oct 4, 2026

Copy link
Copy Markdown

@hyperpolymath hyperpolymath changed the title fix(k9): Dyn, not Any — the contract on main does not typecheck fix(k9): make L2 real — seven defects the first Nickel runs found (#1058, D173) Oct 4, 2026
@hyperpolymath
hyperpolymath merged commit 93f0c3f into main Oct 4, 2026
44 of 50 checks passed
@hyperpolymath
hyperpolymath deleted the arena/01a10407-standards branch October 4, 2026 02:28
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants