Skip to content

Commit fab507c

Browse files
hyperpolymathclaude
andcommitted
feat(contractiles): CONTRACTILE-SPEC + _base.ncl + validator refactor
- New CONTRACTILE-SPEC.adoc (normative spec, ~430 lines) - Extracted _base.ncl (shared pedigree_schema + run_defaults + probe_schema) - Refactored 7 verb validators to import from _base.ncl - Populated empty Bustfile.a2ml with seed skeleton (was 1 byte) - Populated Intendfile.a2ml with valid skeleton (3 example intents) - Documented k9 exception thoroughly in contractiles/README.adoc - New INDEX.a2ml registry listing all 8 verbs + k9 exception - All 7 verb .ncl files pass nickel typecheck Legacy String probes kept with TODO comments; probe_schema is spec-only pending CLI migration. Composition primitive deferred (spec only). Intentfile rename: already completed prior to this session. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
1 parent d149a4f commit fab507c

13 files changed

Lines changed: 1125 additions & 60 deletions

File tree

Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# INDEX.a2ml — Contractile Registry
3+
# Author: Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>
4+
#
5+
# Machine-readable catalogue of all contractile verbs in this template set.
6+
# Consumers (CI scripts, the contractile CLI, Hypatia rules) SHOULD read this
7+
# file to discover available verbs rather than hard-coding the list.
8+
#
9+
# See: docs/CONTRACTILE-SPEC.adoc §Registry
10+
11+
---
12+
id = "contractiles-registry"
13+
version = "1.1.0"
14+
spec = "docs/CONTRACTILE-SPEC.adoc"
15+
last_updated = "2026-04-17"
16+
base_schema = ".machine_readable/contractiles/_base.ncl"
17+
meta_schema_status = "pending — see CONTRACTILE-SPEC §validator-meta-schema"
18+
19+
## Verbs
20+
21+
[[verbs]]
22+
name = "adjust"
23+
semantics = "accessibility compliance"
24+
file_pair = [
25+
"adjust/Adjustfile.a2ml",
26+
"adjust/adjust.ncl",
27+
]
28+
status = "active"
29+
gating = "advisory (continue-with-warnings)"
30+
notes = "auto_fix_when_available applies deterministic patches; advisory otherwise"
31+
32+
[[verbs]]
33+
name = "bust"
34+
semantics = "error handling + failure recovery"
35+
file_pair = [
36+
"bust/Bustfile.a2ml",
37+
"bust/bust.ncl",
38+
]
39+
status = "active"
40+
gating = "hard (exit-nonzero)"
41+
notes = "injects failures via declared probes and verifies recovery paths"
42+
43+
[[verbs]]
44+
name = "dust"
45+
semantics = "exnovation / removal"
46+
file_pair = [
47+
"dust/Dustfile.a2ml",
48+
"dust/dust.ncl",
49+
]
50+
status = "active"
51+
gating = "advisory (continue-with-warnings)"
52+
notes = "destructive mode gated behind --apply flag + explicit item approval"
53+
54+
[[verbs]]
55+
name = "intend"
56+
semantics = "north-star / aspirational"
57+
file_pair = [
58+
"intend/Intendfile.a2ml",
59+
"intend/intend.ncl",
60+
]
61+
status = "active"
62+
gating = "non-gating (continue)"
63+
notes = "reports progress toward committed next-actions; never blocks"
64+
65+
[[verbs]]
66+
name = "k9"
67+
semantics = "trust-tier templates (EXCEPTION to one-verbfile rule)"
68+
file_pair = [
69+
"k9/template-hunt.k9.ncl",
70+
"k9/template-kennel.k9.ncl",
71+
"k9/template-yard.k9.ncl",
72+
]
73+
status = "exception"
74+
gating = "not applicable"
75+
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."
76+
77+
[[verbs]]
78+
name = "lust"
79+
semantics = "aspirational / wishlist / horizon"
80+
file_pair = [
81+
"lust/Lustfile.a2ml",
82+
"lust/lust.ncl",
83+
]
84+
status = "active"
85+
gating = "non-gating (continue)"
86+
notes = "horizon-grouped wishes; distinct from intend (commitment). Never blocks."
87+
88+
[[verbs]]
89+
name = "must"
90+
semantics = "invariant assertion"
91+
file_pair = [
92+
"must/Mustfile.a2ml",
93+
"must/must.ncl",
94+
]
95+
status = "active"
96+
gating = "hard (exit-nonzero)"
97+
notes = "hard gate; single failure blocks merge. Simplest and most commonly populated verb."
98+
99+
[[verbs]]
100+
name = "trust"
101+
semantics = "security + provenance + safe-hacking"
102+
file_pair = [
103+
"trust/Trustfile.a2ml",
104+
"trust/trust.ncl",
105+
]
106+
status = "active"
107+
gating = "hard (exit-nonzero)"
108+
notes = "security verifications + authorised safe-hacking probes scoped to the repo under test"

.machine_readable/contractiles/README.adoc

Lines changed: 50 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,10 @@ Anything else in a verb directory is human-only notes or archive; machines
1717
ignore it. Filenames use lowercase verb in the `.ncl` name and PascalCase
1818
verb in the A2ML (e.g. `intend.ncl` + `Intendfile.a2ml`).
1919

20-
== Verbs (7)
20+
All verb runners import `_base.ncl` (shared pedigree + run-defaults + probe-schema).
21+
See `docs/CONTRACTILE-SPEC.adoc` for the normative specification.
22+
23+
== Verbs (7 + k9 exception)
2124

2225
[cols="1,2,3", options="header"]
2326
|===
@@ -55,23 +58,55 @@ verb in the A2ML (e.g. `intend.ncl` + `Intendfile.a2ml`).
5558
`intend` — `lust` is wish, `intend` is commitment. Non-gating.
5659
|===
5760

58-
== k9 — Service-Automation Layer (parallel to verbs)
61+
== k9 — Service-Automation Layer (EXCEPTION to the one-verbfile rule)
62+
63+
IMPORTANT: `k9/` is **not a contractile verb** and does NOT follow the
64+
`<Verb>file.a2ml` + `<verb>.ncl` pattern. This is an intentional, documented
65+
exception. Do not apply the naming rule to k9.
66+
67+
=== Why k9 is different
68+
69+
The seven verb contractiles each declare *one concern per repo* in a single
70+
xfile. k9 is not a concern; it is the *graded automation surface* that
71+
enforces or validates concern declarations. k9 provides three trust-tier
72+
*templates* that repos copy and instantiate:
73+
74+
[cols="1,1,3", options="header"]
75+
|===
76+
| File | Trust tier | Description
77+
78+
| `k9/template-kennel.k9.ncl`
79+
| Kennel
80+
| Pure data. No subprocess, no filesystem write, no network. Safe for
81+
metadata and declarative settings.
82+
83+
| `k9/template-yard.k9.ncl`
84+
| Yard
85+
| Nickel evaluation with contracts and validation. No side effects.
86+
87+
| `k9/template-hunt.k9.ncl`
88+
| Hunt
89+
| Full execution surface. Must declare side effects, support dry-run, and
90+
be signed before the estate treats it as trustworthy automation.
91+
|===
92+
93+
=== Why the naming rule does not apply
94+
95+
The one-verb-one-Verbfile rule exists to enforce clean concern separation.
96+
k9 is meta-infrastructure: it does not have a `K9file.a2ml` because it is
97+
not a declarative xfile — it is a template set that instantiates into
98+
specific repos. Applying the rule would produce a meaningless `K9file.a2ml`
99+
with nothing to declare.
59100

60-
`k9/` is **not** a verb. It sits alongside the verb templates and provides
61-
the graded automation surface that validates or enforces the verb
62-
declarations. Three trust tiers, lowest to highest authority:
101+
=== Audit rule
63102

64-
* `k9/template-kennel.k9.ncl` — **Kennel**. Pure data. No subprocess, no
65-
filesystem write, no network. Safe for metadata and declarative settings.
66-
* `k9/template-yard.k9.ncl` — **Yard**. Nickel evaluation with contracts and
67-
validation, but no side effects.
68-
* `k9/template-hunt.k9.ncl` — **Hunt**. Full execution surface. Must declare
69-
side effects, support dry-run, and be signed before the estate treats it
70-
as trustworthy automation.
103+
If a repo claims `k9` enforcement, each k9 component in that repo MUST
104+
declare a `paired_xfile` pointing to a specific contractile xfile (e.g.
105+
`../must/Mustfile.a2ml`). Floating k9 components with no paired xfile are
106+
non-conformant.
71107

72-
Audit rule: if a repo claims `k9` enforcement, each k9 component must
73-
correspond to a stated contractile (via the `paired_xfile` field), not float
74-
as an unrelated demo.
108+
See `k9/README.adoc` for the full k9 security model and usage instructions.
109+
See `docs/CONTRACTILE-SPEC.adoc §k9-exception` for the normative statement.
75110

76111
== Fill-In Instructions
77112

Lines changed: 139 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,139 @@
1+
# SPDX-License-Identifier: PMPL-1.0-or-later
2+
# (MPL-2.0 is automatic legal fallback until PMPL is formally recognised)
3+
#
4+
# _base.ncl — Shared contractile base
5+
#
6+
# Provides four named schema fragments imported by every verb runner:
7+
#
8+
# pedigree_schema — canonical pedigree block shape
9+
# status_core_doc — documentation of the shared status trio (String list)
10+
# probe_schema — target structured probe form (spec only; verb files
11+
# still use probe | String with TODO comments)
12+
# run_defaults — default runner behaviour
13+
#
14+
# Usage in a verb runner:
15+
#
16+
# let base = import "../_base.ncl" in
17+
# {
18+
# pedigree = base.pedigree_schema & {
19+
# contractile_verb = "must",
20+
# semantics = "invariant",
21+
# security = {
22+
# leash = 'Kennel,
23+
# trust_level = "read-only verification",
24+
# allow_network = false,
25+
# allow_filesystem_write = false,
26+
# allow_subprocess = true,
27+
# },
28+
# metadata = {
29+
# name = "must-runner",
30+
# version = "1.0.0",
31+
# description = "...",
32+
# paired_xfile = "Mustfile.a2ml",
33+
# author = "Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>",
34+
# },
35+
# },
36+
# schema = { ... },
37+
# run = base.run_defaults & { on_any_fail = "exit-nonzero" },
38+
# }
39+
#
40+
# See: docs/CONTRACTILE-SPEC.adoc §Shared Base
41+
42+
{
43+
# -------------------------------------------------------------------------
44+
# pedigree_schema
45+
#
46+
# The canonical shape of the `pedigree` block required in every verb runner.
47+
# Verb runners merge this with their verb-specific values using Nickel's `&`
48+
# (right-priority merge). Override contractile_verb, semantics, security.*,
49+
# and metadata.* in each verb.
50+
# -------------------------------------------------------------------------
51+
pedigree_schema = {
52+
schema_version | String | default = "1.0.0",
53+
contractile_verb | String | default = "UNSET", # MUST override in verb
54+
semantics | String | default = "UNSET", # MUST override in verb
55+
security = {
56+
leash | [| 'Kennel, 'Yard, 'Hunt |] | default = 'Kennel,
57+
trust_level | String | default = "UNSET", # MUST override in verb
58+
allow_network | Bool | default = false,
59+
allow_filesystem_write | Bool | default = false,
60+
allow_subprocess | Bool | default = true,
61+
# verb-specific additional security fields go in the verb's merge override:
62+
# e.g. authorised_probes_only (trust), injection_scope (bust),
63+
# destructive_mode_requires_flag (dust)
64+
},
65+
metadata = {
66+
name | String | default = "UNSET", # MUST override in verb
67+
version | String | default = "1.0.0",
68+
description | String | default = "UNSET", # MUST override in verb
69+
paired_xfile | String | default = "UNSET", # MUST override in verb
70+
author | String | default = "Jonathan D.A. Jewell <j.d.a.jewell@open.ac.uk>",
71+
},
72+
},
73+
74+
# -------------------------------------------------------------------------
75+
# status_core_doc
76+
#
77+
# Documents the minimum shared status values present in every verb's status
78+
# enum: declared, verified, failing.
79+
#
80+
# Nickel does not support structural enum extension, so verb files reproduce
81+
# their full enum verbatim in `schema`. This field serves as documentation
82+
# and for tooling that introspects the base.
83+
#
84+
# Verbs that extend status_core (i.e. all except must + trust):
85+
# adjust: + 'partial
86+
# bust: + 'drilled
87+
# dust: 'declared, 'proposed, 'approved, 'removed (non-standard)
88+
# intend: 'declared, 'in_progress, 'done, 'deferred, 'retired
89+
# lust: 'declared, 'in_progress, 'achieved, 'abandoned
90+
#
91+
# See: docs/CONTRACTILE-SPEC.adoc §Per-Verb Extension
92+
# -------------------------------------------------------------------------
93+
status_core_doc = "status_core values: declared | verified | failing — extended per verb",
94+
95+
# -------------------------------------------------------------------------
96+
# probe_schema
97+
#
98+
# The TARGET structured probe form. See: docs/CONTRACTILE-SPEC.adoc §Probe
99+
#
100+
# IMPORTANT: This is a spec-only definition. Existing verb runner files still
101+
# use `probe | String` with a `# TODO: migrate to probe_schema` comment.
102+
# This is a breaking change; migration happens when the CLI supports both
103+
# forms.
104+
#
105+
# Adopters writing new xfiles should prefer the structured form:
106+
# probe = {
107+
# command = "test -f my-file",
108+
# timeout_seconds = 60,
109+
# allowed_exit_codes = [0],
110+
# permission_class = 'read_only,
111+
# }
112+
# -------------------------------------------------------------------------
113+
probe_schema = {
114+
command | String,
115+
timeout_seconds | Number | default = 300,
116+
allowed_exit_codes | Array Number | default = [0],
117+
permission_class
118+
| [| 'read_only, 'filesystem_write, 'subprocess, 'network |]
119+
| default = 'read_only,
120+
},
121+
122+
# -------------------------------------------------------------------------
123+
# run_defaults
124+
#
125+
# Default runner behaviour. Verb runners merge this with verb-specific
126+
# overrides using Nickel's `&` (right-priority merge).
127+
#
128+
# Most verbs override on_any_fail:
129+
# "exit-nonzero" : hard gate (must, trust, bust, adjust-gating)
130+
# "continue-with-warnings": advisory (dust, adjust)
131+
# "continue" : never gate (intend, lust)
132+
# -------------------------------------------------------------------------
133+
run_defaults = {
134+
on_pass = "continue",
135+
on_any_fail = "exit-nonzero",
136+
report_format = "a2ml",
137+
emit_summary = true,
138+
},
139+
}

.machine_readable/contractiles/adjust/adjust.ncl

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -9,10 +9,14 @@
99
# `contractile adjust fix` → apply deterministic fixes where defined
1010
#
1111
# Anything else in this directory is human-only notes/archive; machines ignore.
12+
#
13+
# Base: ../_base.ncl provides pedigree_schema, run_defaults, probe_schema.
14+
# See: docs/CONTRACTILE-SPEC.adoc
15+
16+
let base = import "../_base.ncl" in
1217

1318
{
14-
pedigree = {
15-
schema_version = "1.0.0",
19+
pedigree = base.pedigree_schema & {
1620
contractile_verb = "adjust",
1721
semantics = "accessibility compliance",
1822
security = {
@@ -36,16 +40,20 @@
3640
| Array {
3741
id | String,
3842
description | String,
43+
# TODO: migrate to base.probe_schema (structured probe) when CLI supports it
3944
probe | String,
45+
# status_core values: 'declared, 'verified, 'failing; adjust adds 'partial
4046
status | [| 'declared, 'partial, 'verified, 'failing |] | default = 'declared,
4147
compliance | String | optional, # e.g. "WCAG 2.1 AA"
4248
notes | String | optional,
4349
fix | String | optional, # deterministic fix command (optional)
4450
},
4551
},
4652

47-
run = {
48-
on_pass = "continue",
53+
# Runner behaviour — inherits from base.run_defaults.
54+
# adjust is advisory (continue-with-warnings) not a hard gate.
55+
# auto_fix_when_available is adjust-specific.
56+
run = base.run_defaults & {
4957
on_any_fail = "continue-with-warnings", # accessibility is progress-tracked, not a hard gate by default
5058
report_format = "a2ml",
5159
emit_summary = true,

0 commit comments

Comments
 (0)