From f911392b9f6e75814f0c44fb38c78a2648accba9 Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 20 Jun 2026 18:12:25 +0000 Subject: [PATCH 1/2] Add k9-ecosystem aggregator hub (staged for extraction) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Scaffold the standalone k9-ecosystem hub for the K9 self-validating configuration format, staged under k9-ecosystem/ for delivery (intended end-state: its own top-level primary — see k9-ecosystem/BOOTSTRAP.md). The hub owns the canonical spec (spec/SPEC.adoc, v0.1 draft; k9-rs normative until ratified) and a language-agnostic conformance suite (conformance/fixtures/minimal), and aggregates the 11 K9 member repos as git submodules grouped by role: implementations k9-rs, k9_ex, k9_gleam, k9-deno, k9-haskell tooling tree-sitter-k9, vscode-k9, pandoc-k9 ci k9-validate-action, k9-pre-commit examples k9-showcase Members stay standalone (idiomatic to crates.io/Hex/Hackage/JSR and to the tree-sitter/vsce/pre-commit/Actions conventions); the hub provides a single spec source-of-truth, one conformance suite, and one front door. Includes Justfile + scripts/init-submodules.sh for submodule orchestration and .machine_readable/6a2 governance. developer-ecosystem ECOSYSTEM/STATE indexed to reference the staged hub. Co-Authored-By: Claude Opus 4.8 Claude-Session: https://claude.ai/code/session_019i2e5ABGBKQmMdqk8puFWx --- .machine_readable/6a2/ECOSYSTEM.a2ml | 2 +- .machine_readable/6a2/STATE.a2ml | 4 + k9-ecosystem/.gitignore | 10 ++ k9-ecosystem/.gitmodules | 48 +++++++ .../.machine_readable/6a2/ECOSYSTEM.a2ml | 33 +++++ k9-ecosystem/.machine_readable/6a2/META.a2ml | 31 +++++ k9-ecosystem/.machine_readable/6a2/STATE.a2ml | 27 ++++ k9-ecosystem/BOOTSTRAP.md | 55 ++++++++ k9-ecosystem/Justfile | 34 +++++ k9-ecosystem/LICENSE | 14 ++ k9-ecosystem/README.adoc | 128 ++++++++++++++++++ k9-ecosystem/conformance/README.adoc | 44 ++++++ .../conformance/fixtures/minimal/component.k9 | 14 ++ .../fixtures/minimal/expected.json | 21 +++ k9-ecosystem/members/README.adoc | 36 +++++ k9-ecosystem/scripts/init-submodules.sh | 46 +++++++ k9-ecosystem/spec/README.adoc | 19 +++ k9-ecosystem/spec/SPEC.adoc | 120 ++++++++++++++++ 18 files changed, 685 insertions(+), 1 deletion(-) create mode 100644 k9-ecosystem/.gitignore create mode 100644 k9-ecosystem/.gitmodules create mode 100644 k9-ecosystem/.machine_readable/6a2/ECOSYSTEM.a2ml create mode 100644 k9-ecosystem/.machine_readable/6a2/META.a2ml create mode 100644 k9-ecosystem/.machine_readable/6a2/STATE.a2ml create mode 100644 k9-ecosystem/BOOTSTRAP.md create mode 100644 k9-ecosystem/Justfile create mode 100644 k9-ecosystem/LICENSE create mode 100644 k9-ecosystem/README.adoc create mode 100644 k9-ecosystem/conformance/README.adoc create mode 100644 k9-ecosystem/conformance/fixtures/minimal/component.k9 create mode 100644 k9-ecosystem/conformance/fixtures/minimal/expected.json create mode 100644 k9-ecosystem/members/README.adoc create mode 100755 k9-ecosystem/scripts/init-submodules.sh create mode 100644 k9-ecosystem/spec/README.adoc create mode 100644 k9-ecosystem/spec/SPEC.adoc diff --git a/.machine_readable/6a2/ECOSYSTEM.a2ml b/.machine_readable/6a2/ECOSYSTEM.a2ml index 28d8aaa8e..bf9cec5e0 100644 --- a/.machine_readable/6a2/ECOSYSTEM.a2ml +++ b/.machine_readable/6a2/ECOSYSTEM.a2ml @@ -18,7 +18,7 @@ git-tools = ["git-hud", "gitloom", "git-reunify", "git-seo", "polysafe-gitfixer" repo-management = ["oikos", "robot-repo-automaton", "grim-repo", "robot-vacuum-cleaner"] scaffolding = ["scaffoldia"] developer-ux = ["rescript-evangeliser (→ nextgen-languages-evangeliser)", "recon-silly-ation", "nickel-config-reporter"] -ecosystems = ["affinescript-ecosystem", "rescript-ecosystem", "zig-ecosystem", "v-ecosystem (legacy/community-handoff)", "deno-ecosystem", "julia-ecosystem", "coq-ecosystem", "idris2-ecosystem", "iser-tools"] +ecosystems = ["affinescript-ecosystem", "rescript-ecosystem", "zig-ecosystem", "v-ecosystem (legacy/community-handoff)", "deno-ecosystem", "julia-ecosystem", "coq-ecosystem", "idris2-ecosystem", "iser-tools", "k9-ecosystem (staged; pending extraction to standalone primary — see k9-ecosystem/BOOTSTRAP.md)"] cadre = ["cadre-router", "cadre-tea-router"] zig-api = ["zig-api (Phase 1+2 complete)"] diff --git a/.machine_readable/6a2/STATE.a2ml b/.machine_readable/6a2/STATE.a2ml index 9ec23e593..63d3e60ae 100644 --- a/.machine_readable/6a2/STATE.a2ml +++ b/.machine_readable/6a2/STATE.a2ml @@ -13,6 +13,10 @@ name = "developer-ecosystem" completion-percentage = 55 phase = "license-stamps-flipped-to-MPL-2.0; bot_directives-rename-done; contractiles-currency-pass-2026-06-05" +[session-2026-06-20-k9-ecosystem-scaffold] +summary = "Scaffolded the k9-ecosystem aggregator hub under k9-ecosystem/ (staged for extraction to a standalone primary). Owns the K9 spec (spec/SPEC.adoc v0.1 draft) + conformance suite; aggregates 11 members (k9-rs/k9_ex/k9_gleam/k9-deno/k9-haskell + tree-sitter-k9/vscode-k9/pandoc-k9 + k9-validate-action/k9-pre-commit + k9-showcase) as git submodules via k9-ecosystem/.gitmodules. Added Justfile, scripts/init-submodules.sh, BOOTSTRAP.md, and 6a2 governance. Reasoning: members must stay standalone for their registries/toolchains; hub gives one spec + conformance source-of-truth. Staged here because a new top-level repo was outside session scope." +files-changed = "18 (16 new under k9-ecosystem/, ECOSYSTEM.a2ml + STATE.a2ml indexed)" + [session-2026-06-05-currency-checkpoint] summary = "Currency checkpoint (claude/zen-knuth-6hoEt branch). Tasks completed: (1) agent_instructions→bot_directives rename already done at root; external references fixed in 8 files across affinescript-ecosystem and rescript-ecosystem satellite subdirs. (2) Contractiles: INDEX.a2ml + _base.ncl added; missing verb dirs created (adjust/, bust/, intend/) each with Xfile.a2ml + canonical .ncl runner; must/trust/dust runners (must.ncl/trust.ncl/dust.ncl) added alongside existing Xfile.a2ml. (3) svc/k9 templates already present. (4) 6a2 files refreshed. (5) README/EXPLAINME currency-only fixes. (6) License scan flagged." files-changed = "~20 (currency + contractile files)" diff --git a/k9-ecosystem/.gitignore b/k9-ecosystem/.gitignore new file mode 100644 index 000000000..5ae954b48 --- /dev/null +++ b/k9-ecosystem/.gitignore @@ -0,0 +1,10 @@ +# SPDX-License-Identifier: MPL-2.0 +# Submodule build artifacts (members manage their own; never commit theirs here) +**/target/ +**/_build/ +**/build/ +**/node_modules/ +**/.deno/ +**/dist-newstyle/ +*.log +.DS_Store diff --git a/k9-ecosystem/.gitmodules b/k9-ecosystem/.gitmodules new file mode 100644 index 000000000..805d3502d --- /dev/null +++ b/k9-ecosystem/.gitmodules @@ -0,0 +1,48 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# K9 ecosystem members, grouped by role. +# Inert while staged inside developer-ecosystem (git only reads the root +# .gitmodules). Becomes active once this directory is extracted to its own +# repository — see BOOTSTRAP.md. + +# --- Implementations ------------------------------------------------------- +[submodule "members/implementations/k9-rs"] + path = members/implementations/k9-rs + url = git@github.com:hyperpolymath/k9-rs.git +[submodule "members/implementations/k9_ex"] + path = members/implementations/k9_ex + url = git@github.com:hyperpolymath/k9_ex.git +[submodule "members/implementations/k9_gleam"] + path = members/implementations/k9_gleam + url = git@github.com:hyperpolymath/k9_gleam.git +[submodule "members/implementations/k9-deno"] + path = members/implementations/k9-deno + url = git@github.com:hyperpolymath/k9-deno.git +[submodule "members/implementations/k9-haskell"] + path = members/implementations/k9-haskell + url = git@github.com:hyperpolymath/k9-haskell.git + +# --- Tooling --------------------------------------------------------------- +[submodule "members/tooling/tree-sitter-k9"] + path = members/tooling/tree-sitter-k9 + url = git@github.com:hyperpolymath/tree-sitter-k9.git +[submodule "members/tooling/vscode-k9"] + path = members/tooling/vscode-k9 + url = git@github.com:hyperpolymath/vscode-k9.git +[submodule "members/tooling/pandoc-k9"] + path = members/tooling/pandoc-k9 + url = git@github.com:hyperpolymath/pandoc-k9.git + +# --- CI / validation ------------------------------------------------------- +[submodule "members/ci/k9-validate-action"] + path = members/ci/k9-validate-action + url = git@github.com:hyperpolymath/k9-validate-action.git +[submodule "members/ci/k9-pre-commit"] + path = members/ci/k9-pre-commit + url = git@github.com:hyperpolymath/k9-pre-commit.git + +# --- Examples -------------------------------------------------------------- +[submodule "members/examples/k9-showcase"] + path = members/examples/k9-showcase + url = git@github.com:hyperpolymath/k9-showcase.git diff --git a/k9-ecosystem/.machine_readable/6a2/ECOSYSTEM.a2ml b/k9-ecosystem/.machine_readable/6a2/ECOSYSTEM.a2ml new file mode 100644 index 000000000..758de12ce --- /dev/null +++ b/k9-ecosystem/.machine_readable/6a2/ECOSYSTEM.a2ml @@ -0,0 +1,33 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) +# ECOSYSTEM.a2ml — Ecosystem position + +[metadata] +project = "k9-ecosystem" +last-updated = "2026-06-20" +ecosystem = "hyperpolymath" + +[position] +type = "aggregator-hub" +role = "canonical home of the K9 self-validating configuration format — owns the spec + conformance suite, aggregates the implementations and tooling as submodules" +parent = "hyperpolymath estate" +staged-in = "developer-ecosystem/k9-ecosystem (pending extraction to standalone repo — see BOOTSTRAP.md)" + +[members] +# Aggregated as git submodules, grouped by role (see .gitmodules) +implementations = ["k9-rs (reference, Rust/crates.io)", "k9_ex (Elixir/Hex)", "k9_gleam (Gleam/Hex)", "k9-deno (Deno/JSR)", "k9-haskell (Haskell/Hackage)"] +tooling = ["tree-sitter-k9", "vscode-k9", "pandoc-k9"] +ci = ["k9-validate-action", "k9-pre-commit"] +examples = ["k9-showcase"] + +[owns] +spec = "spec/SPEC.adoc (v0.1 draft; k9-rs normative until ratified)" +conformance = "conformance/fixtures/ (canonical-projection golden tests, all implementations)" + +[related-projects] +# relationship types: sibling-standard, dependency, dependent, inspiration, potential-consumer +standards = { name = "hyperpolymath/standards", relationship = "dependent", note = "references k9-svc as a reusable standard" } +developer-ecosystem = { name = "developer-ecosystem", relationship = "dependent", note = "contractile tridents consume .k9.ncl (k9.ncl); also potential dev-ux home for vscode-k9" } + +[integration-points] +# Each implementation member is obligated to run conformance/fixtures/. diff --git a/k9-ecosystem/.machine_readable/6a2/META.a2ml b/k9-ecosystem/.machine_readable/6a2/META.a2ml new file mode 100644 index 000000000..a4f5d3f8d --- /dev/null +++ b/k9-ecosystem/.machine_readable/6a2/META.a2ml @@ -0,0 +1,31 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) +# META.a2ml — Architecture decisions and development practices + +[metadata] +project = "k9-ecosystem" +last-updated = "2026-06-20" + +[decision.hub-plus-submodules] +status = "accepted" +date = "2026-06-20" +decision = "Aggregate the K9 family as a hub repo with members as git submodules, rather than a single monorepo or scattered standalone repos." +rationale = "Several members cannot be subdirectories without breaking their toolchains: tree-sitter-k9 (tree-sitter CLI + bindings expect a repo named tree-sitter-* at root), k9-validate-action (consumed as owner/repo@ref; Marketplace needs its own repo), k9-pre-commit (consumers reference the repo URL; .pre-commit-hooks.yaml at root), vscode-k9 (vsce per-extension publishing). The five implementations each publish to a different registry (crates.io/Hex/Hackage/JSR) with independent versioning. A hub still gives one spec source-of-truth, one conformance suite, and one front door." +follows = "developer-ecosystem satellites + *-ecosystem submodule pattern" + +[decision.standalone-primary] +status = "accepted" +date = "2026-06-20" +decision = "k9-ecosystem is its own top-level primary, not nested under developer-ecosystem." +rationale = "developer-ecosystem's charter is developer workflow tooling; K9 is a configuration format/standard with a different audience. Nesting muddies both charters. Staged inside developer-ecosystem only as a delivery vehicle (session scope); end-state is standalone (BOOTSTRAP.md)." + +[decision.naming] +status = "accepted" +date = "2026-06-20" +decision = "Umbrella named 'k9-ecosystem', not 'k9-svc'." +rationale = "k9-svc is already the Rust crate lib name (k9_svc, 'self-validating configuration') and a referenced standard in hyperpolymath/standards; reusing it as the umbrella would collide. 'k9-ecosystem' matches the estate's *-ecosystem convention." + +[practices] +spec-first = "Format changes land in spec/ first, gated by conformance/ fixtures, then propagate to implementations." +spdx = "MPL-2.0 SPDX header on every file." +languages = "Docs in AsciiDoc/Markdown; machine-readable in .a2ml (TOML); orchestration in just + minimal Bash; configs in Nickel. No banned languages introduced." diff --git a/k9-ecosystem/.machine_readable/6a2/STATE.a2ml b/k9-ecosystem/.machine_readable/6a2/STATE.a2ml new file mode 100644 index 000000000..f220b8896 --- /dev/null +++ b/k9-ecosystem/.machine_readable/6a2/STATE.a2ml @@ -0,0 +1,27 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) +# STATE.a2ml — Project state checkpoint + +[metadata] +project = "k9-ecosystem" +version = "0.1.0" +last-updated = "2026-06-20" +status = "scaffolded" + +[project-context] +name = "k9-ecosystem" +completion-percentage = 20 +phase = "hub scaffolded and staged inside developer-ecosystem; awaiting extraction to standalone repo and live submodule wiring" + +[session-2026-06-20-hub-scaffold] +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." +files-added = 16 + +[next-actions] +extract = "Promote to standalone hyperpolymath/k9-ecosystem (BOOTSTRAP.md option A)" +wire = "Run scripts/init-submodules.sh to populate the 11 member submodules" +cross-link = "Update each member's 6a2/ECOSYSTEM.a2ml to reference k9-ecosystem (bidirectional graph)" +spec = "Ratify SPEC.adoc; resolve the [OPEN] items via conformance fixtures" + +[blockers] +new-repo-scope = "Creating/pushing hyperpolymath/k9-ecosystem is outside this session's repo scope; needs scope grant or manual extraction" diff --git a/k9-ecosystem/BOOTSTRAP.md b/k9-ecosystem/BOOTSTRAP.md new file mode 100644 index 000000000..51b03cc02 --- /dev/null +++ b/k9-ecosystem/BOOTSTRAP.md @@ -0,0 +1,55 @@ + + + +# Bootstrapping `k9-ecosystem` into a standalone repository + +This directory was authored as the **standalone `k9-ecosystem` hub** but is +currently **staged inside `developer-ecosystem/`** because the delivering +session's scope did not permit creating a new top-level GitHub repository. +Everything here is defined as if `k9-ecosystem/` were a repository root (its own +`.gitmodules`, `README.adoc`, `LICENSE`, `.machine_readable/6a2/`), so promotion +is mechanical. + +## Option A — extract preserving history (recommended) + +```sh +# from the developer-ecosystem repo root +git subtree split -P k9-ecosystem -b k9-ecosystem-export + +# create the empty repo hyperpolymath/k9-ecosystem on GitHub first, then: +mkdir ../k9-ecosystem && cd ../k9-ecosystem +git init +git pull ../developer-ecosystem k9-ecosystem-export +git remote add origin git@github.com:hyperpolymath/k9-ecosystem.git +git push -u origin main + +# activate the members +just init # or: bash scripts/init-submodules.sh +``` + +## Option B — fresh start (no history) + +```sh +cp -r developer-ecosystem/k9-ecosystem /path/to/k9-ecosystem +cd /path/to/k9-ecosystem +git init && git add -A && git commit -m "Initialise k9-ecosystem hub" +git remote add origin git@github.com:hyperpolymath/k9-ecosystem.git +git push -u origin main +bash scripts/init-submodules.sh +``` + +## After extraction + +1. Copy the full MPL-2.0 text to `license/MPL-2.0.txt` (see `LICENSE`). +2. Remove the staging copy from `developer-ecosystem/` and, if desired, add the + new repo back as a submodule or cross-reference it from + `developer-ecosystem/.machine_readable/6a2/ECOSYSTEM.a2ml`. +3. Point each member repo's `.machine_readable/6a2/ECOSYSTEM.a2ml` at + `k9-ecosystem` (relationship `dependency` / `sibling-standard`) so the graph + is bidirectional. + +## Alternative: let the agent push it directly + +If you add `hyperpolymath/k9-ecosystem` to the session's repository scope (and +create the empty repo), the agent can push the hub and open its PR directly, +skipping the manual extraction above. diff --git a/k9-ecosystem/Justfile b/k9-ecosystem/Justfile new file mode 100644 index 000000000..2f1878dab --- /dev/null +++ b/k9-ecosystem/Justfile @@ -0,0 +1,34 @@ +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# k9-ecosystem hub — submodule orchestration. +# These recipes assume this directory is a repository root (post-extraction). + +# List available recipes +default: + @just --list + +# Populate every member submodule +init: + git submodule update --init --recursive + +# Add all members from scratch (first-time wiring; idempotent) +wire: + bash scripts/init-submodules.sh + +# Fast-forward every member to its tracked commit +update: + git submodule update --remote --merge + +# Show checkout state of every member +status: + git submodule status --recursive + +# Run a command in every member, e.g. `just foreach 'git status -s'` +foreach CMD: + git submodule foreach --recursive '{{CMD}}' + +# Verify the canonical spec and conformance fixtures are present +check-spec: + test -f spec/SPEC.adoc && echo "spec present" + test -d conformance/fixtures && echo "conformance fixtures present" diff --git a/k9-ecosystem/LICENSE b/k9-ecosystem/LICENSE new file mode 100644 index 000000000..e80368936 --- /dev/null +++ b/k9-ecosystem/LICENSE @@ -0,0 +1,14 @@ +SPDX-License-Identifier: MPL-2.0 +SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) + +This project is licensed under the Mozilla Public License, version 2.0 +(MPL-2.0-or-later), consistent with the hyperpolymath estate's Palimpsest +philosophy. + +The canonical MPL-2.0 text is provided by the estate at +`developer-ecosystem/license/` and by the upstream palimpsest-license +repository. When this directory is extracted into its own repository, copy the +full MPL-2.0 text to `license/MPL-2.0.txt` alongside this notice +(see BOOTSTRAP.md). + +Each source file additionally carries an SPDX `License-Identifier` header. diff --git a/k9-ecosystem/README.adoc b/k9-ecosystem/README.adoc new file mode 100644 index 000000000..b5e7d4bbe --- /dev/null +++ b/k9-ecosystem/README.adoc @@ -0,0 +1,128 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) +image:https://img.shields.io/badge/License-MPL_2.0-blue.svg[MPL-2.0-or-later,link="https://opensource.org/licenses/MPL-2.0"] +image:https://img.shields.io/badge/Philosophy-Palimpsest-indigo.svg[Palimpsest,link="https://github.com/hyperpolymath/palimpsest-license"] + += K9 Ecosystem +Jonathan D.A. Jewell +:toc: left +:toclevels: 3 +:icons: font +:revnumber: 0.1.0 +:revdate: 2026-06-20 + +[NOTE] +==== +This directory is the *standalone `k9-ecosystem` hub*, staged inside +`developer-ecosystem/` for delivery. Its intended end-state is its own +top-level repository (`hyperpolymath/k9-ecosystem`). See +link:BOOTSTRAP.md[BOOTSTRAP.md] for the one-command extraction. +==== + +== What is K9? + +*K9* is a self-validating configuration format for software component +declarations. Each component carries its own provenance, security posture, +build recipe, and runtime contracts, so that a `.k9` file is both +*documentation* and an *executable check*. + +Each component declares: + +[cols="1,3", options="header"] +|=== +| Field | Meaning +| *Pedigree* | Provenance — origin URL, author, license, supply-chain hashes +| *SecurityLevel* | Three-tier model: `Kennel` / `Yard` / `Hunt` +| *Recipe* | Build or assembly instructions (tool + command) +| *Contracts* | Runtime invariants with check commands and severity levels +|=== + +Two surface formats are defined: + +* `.k9` — YAML-like plain-text format, parsed natively +* `.k9.ncl` — https://nickel-lang.org[Nickel] format (requires the Nickel evaluator) + +The canonical format definition lives in link:spec/SPEC.adoc[`spec/SPEC.adoc`]; +the shared cross-implementation test suite lives in link:conformance/[`conformance/`]. + +== Why this hub exists + +The K9 family spans five language implementations plus editor, CI, and +documentation tooling — eleven repositories in all. They are kept as +*independent repositories* (each idiomatic to its package registry and tooling) +and *aggregated here as git submodules*. This gives one source of truth for the +spec, one conformance suite every implementation tests against, and one front +door — without forcing a monorepo on toolchains that expect standalone repos +(crates.io, Hex, Hackage, JSR, the Tree-sitter CLI, the VS Code Marketplace, +pre-commit, and GitHub Actions). + +== Members + +Run `just init` (or `scripts/init-submodules.sh`) to populate the submodules. + +=== Implementations (parsers + renderers) + +[cols="1,1,2", options="header"] +|=== +| Repo | Language | Registry +| `k9-rs` | Rust | crates.io (reference implementation) +| `k9_ex` | Elixir | Hex +| `k9_gleam` | Gleam | Hex / Gleam +| `k9-deno` | Deno | JSR +| `k9-haskell` | Haskell | Hackage +|=== + +=== Tooling (format + editor) + +[cols="1,3", options="header"] +|=== +| Repo | Purpose +| `tree-sitter-k9` | Tree-sitter grammar + bindings +| `vscode-k9` | VS Code language extension +| `pandoc-k9` | Pandoc integration for `.k9.ncl` (Nickel) files +|=== + +=== CI / validation + +[cols="1,3", options="header"] +|=== +| Repo | Purpose +| `k9-validate-action` | GitHub Action — validates `.k9` files in CI +| `k9-pre-commit` | pre-commit hook — validates `.k9` files locally +|=== + +=== Examples + +[cols="1,3", options="header"] +|=== +| Repo | Purpose +| `k9-showcase` | Worked examples and demonstrations +|=== + +== Quick start + +[source,sh] +---- +# clone with all members +git clone --recurse-submodules +cd k9-ecosystem + +# or, after a plain clone +just init # = git submodule update --init --recursive + +just status # show member checkout state +just update # fast-forward every member to its tracked commit +---- + +== Relationship to the wider estate + +* `hyperpolymath/standards` references *k9-svc* (the Rust crate lib name, + `k9_svc`) as a reusable standard; the `.k9.ncl` form is woven into the + `developer-ecosystem` *contractile tridents* (`k9.ncl`). This hub is the + home of the *format* itself, distinct from those consumers. +* `vscode-k9` may also be cross-referenced as a `developer-ux` satellite of + `developer-ecosystem`; a submodule can be aggregated by more than one hub. + +== License + +MPL-2.0-or-later. SPDX headers are carried on every file. See link:LICENSE[LICENSE]. diff --git a/k9-ecosystem/conformance/README.adoc b/k9-ecosystem/conformance/README.adoc new file mode 100644 index 000000000..73ce25c88 --- /dev/null +++ b/k9-ecosystem/conformance/README.adoc @@ -0,0 +1,44 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) += K9 Conformance Suite + +Language-agnostic fixtures that *every* implementation must satisfy. This is the +contract that keeps five independent parsers (`k9-rs`, `k9_ex`, `k9_gleam`, +`k9-deno`, `k9-haskell`) behaving identically. + +== Layout + +---- +conformance/ +└── fixtures/ + └── / + ├── component.k9 # input (also /component.k9.ncl where relevant) + └── expected.json # canonical projection (see ../spec/SPEC.adoc §7) +---- + +== Contract + +For each fixture, an implementation parses `component.k9` and emits the canonical +JSON projection. The result MUST be *structurally equal* to `expected.json` +(key order per declaration order, `security` lower-cased). Whitespace and +insignificant formatting are ignored. + +== Wiring an implementation + +Each implementation member adds a thin test that walks `fixtures/`, parses each +input, and compares against `expected.json`. Suggested integration: + +* `k9-rs` — a `#[test]` iterating the fixtures directory +* `k9_ex` / `k9_gleam` — an ExUnit / gleeunit case +* `k9-deno` — a `Deno.test` over the fixtures +* `k9-haskell` — an HUnit/`tasty` golden test + +The fixtures are intentionally implementation-free so they can be vendored or +referenced (git submodule / sparse checkout) by each member. + +== Adding a fixture + +1. Create `fixtures//component.k9`. +2. Generate `expected.json` with the reference implementation (`k9-rs`) and + eyeball it against link:../spec/SPEC.adoc[the spec]. +3. Confirm every implementation passes before merging. diff --git a/k9-ecosystem/conformance/fixtures/minimal/component.k9 b/k9-ecosystem/conformance/fixtures/minimal/component.k9 new file mode 100644 index 000000000..261a89b37 --- /dev/null +++ b/k9-ecosystem/conformance/fixtures/minimal/component.k9 @@ -0,0 +1,14 @@ +component: hello-svc + version: 0.1.0 + pedigree: + origin: https://github.com/hyperpolymath/hello-svc + author: Jonathan D.A. Jewell + license: MPL-2.0 + security: kennel + recipe: + tool: cargo + command: build --release + contracts: + - name: binary-exists + check: test -x target/release/hello-svc + severity: error diff --git a/k9-ecosystem/conformance/fixtures/minimal/expected.json b/k9-ecosystem/conformance/fixtures/minimal/expected.json new file mode 100644 index 000000000..f1c01aef3 --- /dev/null +++ b/k9-ecosystem/conformance/fixtures/minimal/expected.json @@ -0,0 +1,21 @@ +{ + "component": "hello-svc", + "version": "0.1.0", + "pedigree": { + "origin": "https://github.com/hyperpolymath/hello-svc", + "author": "Jonathan D.A. Jewell", + "license": "MPL-2.0" + }, + "security": "kennel", + "recipe": { + "tool": "cargo", + "command": "build --release" + }, + "contracts": [ + { + "name": "binary-exists", + "check": "test -x target/release/hello-svc", + "severity": "error" + } + ] +} diff --git a/k9-ecosystem/members/README.adoc b/k9-ecosystem/members/README.adoc new file mode 100644 index 000000000..13c997b49 --- /dev/null +++ b/k9-ecosystem/members/README.adoc @@ -0,0 +1,36 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) += K9 Members + +Member repositories are aggregated here as *git submodules*, grouped by role. +They are empty until populated — run `just init` (or +`bash scripts/init-submodules.sh`) from the hub root. + +---- +members/ +├── implementations/ # parsers + renderers, one per language +│ ├── k9-rs # Rust (reference implementation, crates.io) +│ ├── k9_ex # Elixir (Hex) +│ ├── k9_gleam # Gleam (Hex / Gleam) +│ ├── k9-deno # Deno (JSR) +│ └── k9-haskell # Haskell (Hackage) +├── tooling/ # format + editor support +│ ├── tree-sitter-k9 # grammar + bindings +│ ├── vscode-k9 # VS Code extension +│ └── pandoc-k9 # Pandoc integration for .k9.ncl +├── ci/ # validation hooks +│ ├── k9-validate-action # GitHub Action +│ └── k9-pre-commit # pre-commit hook +└── examples/ + └── k9-showcase # worked examples +---- + +Each member stays idiomatic to its own ecosystem (its own build tool, version, +release cadence, and package registry). The hub pins each to a specific commit; +`just update` advances those pins. + +== Conformance obligation + +Every *implementation* member is expected to run the shared fixtures in +`../conformance/fixtures/` and match the documented expected output. This is the +contract that keeps five independent parsers behaving identically. diff --git a/k9-ecosystem/scripts/init-submodules.sh b/k9-ecosystem/scripts/init-submodules.sh new file mode 100755 index 000000000..04d2fa2a5 --- /dev/null +++ b/k9-ecosystem/scripts/init-submodules.sh @@ -0,0 +1,46 @@ +#!/usr/bin/env bash +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) +# +# Idempotently wire every K9 member as a git submodule. +# Safe to re-run: skips members already present. +# Run from the k9-ecosystem repository root (post-extraction — see BOOTSTRAP.md). + +set -euo pipefail + +GH="git@github.com:hyperpolymath" + +# path repo +members=( + "members/implementations/k9-rs k9-rs" + "members/implementations/k9_ex k9_ex" + "members/implementations/k9_gleam k9_gleam" + "members/implementations/k9-deno k9-deno" + "members/implementations/k9-haskell k9-haskell" + "members/tooling/tree-sitter-k9 tree-sitter-k9" + "members/tooling/vscode-k9 vscode-k9" + "members/tooling/pandoc-k9 pandoc-k9" + "members/ci/k9-validate-action k9-validate-action" + "members/ci/k9-pre-commit k9-pre-commit" + "members/examples/k9-showcase k9-showcase" +) + +if [ ! -f .gitmodules ] || [ ! -d .git ]; then + echo "error: run from the k9-ecosystem repository root" >&2 + exit 1 +fi + +for entry in "${members[@]}"; do + # shellcheck disable=SC2086 + set -- $entry + path="$1"; repo="$2" + if [ -e "$path/.git" ] || git config --file .gitmodules --get "submodule.$path.url" >/dev/null 2>&1; then + echo "skip $path (already configured)" + continue + fi + echo "add $path -> $GH/$repo.git" + git submodule add "$GH/$repo.git" "$path" +done + +git submodule update --init --recursive +echo "done: $(git submodule status --recursive | wc -l) members wired" diff --git a/k9-ecosystem/spec/README.adoc b/k9-ecosystem/spec/README.adoc new file mode 100644 index 000000000..442c6ce6b --- /dev/null +++ b/k9-ecosystem/spec/README.adoc @@ -0,0 +1,19 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) += K9 Specification + +* link:SPEC.adoc[*SPEC.adoc*] — the canonical K9 format specification (v0.1 draft). + +== Status + +The format is at *v0.1 draft*. `k9-rs` is the normative reference until the spec +is ratified. This `spec/` directory is the single source of truth: changes to +the format land here first, then propagate to the implementations, gated by the +link:../conformance/[conformance suite]. + +== Change process + +1. Propose the change as a PR against `SPEC.adoc`. +2. Add or amend fixtures in `../conformance/fixtures/` demonstrating it. +3. Update each implementation submodule to satisfy the new fixtures. +4. Bump the spec `:revnumber:` when merged. diff --git a/k9-ecosystem/spec/SPEC.adoc b/k9-ecosystem/spec/SPEC.adoc new file mode 100644 index 000000000..5895a879f --- /dev/null +++ b/k9-ecosystem/spec/SPEC.adoc @@ -0,0 +1,120 @@ +// SPDX-License-Identifier: MPL-2.0 +// SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) += K9 Format Specification +:revnumber: 0.1.0-draft +:revdate: 2026-06-20 +:toc: left +:icons: font + +[IMPORTANT] +==== +*Status: DRAFT (v0.1).* This document is the working canonical specification for +the K9 format. Until it is ratified, `k9-rs` is the *normative reference +implementation*: where this text and `k9-rs` disagree, `k9-rs` wins and this +text is the bug. Open questions are marked `[OPEN]`. +==== + +== 1. Overview + +K9 is a self-validating configuration format for *software component +declarations*. A K9 document describes one component and the checks that prove +it is what it claims to be. + +Two surface syntaxes are defined: + +* *`.k9`* — a YAML-like, indentation-structured plain-text format, parsed + natively by every implementation. +* *`.k9.ncl`* — a https://nickel-lang.org[Nickel] document. Implementations + without a Nickel evaluator MUST detect this form and report it distinctly + (e.g. `k9-rs` returns `K9Error::NickelFormat`); evaluation is delegated to a + Nickel-aware tool such as `pandoc-k9`. + +Both surfaces denote the same abstract model (sections 3–6) and MUST produce +the same canonical projection (section 7). + +== 2. Document structure + +A document declares exactly one `component` with a name, a `version`, and the +sections below. Indentation is significant; two spaces per level is the +canonical style. + +[source,yaml] +---- +component: + version: + pedigree: { ... } # section 3 + security: # section 4 + recipe: { ... } # section 5 + contracts: [ ... ] # section 6 +---- + +`component` and `version` are REQUIRED. `pedigree`, `security`, `recipe`, and +`contracts` are each OPTIONAL but RECOMMENDED. + +== 3. Pedigree — provenance + +[cols="1,1,3", options="header"] +|=== +| Key | Required | Meaning +| `origin` | yes | Source URL of the component (HTTPS) +| `author` | yes | Responsible author or maintainer +| `license` | no | SPDX license identifier +| `hashes` | no | Supply-chain hashes (SHA-256+). `[OPEN]` exact key shape TBD +|=== + +MD5 and SHA-1 MUST NOT be used for `hashes`. + +== 4. SecurityLevel + +A single enumerated value describing the trust tier (shared with the K9 Nickel +component model): + +[cols="1,3", options="header"] +|=== +| Level | Meaning +| `kennel` | Most restricted — trusted, sandboxed, minimal surface +| `yard` | Intermediate — controlled exposure +| `hunt` | Least restricted — operates in the open +|=== + +`[OPEN]` Whether levels are case-insensitive on input is reference-defined; the +canonical projection (section 7) lower-cases them. + +== 5. Recipe — build / assembly + +[cols="1,1,3", options="header"] +|=== +| Key | Required | Meaning +| `tool` | yes | Build tool (e.g. `cargo`, `mix`, `gleam`, `deno`, `cabal`) +| `command` | yes | Arguments passed to the tool +|=== + +== 6. Contracts — runtime invariants + +`contracts` is an ordered list. Each entry: + +[cols="1,1,3", options="header"] +|=== +| Key | Required | Meaning +| `name` | yes | Stable identifier for the invariant +| `check` | yes | Shell command; exit code 0 = satisfied +| `severity` | yes | `error` \| `warn` \| `info` +|=== + +A K9 validator runs each `check`; a failing `error` contract fails validation, +`warn` and `info` are reported without failing. `[OPEN]` ordering/short-circuit +semantics across multiple failures to be pinned by the conformance suite. + +== 7. Canonical projection + +For cross-implementation conformance, a parsed document projects to canonical +JSON: object keys in declaration order, `security` lower-cased, `contracts` as a +JSON array, no implementation-specific fields. See +`../conformance/fixtures/` for worked examples. Every implementation MUST emit a +projection that is structurally equal to the fixture's `expected.json`. + +== 8. Versioning + +This spec is versioned independently of any implementation. Implementations +declare the spec version they target. Breaking changes bump the minor version +while in `0.x`. From d2300b855a5b81ae0844753ce14eae5ce7b8f19a Mon Sep 17 00:00:00 2001 From: Claude Date: Sat, 20 Jun 2026 18:19:43 +0000 Subject: [PATCH 2/2] 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 Claude-Session: https://claude.ai/code/session_019i2e5ABGBKQmMdqk8puFWx --- k9-ecosystem/.machine_readable/6a2/STATE.a2ml | 5 +- k9-ecosystem/conformance/README.adoc | 13 +- .../conformance/fixtures/minimal/component.k9 | 14 -- .../fixtures/minimal/component.k9.ncl | 33 +++++ .../fixtures/minimal/expected.json | 34 ++--- k9-ecosystem/spec/SPEC.adoc | 137 ++++++++---------- 6 files changed, 124 insertions(+), 112 deletions(-) delete mode 100644 k9-ecosystem/conformance/fixtures/minimal/component.k9 create mode 100644 k9-ecosystem/conformance/fixtures/minimal/component.k9.ncl diff --git a/k9-ecosystem/.machine_readable/6a2/STATE.a2ml b/k9-ecosystem/.machine_readable/6a2/STATE.a2ml index f220b8896..fb4d349e4 100644 --- a/k9-ecosystem/.machine_readable/6a2/STATE.a2ml +++ b/k9-ecosystem/.machine_readable/6a2/STATE.a2ml @@ -14,9 +14,12 @@ completion-percentage = 20 phase = "hub scaffolded and staged inside developer-ecosystem; awaiting extraction to standalone repo and live submodule wiring" [session-2026-06-20-hub-scaffold] -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." +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." files-added = 16 +[session-2026-06-20-dogfood-fix] +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." + [next-actions] extract = "Promote to standalone hyperpolymath/k9-ecosystem (BOOTSTRAP.md option A)" wire = "Run scripts/init-submodules.sh to populate the 11 member submodules" diff --git a/k9-ecosystem/conformance/README.adoc b/k9-ecosystem/conformance/README.adoc index 73ce25c88..134a5b420 100644 --- a/k9-ecosystem/conformance/README.adoc +++ b/k9-ecosystem/conformance/README.adoc @@ -12,16 +12,17 @@ contract that keeps five independent parsers (`k9-rs`, `k9_ex`, `k9_gleam`, conformance/ └── fixtures/ └── / - ├── component.k9 # input (also /component.k9.ncl where relevant) - └── expected.json # canonical projection (see ../spec/SPEC.adoc §7) + ├── component.k9.ncl # input (Nickel surface; or component.k9 for the plain surface) + └── expected.json # canonical projection (see ../spec/SPEC.adoc §6) ---- == Contract -For each fixture, an implementation parses `component.k9` and emits the canonical -JSON projection. The result MUST be *structurally equal* to `expected.json` -(key order per declaration order, `security` lower-cased). Whitespace and -insignificant formatting are ignored. +For each fixture, an implementation produces the canonical JSON projection from +the input — evaluating the Nickel surface (`.k9.ncl`) or parsing the plain +surface (`.k9`) — and the result MUST be *structurally equal* to `expected.json` +(records in declaration order; Nickel enum tags rendered as bare strings, e.g. +`'Kennel` -> `"Kennel"`). Whitespace and insignificant formatting are ignored. == Wiring an implementation diff --git a/k9-ecosystem/conformance/fixtures/minimal/component.k9 b/k9-ecosystem/conformance/fixtures/minimal/component.k9 deleted file mode 100644 index 261a89b37..000000000 --- a/k9-ecosystem/conformance/fixtures/minimal/component.k9 +++ /dev/null @@ -1,14 +0,0 @@ -component: hello-svc - version: 0.1.0 - pedigree: - origin: https://github.com/hyperpolymath/hello-svc - author: Jonathan D.A. Jewell - license: MPL-2.0 - security: kennel - recipe: - tool: cargo - command: build --release - contracts: - - name: binary-exists - check: test -x target/release/hello-svc - severity: error diff --git a/k9-ecosystem/conformance/fixtures/minimal/component.k9.ncl b/k9-ecosystem/conformance/fixtures/minimal/component.k9.ncl new file mode 100644 index 000000000..2b5e8413f --- /dev/null +++ b/k9-ecosystem/conformance/fixtures/minimal/component.k9.ncl @@ -0,0 +1,33 @@ +K9! +# SPDX-License-Identifier: MPL-2.0 +# SPDX-FileCopyrightText: 2026 Jonathan D.A. Jewell (hyperpolymath) +# minimal — the smallest valid K9 contract. Conformance fixture: every +# implementation must evaluate this and project it to expected.json. + +{ + pedigree = { + schema_version = "1.0.0", + security_level = 'Kennel, + + # L1 — The Snout: identity + metadata = { + name = "hello-svc", + version = "0.1.0", + breed = "application/vnd.k9+nickel", + magic_number = "K9!", + license = "MPL-2.0", + }, + + # L3 — The Leash: security posture + security = { + trust_level = 'Kennel, + allow_network = false, + allow_subprocess = false, + }, + + # L5 — The Muscle: recipes + recipes = { + build = "cargo build --release", + }, + }, +} diff --git a/k9-ecosystem/conformance/fixtures/minimal/expected.json b/k9-ecosystem/conformance/fixtures/minimal/expected.json index f1c01aef3..2592c4ff8 100644 --- a/k9-ecosystem/conformance/fixtures/minimal/expected.json +++ b/k9-ecosystem/conformance/fixtures/minimal/expected.json @@ -1,21 +1,21 @@ { - "component": "hello-svc", - "version": "0.1.0", "pedigree": { - "origin": "https://github.com/hyperpolymath/hello-svc", - "author": "Jonathan D.A. Jewell", - "license": "MPL-2.0" - }, - "security": "kennel", - "recipe": { - "tool": "cargo", - "command": "build --release" - }, - "contracts": [ - { - "name": "binary-exists", - "check": "test -x target/release/hello-svc", - "severity": "error" + "schema_version": "1.0.0", + "security_level": "Kennel", + "metadata": { + "name": "hello-svc", + "version": "0.1.0", + "breed": "application/vnd.k9+nickel", + "magic_number": "K9!", + "license": "MPL-2.0" + }, + "security": { + "trust_level": "Kennel", + "allow_network": false, + "allow_subprocess": false + }, + "recipes": { + "build": "cargo build --release" } - ] + } } diff --git a/k9-ecosystem/spec/SPEC.adoc b/k9-ecosystem/spec/SPEC.adoc index 5895a879f..c312030a2 100644 --- a/k9-ecosystem/spec/SPEC.adoc +++ b/k9-ecosystem/spec/SPEC.adoc @@ -8,113 +8,102 @@ [IMPORTANT] ==== -*Status: DRAFT (v0.1).* This document is the working canonical specification for -the K9 format. Until it is ratified, `k9-rs` is the *normative reference -implementation*: where this text and `k9-rs` disagree, `k9-rs` wins and this -text is the bug. Open questions are marked `[OPEN]`. +*Status: DRAFT (v0.1).* This text is reconstructed from the living K9 corpus +(the `*.k9.ncl` deployment components across the estate), the +`hyperpolymath/k9-validate-action` validator, and `k9-rs`. Where they disagree, +the *validator + `k9-rs` are normative* and this text is the bug. Open questions +are marked `[OPEN]`. ==== == 1. Overview K9 is a self-validating configuration format for *software component -declarations*. A K9 document describes one component and the checks that prove -it is what it claims to be. +declarations*. A K9 document describes one component — its identity, target, +security posture, self-checks, and recipes — so the file is both documentation +and an executable, authorisable contract. -Two surface syntaxes are defined: +Two surfaces are defined: -* *`.k9`* — a YAML-like, indentation-structured plain-text format, parsed - natively by every implementation. -* *`.k9.ncl`* — a https://nickel-lang.org[Nickel] document. Implementations - without a Nickel evaluator MUST detect this form and report it distinctly - (e.g. `k9-rs` returns `K9Error::NickelFormat`); evaluation is delegated to a - Nickel-aware tool such as `pandoc-k9`. +* *`.k9`* — plain-text surface. +* *`.k9.ncl`* — https://nickel-lang.org[Nickel] surface (the form used across the + estate today). Evaluation is delegated to a Nickel-aware tool (e.g. + `pandoc-k9`); parsers without an evaluator detect it distinctly (`k9-rs` + returns `K9Error::NickelFormat`). -Both surfaces denote the same abstract model (sections 3–6) and MUST produce -the same canonical projection (section 7). +== 2. File preamble (required) -== 2. Document structure - -A document declares exactly one `component` with a name, a `version`, and the -sections below. Indentation is significant; two spaces per level is the -canonical style. +[cols="1,3", options="header"] +|=== +| Rule | Requirement +| Magic | The *first non-empty line MUST be exactly* `K9!` +| Licence | An SPDX `License-Identifier` SHOULD appear within the first 10 lines +|=== -[source,yaml] ----- -component: - version: - pedigree: { ... } # section 3 - security: # section 4 - recipe: { ... } # section 5 - contracts: [ ... ] # section 6 ----- +The validator treats a missing magic line or a missing `pedigree` block as +*errors*; a missing SPDX line, `version`/`schema_version`, or security level as +*warnings*. -`component` and `version` are REQUIRED. `pedigree`, `security`, `recipe`, and -`contracts` are each OPTIONAL but RECOMMENDED. +== 3. The Pedigree -== 3. Pedigree — provenance +Every document MUST contain a `pedigree = { … }` record — the component's +self-description. Canonically it is organised in *five layers*: [cols="1,1,3", options="header"] |=== -| Key | Required | Meaning -| `origin` | yes | Source URL of the component (HTTPS) -| `author` | yes | Responsible author or maintainer -| `license` | no | SPDX license identifier -| `hashes` | no | Supply-chain hashes (SHA-256+). `[OPEN]` exact key shape TBD +| Layer | Key | Holds +| L1 — The Snout | `metadata` | Identity: `name`, `version`, `breed` (a MIME-like type, e.g. `application/vnd.k9+nickel`), `magic_number`, `license`, `description` +| L2 — The Scent | `target` | Environment: `os`, `requires_*`, resource minimums +| L3 — The Leash | `security` | Trust tier + capability flags (section 4) +| L4 — The Gut | `validation` | `checksum`, `pedigree_version`, authorisation state +| L5 — The Muscle | `recipes` | Named build/deploy/verify commands |=== -MD5 and SHA-1 MUST NOT be used for `hashes`. +The pedigree SHOULD carry a top-level `schema_version` (or `version`) and a +`security_level`/`leash` so it validates without warnings. `[OPEN]` whether +`security_level` is required at pedigree top level or may be inferred from +`security.trust_level`. -== 4. SecurityLevel +== 4. The Leash — trust levels -A single enumerated value describing the trust tier (shared with the K9 Nickel -component model): +`security.trust_level` (and the top-level `security_level`) take one of three +tiers, given as Nickel enum tags: [cols="1,3", options="header"] |=== | Level | Meaning -| `kennel` | Most restricted — trusted, sandboxed, minimal surface -| `yard` | Intermediate — controlled exposure -| `hunt` | Least restricted — operates in the open +| `'Kennel` | Most restricted — sandboxed, minimal capabilities, no side effects +| `'Yard` | Intermediate — controlled exposure +| `'Hunt` | Least restricted — may execute shell commands; REQUIRES explicit +authorisation (a signature / handshake) before execution |=== -`[OPEN]` Whether levels are case-insensitive on input is reference-defined; the -canonical projection (section 7) lower-cases them. - -== 5. Recipe — build / assembly - -[cols="1,1,3", options="header"] -|=== -| Key | Required | Meaning -| `tool` | yes | Build tool (e.g. `cargo`, `mix`, `gleam`, `deno`, `cabal`) -| `command` | yes | Arguments passed to the tool -|=== +Capability flags (`allow_network`, `allow_subprocess`, `allow_filesystem_write`, +…) further constrain the component. A `'Hunt` component MUST NOT run until +authorised (`validation.hunt_authorized = true` after signature verification). +Signatures are Ed25519. `[OPEN]` exact signature envelope. -== 6. Contracts — runtime invariants +== 5. Recipes -`contracts` is an ordered list. Each entry: +`recipes` is a record of named commands (e.g. `build`, `validate`, `deploy`, +`migrate`, `rollback`). Multi-line scripts use Nickel string blocks (`m%"…"%`). -[cols="1,1,3", options="header"] -|=== -| Key | Required | Meaning -| `name` | yes | Stable identifier for the invariant -| `check` | yes | Shell command; exit code 0 = satisfied -| `severity` | yes | `error` \| `warn` \| `info` -|=== +== 6. Surfaces and canonical projection -A K9 validator runs each `check`; a failing `error` contract fails validation, -`warn` and `info` are reported without failing. `[OPEN]` ordering/short-circuit -semantics across multiple failures to be pinned by the conformance suite. +For cross-implementation conformance, a document projects to canonical JSON: +the `pedigree` record (and any sibling top-level keys) as JSON, Nickel enum tags +rendered as their bare string (`'Kennel` → `"Kennel"`), records in declaration +order. A `.k9.ncl` document is evaluated first, then projected; a `.k9` document +is parsed natively. Both surfaces MUST yield the same projection. See +`../conformance/fixtures/` for worked examples. -== 7. Canonical projection +== 7. Conformance -For cross-implementation conformance, a parsed document projects to canonical -JSON: object keys in declaration order, `security` lower-cased, `contracts` as a -JSON array, no implementation-specific fields. See -`../conformance/fixtures/` for worked examples. Every implementation MUST emit a -projection that is structurally equal to the fixture's `expected.json`. +Every implementation MUST reproduce each fixture's `expected.json` from its +input. The fixtures are the executable form of this spec; an `[OPEN]` item is +closed by adding a fixture that pins the behaviour. == 8. Versioning -This spec is versioned independently of any implementation. Implementations +This spec is versioned independently of any implementation; implementations declare the spec version they target. Breaking changes bump the minor version while in `0.x`.