Skip to content

Commit d2300b8

Browse files
committed
Fix Dogfood Gate: make minimal K9 fixture a valid contract
The conformance fixture was a YAML-like sketch (from the k9-rs README) that lacked the required `K9!` magic line and a `pedigree = { }` block, so the repo's `Validate K9 contracts` gate (k9-validate-action) errored. Rewrite it as a valid Nickel K9 contract modelled on the live corpus: `K9!` magic, SPDX header, five-layer pedigree (Snout/Scent/Leash/Gut/ Muscle) with a `'Kennel` trust level, plus `schema_version` and `security_level` to clear the validator's warnings. Rename to component.k9.ncl (it is Nickel), realign expected.json, and rewrite spec/SPEC.adoc to document the actually-enforced format rather than the earlier guess. The PR's other red checks (workflow security linter, trufflehog, deno @cadre/router duplicate) are pre-existing on main and unrelated to this diff. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_019i2e5ABGBKQmMdqk8puFWx
1 parent f911392 commit d2300b8

6 files changed

Lines changed: 124 additions & 112 deletions

File tree

k9-ecosystem/.machine_readable/6a2/STATE.a2ml

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,9 +14,12 @@ completion-percentage = 20
1414
phase = "hub scaffolded and staged inside developer-ecosystem; awaiting extraction to standalone repo and live submodule wiring"
1515

1616
[session-2026-06-20-hub-scaffold]
17-
summary = "Created the k9-ecosystem aggregator hub: README, .gitmodules (11 members grouped implementations/tooling/ci/examples), Justfile + scripts/init-submodules.sh, spec/SPEC.adoc (v0.1 draft) + spec/README, conformance/ suite with the 'minimal' fixture (component.k9 + expected.json), members/README, LICENSE, BOOTSTRAP.md, and 6a2 governance. Staged at developer-ecosystem/k9-ecosystem/ because the session scope did not permit creating a new top-level repo."
17+
summary = "Created the k9-ecosystem aggregator hub: README, .gitmodules (11 members grouped implementations/tooling/ci/examples), Justfile + scripts/init-submodules.sh, spec/SPEC.adoc (v0.1 draft) + spec/README, conformance/ suite with the 'minimal' fixture (component.k9.ncl + expected.json), members/README, LICENSE, BOOTSTRAP.md, and 6a2 governance. Staged at developer-ecosystem/k9-ecosystem/ because the session scope did not permit creating a new top-level repo."
1818
files-added = 16
1919

20+
[session-2026-06-20-dogfood-fix]
21+
summary = "Fixed the Dogfood Gate 'Validate K9 contracts' failure on PR #120. The minimal fixture was a YAML-like sketch from the k9-rs README and lacked the K9! magic line and a 'pedigree = { }' block, so k9-validate-action errored. Rewrote it as a valid Nickel K9 contract (component.k9.ncl) modelled on the live corpus (K9! magic, SPDX, five-layer pedigree, 'Kennel leash), realigned expected.json and rewrote spec/SPEC.adoc to the actually-enforced format. The other three red checks on the PR (workflow security linter, trufflehog, deno @cadre/router duplicate) are pre-existing on main and untouched by this diff."
22+
2023
[next-actions]
2124
extract = "Promote to standalone hyperpolymath/k9-ecosystem (BOOTSTRAP.md option A)"
2225
wire = "Run scripts/init-submodules.sh to populate the 11 member submodules"

k9-ecosystem/conformance/README.adoc

Lines changed: 7 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,16 +12,17 @@ contract that keeps five independent parsers (`k9-rs`, `k9_ex`, `k9_gleam`,
1212
conformance/
1313
└── fixtures/
1414
└── <case>/
15-
├── component.k9 # input (also <case>/component.k9.ncl where relevant)
16-
└── expected.json # canonical projection (see ../spec/SPEC.adoc §7)
15+
├── component.k9.ncl # input (Nickel surface; or component.k9 for the plain surface)
16+
└── expected.json # canonical projection (see ../spec/SPEC.adoc §6)
1717
----
1818

1919
== Contract
2020

21-
For each fixture, an implementation parses `component.k9` and emits the canonical
22-
JSON projection. The result MUST be *structurally equal* to `expected.json`
23-
(key order per declaration order, `security` lower-cased). Whitespace and
24-
insignificant formatting are ignored.
21+
For each fixture, an implementation produces the canonical JSON projection from
22+
the input — evaluating the Nickel surface (`.k9.ncl`) or parsing the plain
23+
surface (`.k9`) — and the result MUST be *structurally equal* to `expected.json`
24+
(records in declaration order; Nickel enum tags rendered as bare strings, e.g.
25+
`'Kennel` -> `"Kennel"`). Whitespace and insignificant formatting are ignored.
2526

2627
== Wiring an implementation
2728

k9-ecosystem/conformance/fixtures/minimal/component.k9

Lines changed: 0 additions & 14 deletions
This file was deleted.
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
K9!
2+
# SPDX-License-Identifier: MPL-2.0
3+
# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) <j.d.a.jewell@open.ac.uk>
4+
# minimal — the smallest valid K9 contract. Conformance fixture: every
5+
# implementation must evaluate this and project it to expected.json.
6+
7+
{
8+
pedigree = {
9+
schema_version = "1.0.0",
10+
security_level = 'Kennel,
11+
12+
# L1 — The Snout: identity
13+
metadata = {
14+
name = "hello-svc",
15+
version = "0.1.0",
16+
breed = "application/vnd.k9+nickel",
17+
magic_number = "K9!",
18+
license = "MPL-2.0",
19+
},
20+
21+
# L3 — The Leash: security posture
22+
security = {
23+
trust_level = 'Kennel,
24+
allow_network = false,
25+
allow_subprocess = false,
26+
},
27+
28+
# L5 — The Muscle: recipes
29+
recipes = {
30+
build = "cargo build --release",
31+
},
32+
},
33+
}
Lines changed: 17 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,21 @@
11
{
2-
"component": "hello-svc",
3-
"version": "0.1.0",
42
"pedigree": {
5-
"origin": "https://github.com/hyperpolymath/hello-svc",
6-
"author": "Jonathan D.A. Jewell",
7-
"license": "MPL-2.0"
8-
},
9-
"security": "kennel",
10-
"recipe": {
11-
"tool": "cargo",
12-
"command": "build --release"
13-
},
14-
"contracts": [
15-
{
16-
"name": "binary-exists",
17-
"check": "test -x target/release/hello-svc",
18-
"severity": "error"
3+
"schema_version": "1.0.0",
4+
"security_level": "Kennel",
5+
"metadata": {
6+
"name": "hello-svc",
7+
"version": "0.1.0",
8+
"breed": "application/vnd.k9+nickel",
9+
"magic_number": "K9!",
10+
"license": "MPL-2.0"
11+
},
12+
"security": {
13+
"trust_level": "Kennel",
14+
"allow_network": false,
15+
"allow_subprocess": false
16+
},
17+
"recipes": {
18+
"build": "cargo build --release"
1919
}
20-
]
20+
}
2121
}

k9-ecosystem/spec/SPEC.adoc

Lines changed: 63 additions & 74 deletions
Original file line numberDiff line numberDiff line change
@@ -8,113 +8,102 @@
88

99
[IMPORTANT]
1010
====
11-
*Status: DRAFT (v0.1).* This document is the working canonical specification for
12-
the K9 format. Until it is ratified, `k9-rs` is the *normative reference
13-
implementation*: where this text and `k9-rs` disagree, `k9-rs` wins and this
14-
text is the bug. Open questions are marked `[OPEN]`.
11+
*Status: DRAFT (v0.1).* This text is reconstructed from the living K9 corpus
12+
(the `*.k9.ncl` deployment components across the estate), the
13+
`hyperpolymath/k9-validate-action` validator, and `k9-rs`. Where they disagree,
14+
the *validator + `k9-rs` are normative* and this text is the bug. Open questions
15+
are marked `[OPEN]`.
1516
====
1617

1718
== 1. Overview
1819

1920
K9 is a self-validating configuration format for *software component
20-
declarations*. A K9 document describes one component and the checks that prove
21-
it is what it claims to be.
21+
declarations*. A K9 document describes one component — its identity, target,
22+
security posture, self-checks, and recipes — so the file is both documentation
23+
and an executable, authorisable contract.
2224

23-
Two surface syntaxes are defined:
25+
Two surfaces are defined:
2426

25-
* *`.k9`* — a YAML-like, indentation-structured plain-text format, parsed
26-
natively by every implementation.
27-
* *`.k9.ncl`* — a https://nickel-lang.org[Nickel] document. Implementations
28-
without a Nickel evaluator MUST detect this form and report it distinctly
29-
(e.g. `k9-rs` returns `K9Error::NickelFormat`); evaluation is delegated to a
30-
Nickel-aware tool such as `pandoc-k9`.
27+
* *`.k9`* — plain-text surface.
28+
* *`.k9.ncl`* — https://nickel-lang.org[Nickel] surface (the form used across the
29+
estate today). Evaluation is delegated to a Nickel-aware tool (e.g.
30+
`pandoc-k9`); parsers without an evaluator detect it distinctly (`k9-rs`
31+
returns `K9Error::NickelFormat`).
3132

32-
Both surfaces denote the same abstract model (sections 3–6) and MUST produce
33-
the same canonical projection (section 7).
33+
== 2. File preamble (required)
3434

35-
== 2. Document structure
36-
37-
A document declares exactly one `component` with a name, a `version`, and the
38-
sections below. Indentation is significant; two spaces per level is the
39-
canonical style.
35+
[cols="1,3", options="header"]
36+
|===
37+
| Rule | Requirement
38+
| Magic | The *first non-empty line MUST be exactly* `K9!`
39+
| Licence | An SPDX `License-Identifier` SHOULD appear within the first 10 lines
40+
|===
4041

41-
[source,yaml]
42-
----
43-
component: <name>
44-
version: <semver>
45-
pedigree: { ... } # section 3
46-
security: <level> # section 4
47-
recipe: { ... } # section 5
48-
contracts: [ ... ] # section 6
49-
----
42+
The validator treats a missing magic line or a missing `pedigree` block as
43+
*errors*; a missing SPDX line, `version`/`schema_version`, or security level as
44+
*warnings*.
5045

51-
`component` and `version` are REQUIRED. `pedigree`, `security`, `recipe`, and
52-
`contracts` are each OPTIONAL but RECOMMENDED.
46+
== 3. The Pedigree
5347

54-
== 3. Pedigree — provenance
48+
Every document MUST contain a `pedigree = { … }` record — the component's
49+
self-description. Canonically it is organised in *five layers*:
5550

5651
[cols="1,1,3", options="header"]
5752
|===
58-
| Key | Required | Meaning
59-
| `origin` | yes | Source URL of the component (HTTPS)
60-
| `author` | yes | Responsible author or maintainer
61-
| `license` | no | SPDX license identifier
62-
| `hashes` | no | Supply-chain hashes (SHA-256+). `[OPEN]` exact key shape TBD
53+
| Layer | Key | Holds
54+
| L1 — The Snout | `metadata` | Identity: `name`, `version`, `breed` (a MIME-like type, e.g. `application/vnd.k9+nickel`), `magic_number`, `license`, `description`
55+
| L2 — The Scent | `target` | Environment: `os`, `requires_*`, resource minimums
56+
| L3 — The Leash | `security` | Trust tier + capability flags (section 4)
57+
| L4 — The Gut | `validation` | `checksum`, `pedigree_version`, authorisation state
58+
| L5 — The Muscle | `recipes` | Named build/deploy/verify commands
6359
|===
6460

65-
MD5 and SHA-1 MUST NOT be used for `hashes`.
61+
The pedigree SHOULD carry a top-level `schema_version` (or `version`) and a
62+
`security_level`/`leash` so it validates without warnings. `[OPEN]` whether
63+
`security_level` is required at pedigree top level or may be inferred from
64+
`security.trust_level`.
6665

67-
== 4. SecurityLevel
66+
== 4. The Leash — trust levels
6867

69-
A single enumerated value describing the trust tier (shared with the K9 Nickel
70-
component model):
68+
`security.trust_level` (and the top-level `security_level`) take one of three
69+
tiers, given as Nickel enum tags:
7170

7271
[cols="1,3", options="header"]
7372
|===
7473
| Level | Meaning
75-
| `kennel` | Most restricted — trusted, sandboxed, minimal surface
76-
| `yard` | Intermediate — controlled exposure
77-
| `hunt` | Least restricted — operates in the open
74+
| `'Kennel` | Most restricted — sandboxed, minimal capabilities, no side effects
75+
| `'Yard` | Intermediate — controlled exposure
76+
| `'Hunt` | Least restricted — may execute shell commands; REQUIRES explicit
77+
authorisation (a signature / handshake) before execution
7878
|===
7979

80-
`[OPEN]` Whether levels are case-insensitive on input is reference-defined; the
81-
canonical projection (section 7) lower-cases them.
82-
83-
== 5. Recipe — build / assembly
84-
85-
[cols="1,1,3", options="header"]
86-
|===
87-
| Key | Required | Meaning
88-
| `tool` | yes | Build tool (e.g. `cargo`, `mix`, `gleam`, `deno`, `cabal`)
89-
| `command` | yes | Arguments passed to the tool
90-
|===
80+
Capability flags (`allow_network`, `allow_subprocess`, `allow_filesystem_write`,
81+
…) further constrain the component. A `'Hunt` component MUST NOT run until
82+
authorised (`validation.hunt_authorized = true` after signature verification).
83+
Signatures are Ed25519. `[OPEN]` exact signature envelope.
9184

92-
== 6. Contracts — runtime invariants
85+
== 5. Recipes
9386

94-
`contracts` is an ordered list. Each entry:
87+
`recipes` is a record of named commands (e.g. `build`, `validate`, `deploy`,
88+
`migrate`, `rollback`). Multi-line scripts use Nickel string blocks (`m%"…"%`).
9589

96-
[cols="1,1,3", options="header"]
97-
|===
98-
| Key | Required | Meaning
99-
| `name` | yes | Stable identifier for the invariant
100-
| `check` | yes | Shell command; exit code 0 = satisfied
101-
| `severity` | yes | `error` \| `warn` \| `info`
102-
|===
90+
== 6. Surfaces and canonical projection
10391

104-
A K9 validator runs each `check`; a failing `error` contract fails validation,
105-
`warn` and `info` are reported without failing. `[OPEN]` ordering/short-circuit
106-
semantics across multiple failures to be pinned by the conformance suite.
92+
For cross-implementation conformance, a document projects to canonical JSON:
93+
the `pedigree` record (and any sibling top-level keys) as JSON, Nickel enum tags
94+
rendered as their bare string (`'Kennel` → `"Kennel"`), records in declaration
95+
order. A `.k9.ncl` document is evaluated first, then projected; a `.k9` document
96+
is parsed natively. Both surfaces MUST yield the same projection. See
97+
`../conformance/fixtures/` for worked examples.
10798

108-
== 7. Canonical projection
99+
== 7. Conformance
109100

110-
For cross-implementation conformance, a parsed document projects to canonical
111-
JSON: object keys in declaration order, `security` lower-cased, `contracts` as a
112-
JSON array, no implementation-specific fields. See
113-
`../conformance/fixtures/` for worked examples. Every implementation MUST emit a
114-
projection that is structurally equal to the fixture's `expected.json`.
101+
Every implementation MUST reproduce each fixture's `expected.json` from its
102+
input. The fixtures are the executable form of this spec; an `[OPEN]` item is
103+
closed by adding a fixture that pins the behaviour.
115104

116105
== 8. Versioning
117106

118-
This spec is versioned independently of any implementation. Implementations
107+
This spec is versioned independently of any implementation; implementations
119108
declare the spec version they target. Breaking changes bump the minor version
120109
while in `0.x`.

0 commit comments

Comments
 (0)