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

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
79 changes: 79 additions & 0 deletions .github/workflows/deed-conformance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
# SPDX-License-Identifier: MPL-2.0
# DEED conformance lane — STATUS: NOT WIRABLE FROM THIS REPO (measured 2026-10-25).
#
# The intended lane calls the estate's shared DEED linter (grammar v1.2.0,
# normative) over every committed *.deed file, with `require-files: true` so
# an empty corpus fails the gate rather than passing vacuously.
#
# MEASURED, twice, on PR #65:
# * pinned to master (metadatastician/standards@8e48b1c): `startup_failure`
# — no job, no log;
# * pinned to cf8f9121 (the commit whose actions.lock GitHub accepts, and
# where deed-conformance.yml exists; the same pin the secret-scanner lane
# runs successfully): `startup_failure` again.
# The pin is therefore not the variable. The variable is the org Actions
# allowlist: under the estate's `selected` actions posture, GitHub kills at
# startup any `uses:` of a reusable workflow that is not on the org's live
# `patterns_allowed` — zero jobs, no log (rhodium-standard-repositories/
# actions-allowlist/README.adoc documents the mechanism and its history).
# The secret-scanner reusable is on the org's live list; deed-conformance
# is not. That is an estate-admin change, not a repository change.
#
# The conformance evidence of record meanwhile:
# * both deeds pass the estate's normative deed_lint.js (grammar v1.2.0)
# — run under node, the same linter CI would run;
# * `estate / manifests` (the estate CI/CD audit) passes on this tree;
# * the doc-drift and e2e gates keep the surrounding docs honest.
#
# TO WIRE once the estate adds
# `metadatastician/standards/.github/workflows/deed-conformance.yml@<pinned-sha>`
# to the org's allowed patterns — restore the call (the owner spelling is
# load-bearing: the Actions resolver does not follow the estate rename,
# measured in #64):
#
# name: deed-conformance
#
# on:
# push:
# branches: [main]
# pull_request:
# workflow_dispatch:
#
# permissions:
# contents: read
#
# jobs:
# deed:
# permissions:
# contents: read
# uses: metadatastician/standards/.github/workflows/deed-conformance.yml@<accepted-pin>
# with:
# require-files: true

# A workflow file needs a valid `on:` — a trigger-less file makes GitHub
# manufacture a failing run attributed to the FILE path on every push (the
# same artifact documented in secret-scanner.yml). This dispatch-only stub
# keeps the file valid while the real lane stays unwired: dispatching it
# reports the measured status and exits 0.

name: deed-conformance

on:
workflow_dispatch:

permissions:
contents: read

jobs:
status:
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: report measured status
run: |
echo "deed-conformance: not wirable from this repository (measured 2026-10-25)."
echo "The org Actions allowlist does not yet list"
echo " metadatastician/standards/.github/workflows/deed-conformance.yml"
echo "so every pin dies at job setup with startup_failure, no job, no log."
echo "Intended lane: DEED linter (grammar v1.2.0, normative) over every committed *.deed file."
echo "See the measured record above; restore the call once the estate lists the reusable."
80 changes: 80 additions & 0 deletions .github/workflows/k9-conformance.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
# SPDX-License-Identifier: MPL-2.0
# K9 contract conformance lane — STATUS: NOT WIRABLE YET (measured 2026-10-25).
#
# The intended lane calls the estate's shared K9 validator over every
# committed K9 file (here: the six verb contracts under
# .machine_readable/contractiles/*/), typechecking them under a pinned
# Nickel (layer 2), not just the structural screen.
#
# MEASURED, not guessed: called on a pull request pinned to master
# (metadatastician/standards@8e48b1c, where k9-conformance.yml exists), the
# call was refused at job setup with `startup_failure` — no job, no log, the
# exact silent failure mode documented in secret-scanner.yml. The only pin
# GitHub has accepted in this repository so far is `cf8f9121` (the
# secret-scanner lane runs it), and k9-conformance.yml does not exist at
# that commit, so no re-pin can start this lane today. This file therefore
# declares no job: a lane that always reports startup_failure on every pull
# request is the failure mode that got a validator deleted instead of the
# files fixed (standards .machine_readable/k9-contract-debt.txt, preamble).
#
# The conformance evidence of record meanwhile:
# * all six verb contracts under .machine_readable/contractiles/*/ are the
# estate canon at 8e48b1c and conform under k9-validate.sh (contract
# v1.0.0, layer 1 structural);
# * layer 2 (Nickel typecheck) and layer 3 (Ed25519) were NOT run locally —
# skipped, never passed;
# * the contractile set itself is gated by tests/e2e.sh structure checks.
#
# TO WIRE when the estate publishes an accepted pin carrying
# k9-conformance.yml — restore the call (owner spelling is load-bearing:
# metadatastician/standards; the Actions resolver does not follow the estate
# rename, measured in #64):
#
# name: k9-conformance
#
# on:
# push:
# branches: [main]
# pull_request:
# workflow_dispatch:
#
# permissions:
# contents: read
# pull-requests: read
#
# jobs:
# k9:
# permissions:
# contents: read
# pull-requests: read
# uses: metadatastician/standards/.github/workflows/k9-conformance.yml@<accepted-pin>
# with:
# require-files: true

# A workflow file needs a valid `on:` — a trigger-less file makes GitHub
# manufacture a failing run attributed to the FILE path on every push (the
# same artifact documented in secret-scanner.yml). This dispatch-only stub
# keeps the file valid while the real lane stays unwired: dispatching it
# reports the measured status and exits 0.

name: k9-conformance

on:
workflow_dispatch:

permissions:
contents: read

jobs:
status:
runs-on: ubuntu-latest
timeout-minutes: 2
steps:
- name: report measured status
run: |
echo "k9-conformance: not wirable from this repository (measured 2026-10-25)."
echo "The org Actions allowlist does not yet list"
echo " metadatastician/standards/.github/workflows/k9-conformance.yml"
echo "so every pin dies at job setup with startup_failure, no job, no log."
echo "Intended lane: estate K9 validator over the six verb contracts under .machine_readable/contractiles/*/."
echo "See the measured record above; restore the call once the estate lists the reusable."
17 changes: 3 additions & 14 deletions .machine_readable/contractiles/INDEX.a2ml
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,9 @@

---
id = "contractiles-registry"
version = "2.0.0" # 2.0.0 (2026-04-18): all 6 verbs on trident shape; verb set complete.
version = "2.1.0" # 2.1.0 (2026-10-05): removed k9 exception per ADR-001; registry now contains only 6 verbs with no exceptions.
spec = "docs/CONTRACTILE-SPEC.adoc"
last_updated = "2026-04-18"
last_updated = "2026-10-05"
base_schema = ".machine_readable/contractiles/_base.ncl"
meta_schema_status = "pending — see CONTRACTILE-SPEC §validator-meta-schema"

Expand Down Expand Up @@ -82,18 +82,7 @@ gating = "non-gating (continue)"
cardinality = "one per repo"
notes = "First trident instance in the estate (2026-04-18). Reports progress toward committed next-actions AND lists horizon aspirations. Absorbed the deprecated `lust` verb 2026-04-18. Never blocks. Remaining 5 verbs still on file_pair shape until tridents are built."

[[verbs]]
name = "k9"
semantics = "trust-tier templates (EXCEPTION to one-verbfile rule)"
file_pair = [
"k9/template-hunt.k9.ncl",
"k9/template-kennel.k9.ncl",
"k9/template-yard.k9.ncl",
]
status = "exception"
gating = "not applicable"
notes = "k9 is service-automation meta-infrastructure, not a verb contractile. Three trust-tier templates (Kennel/Yard/Hunt). Does not have a Verbfile.a2ml. See CONTRACTILE-SPEC §k9-exception."

# [[verbs]] k9 REMOVED 2026-10-05 — relocated by ADR-001 to .machine_readable/svc/k9/; not a verb contractile
# [[verbs]] lust REMOVED 2026-04-18 — name had unwanted associations;
# the horizon/aspiration semantics were always meant to live inside `intend`
# (the north-star verb). The [[wishes]] schema was absorbed into
Expand Down
68 changes: 17 additions & 51 deletions .machine_readable/contractiles/README.adoc
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ Each verb directory holds a *trident* of files:

* `<Verb>file.a2ml` — the project-specific declaration (data)
* `<verb>.ncl` — the paired Nickel runner (pedigree + schema + run policy)
* `<verb>.k9.ncl` — the k9 service-automation component (trust-tiered)
* `<verb>.k9.ncl` — the verb's K9 contract (pedigree, leash, probes; imports `../_base.ncl`)

A per-verb `<verb>.manifest.a2ml` asserts that exactly these three files
constitute the trident, pins their content-hashes, and requires the
Expand All @@ -31,7 +31,7 @@ probe-schema). The `INDEX.a2ml` registry catalogues every verb; consumers
available verbs rather than hard-coding the list. See
`docs/CONTRACTILE-SPEC.adoc` for the normative specification.

== Verbs (6 + k9 exception)
== Verbs (6)

[cols="1,2,3", options="header"]
|===
Expand Down Expand Up @@ -70,57 +70,24 @@ associations). Its [[wishes]] semantics live inside `intend/Intentfile.a2ml`
as a second section alongside [[intents]]. Any `lust/` dir encountered in
an estate repo is drift and should be removed.

== k9 — Service-Automation Layer (EXCEPTION to the one-verbfile rule)
[[k9-relocation]]
== k9 Service-Automation Layer (Relocated)

IMPORTANT: `k9/` is **not a contractile verb** and does NOT follow the
`<Verb>file.a2ml` + `<verb>.ncl` pattern. This is an intentional, documented
exception. Do not apply the naming rule to k9.
IMPORTANT: k9 is **not a contractile verb**. As of ADR-001 (2026-04-18),
k9 has been relocated from this directory to `.machine_readable/svc/k9/`
estate-wide. The `k9/` directory entry in this location is a **signpost
only**.

=== Why k9 is different
k9 provides three trust-tier templates (Kennel, Yard, Hunt) that repos
copy and instantiate. It is service-automation meta-infrastructure, not a
verb contractile, and therefore does not appear in the contractile
registry (`INDEX.a2ml`). See ADR-001 for the relocation rationale and
`.machine_readable/svc/k9/README.adoc` for the full k9 security model.

The six verb contractiles each declare *one concern per repo* in a single
xfile. k9 is not a concern; it is the *graded automation surface* that
enforces or validates concern declarations. k9 provides three trust-tier
*templates* that repos copy and instantiate:

[cols="1,1,3", options="header"]
|===
| File | Trust tier | Description

| `k9/template-kennel.k9.ncl`
| Kennel
| Pure data. No subprocess, no filesystem write, no network. Safe for
metadata and declarative settings.

| `k9/template-yard.k9.ncl`
| Yard
| Nickel evaluation with contracts and validation. No side effects.

| `k9/template-hunt.k9.ncl`
| Hunt
| Full execution surface. Must declare side effects, support dry-run, and
be signed before the estate treats it as trustworthy automation.
|===

Each verb's `<verb>.k9.ncl` instantiates one of these tiers (the runners in
this set import `../k9/template-hunt.k9.ncl`).

=== Why the naming rule does not apply

The one-verb-one-Verbfile rule exists to enforce clean concern separation.
k9 is meta-infrastructure: it does not have a `K9file.a2ml` because it is
not a declarative xfile — it is a template set that instantiates into
specific repos. Applying the rule would produce a meaningless `K9file.a2ml`
with nothing to declare.

=== Audit rule

If a repo claims `k9` enforcement, each k9 component in that repo MUST
declare a `paired_xfile` pointing to a specific contractile xfile (e.g.
`../must/Mustfile.a2ml`). Floating k9 components with no paired xfile are
non-conformant.

See `docs/CONTRACTILE-SPEC.adoc §k9-exception` for the normative statement.
The per-verb `<verb>.k9.ncl` contracts in this set are K9 *contracts*
(pedigree, leash, probes) validated by the estate's
`1-formats/k9/tools/k9-validate.sh`; they no longer import any
`k9/template-*.k9.ncl` file — the pedigree schema lives in `_base.ncl`.

== Fill-In Instructions

Expand All @@ -137,7 +104,6 @@ When copying this set into a new repo:
6. `Bustfile` — declare real breakage / expiry / hard-stop conditions.
7. `Intentfile` — list tracked next-actions with observable probes
([[intents]] section) AND horizon aspirations ([[wishes]] section).
8. Pair any `k9/*.k9.ncl` with a specific contractile via `paired_xfile`.

== Intentfile: Commitments vs Aspirations — Two Sections, One File

Expand Down
17 changes: 9 additions & 8 deletions .machine_readable/contractiles/_base.ncl
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
# SPDX-License-Identifier: MPL-2.0
# (MPL-2.0 is automatic legal fallback until PMPL is formally recognised)
#
# _base.ncl — Shared contractile base
#
Expand Down Expand Up @@ -37,7 +38,6 @@
# }
#
# See: docs/CONTRACTILE-SPEC.adoc §Shared Base

{
# -------------------------------------------------------------------------
# pedigree_schema
Expand All @@ -49,11 +49,11 @@
# -------------------------------------------------------------------------
pedigree_schema = {
schema_version | String | default = "1.0.0",
contractile_verb | String | default = "UNSET", # MUST override in verb
semantics | String | default = "UNSET", # MUST override in verb
contractile_verb | String | default = "UNSET", # MUST override in verb
semantics | String | default = "UNSET", # MUST override in verb
security = {
leash | [| 'Kennel, 'Yard, 'Hunt |] | default = 'Kennel,
trust_level | String | default = "UNSET", # MUST override in verb
trust_level | String | default = "UNSET", # MUST override in verb
allow_network | Bool | default = false,
allow_filesystem_write | Bool | default = false,
allow_subprocess | Bool | default = true,
Expand All @@ -62,10 +62,10 @@
# destructive_mode_requires_flag (dust)
},
metadata = {
name | String | default = "UNSET", # MUST override in verb
name | String | default = "UNSET", # MUST override in verb
version | String | default = "1.0.0",
description | String | default = "UNSET", # MUST override in verb
paired_xfile | String | default = "UNSET", # MUST override in verb
description | String | default = "UNSET", # MUST override in verb
paired_xfile | String | default = "UNSET", # MUST override in verb
author | String | default = "Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>",
},
},
Expand Down Expand Up @@ -117,7 +117,8 @@
allowed_exit_codes | Array Number | default = [0],
permission_class
| [| 'read_only, 'filesystem_write, 'subprocess, 'network |]
| default = 'read_only,
| default
= 'read_only,
},

# -------------------------------------------------------------------------
Expand Down
35 changes: 31 additions & 4 deletions .machine_readable/contractiles/adjust/adjust.k9.ncl
Original file line number Diff line number Diff line change
Expand Up @@ -22,11 +22,22 @@ K9!
# erosion causing gradual parameter drift). Where must flags "broken
# now", adjust flags "drifting toward broken".

let base_k9 = import "../k9/template-hunt.k9.ncl" in
let base = import "../_base.ncl" in
# §11.4/K9-S013 — `pedigree_schema` lives in ../_base.ncl. There is no k9/
# subdirectory under .machine_readable/contractiles/, so the old
# `import "../k9/template-hunt.k9.ncl"` never resolved — and the pedigree a
# reviewer read was not the pedigree a host would evaluate.
let base = import "../_base.ncl" in

{
pedigree = base_k9.pedigree_schema & {
pedigree = base.pedigree_schema & {
# §5.2/§6.2 — schema_version, and the reserved-prefix component_type. The
# `contractile:` prefix is the whole of the disambiguation between this
# family's verbs (must/trust/dust/intend/...) and K9's own execution triad
# (must/just/nickel): the K9 contract never names a contractile verb, it
# only ever sees this string.
schema_version = "1.0.0",
component_type = "contractile:adjust",
semantics = "drift-tolerance-and-corrective-action",
contractile_verb = "adjust",
paired_xfile = "../adjust/Adjustfile.a2ml",
paired_runner = "../adjust/adjust.ncl",
Expand All @@ -49,11 +60,27 @@ let base = import "../_base.ncl" in
allow_network = false,
allow_filesystem_write_conditional = true, # auto_fix_when_available may edit
allow_subprocess = true,
# §8.3/§8.4 — the grant. Each flag above is a REQUEST for a capability,
# and a request the grant does not cover is a contradiction inside the
# pedigree, not a preference (K9-S007). process.spawn pays for
# allow_subprocess; fs.write pays for the conditional write path, which
# K9 can only express as the whole capability. At 'Yard the leash is
# still the cap: §7.3 leaves a Yard component unable to write or spawn,
# so these entries describe the request, not a permission in force.
capabilities = [
"fs.write",
"process.spawn",
],
probe_scope = 'measurement_plus_declared_fix,
},
},

variance_schema = {
# A contract, not payload. The fields below are declarations with no
# definition, and Nickel's export refuses to serialize a record containing
# one ("missing field definition for `entry_id`"). `not_exported` says what
# is true: this block documents the variance shape and is not data to
# serialize. Reading `variance_schema` as a contract is unaffected.
variance_schema | not_exported = {
entry_id | String,
reason | String,
approved_by | String,
Expand Down
Loading
Loading