diff --git a/.machine_readable/template-capability-gates.toml b/.machine_readable/template-capability-gates.toml index 0f6a6194..d99d652d 100644 --- a/.machine_readable/template-capability-gates.toml +++ b/.machine_readable/template-capability-gates.toml @@ -46,6 +46,8 @@ paths = ["README.adoc", "EXPLAINME.adoc", "LICENSE", "SECURITY.md", "CONTRIBUTIN "MAINTAINERS.adoc" = "governance-tier" [presets] +# OPTIONAL shorthands. A profile may declare `capabilities = [...]` directly +# instead of a preset; presets are pure sugar that expands to a capability set. # preset name = [ base capabilities ] rust-cli = ["rust", "cli", "library"] rust-ffi-lib = ["rust", "zig", "ffi", "abi", "library"] diff --git a/TEMPLATE-APPLICABILITY-POLICY.adoc b/TEMPLATE-APPLICABILITY-POLICY.adoc index 72e73895..8addb6bd 100644 --- a/TEMPLATE-APPLICABILITY-POLICY.adoc +++ b/TEMPLATE-APPLICABILITY-POLICY.adoc @@ -27,20 +27,20 @@ makes "carry it only if you need it" a *rule with a test*, not a judgement call. == Model -Three layers: - . *Universal baseline* — modules every repo carries unconditionally (identity, community-health, machine-readable state, the licence invariant). -. *Capability gates* — every other template module is tagged with the - *capability* it serves. A repo carries the module **iff** it declares that - capability. -. *Presets* — named capability bundles (`rust-cli`, `rust-ffi-lib`, - `rust-service`, `formal-proof-lib`, `docs-site`, …) a repo adopts as its base, - then adjusts with explicit per-capability `add` / `remove`. (The "hybrid" - layer: presets for ergonomics, gates for precision.) - -A repo's *profile* (`.machine_readable/rsr-profile.a2ml`) declares its preset -and any adjustments. Its effective capability set drives everything else. +. *Capability gates (the model)* — every other template module is tagged with + the *capability* it serves. A repo *declares its capabilities* and carries a + module **iff** it declares that module's gating capability. +. *Presets (optional sugar)* — named capability bundles (`rust-cli`, + `rust-ffi-lib`, `rust-service`, `formal-proof-lib`, `docs-site`, …). A repo + *may* adopt a preset as a shorthand for its base capabilities instead of + listing them, then adjust with `add` / `remove`. Presets are pure ergonomics + over the gate model — never required. + +A repo's *profile* (`.machine_readable/rsr-profile.a2ml`) declares its +capabilities (directly, or via a preset + adjustments). Its effective +capability set drives everything else. == Capability taxonomy @@ -114,7 +114,10 @@ and the SPDX licence invariant (`LICENCE-POLICY.adoc`). | `.github/workflows/release.yml`, registry metadata | `published-package` |=== -== Presets +== Presets (optional) + +Presets are optional shorthands — a repo can always declare `capabilities` +directly instead. Each expands to a base capability set: [cols="1,3"] |=== @@ -153,21 +156,33 @@ that justifies it. That is the thing this policy removes. == Per-repo profile -`.machine_readable/rsr-profile.a2ml` (A2ML, TOML-like; SPDX `MPL-2.0`): +`.machine_readable/rsr-profile.a2ml` (A2ML, TOML-like; SPDX `MPL-2.0`). Declare +capabilities directly (primary form): [source,toml] ---- [profile] -preset = "rust-cli" -add = [] # extra capabilities beyond the preset -remove = [] # preset capabilities this repo does NOT have +capabilities = ["rust", "cli", "library"] [rationale] -# One line per non-obvious add/remove or declined capability. +# One line per non-obvious capability or declined module. no-abi = "language-agnostic engine; no C-ABI seam, no Idris2 ABI proofs" no-formal-proofs = "consumes Agda (shells out); contains no in-tree proofs" ---- +Or, equivalently, adopt a preset (optional shorthand) and adjust: + +[source,toml] +---- +[profile] +preset = "rust-cli" # = ["rust", "cli", "library"] +add = ["published-package"] # extra capabilities beyond the preset +remove = [] # preset capabilities this repo does NOT have +---- + +`preset`, `capabilities`, `add`, and `remove` all union (then `remove` +subtracts); at least one of `capabilities` / `preset` is required. + == Enforcement (how this evolves the existing machinery) `root-allow.txt` becomes *derived*, not hand-maintained as one shape: diff --git a/scripts/check-rsr-profile.sh b/scripts/check-rsr-profile.sh index 6f9396b0..35d34e78 100755 --- a/scripts/check-rsr-profile.sh +++ b/scripts/check-rsr-profile.sh @@ -30,18 +30,25 @@ section() { awk -v s="[$1]" '$0==s{f=1;next} /^\[/{f=0} f && !/^[[:space:]]*#/ & quoted_on_key() { grep -E "^[[:space:]]*$1[[:space:]]*=" | grep -oE '"[^"]+"' | tr -d '"' || true; } # --- target profile --- +# Capability declaration is primary: a profile may list `capabilities = [...]` +# directly. `preset` is optional sugar — a named bundle expanded from the gate +# data. A profile must provide at least one of the two. PBODY="$(section profile "$PROFILE")" PRESET="$(printf '%s\n' "$PBODY" | quoted_on_key preset | sed -n 1p)" +DIRECT="$(printf '%s\n' "$PBODY" | quoted_on_key capabilities)" ADD="$(printf '%s\n' "$PBODY" | quoted_on_key add)" REMOVE="$(printf '%s\n' "$PBODY" | quoted_on_key remove)" -[ -n "$PRESET" ] || { echo "ERROR: profile declares no preset" >&2; exit 2; } +[ -n "$PRESET$DIRECT" ] || { echo "ERROR: profile declares neither 'capabilities' nor 'preset'" >&2; exit 2; } -# --- preset's base capabilities (from the gate data) --- -PRESETCAPS="$(section presets "$GATES" | quoted_on_key "$PRESET")" -[ -n "$PRESETCAPS" ] || { echo "ERROR: unknown preset '$PRESET' (not in $GATES [presets])" >&2; exit 2; } +# --- preset's base capabilities, if a preset is named --- +PRESETCAPS="" +if [ -n "$PRESET" ]; then + PRESETCAPS="$(section presets "$GATES" | quoted_on_key "$PRESET")" + [ -n "$PRESETCAPS" ] || { echo "ERROR: unknown preset '$PRESET' (not in $GATES [presets])" >&2; exit 2; } +fi -# --- effective capabilities = presetcaps + add - remove --- -EFFECTIVE="$(printf '%s\n%s\n' "$PRESETCAPS" "$ADD" | sort -u | sed '/^$/d')" +# --- effective capabilities = capabilities + presetcaps + add - remove --- +EFFECTIVE="$(printf '%s\n%s\n%s\n' "$DIRECT" "$PRESETCAPS" "$ADD" | sort -u | sed '/^$/d')" if [ -n "$REMOVE" ]; then EFFECTIVE="$(comm -23 <(printf '%s\n' "$EFFECTIVE") <(printf '%s\n' "$REMOVE" | sort -u))" fi @@ -58,7 +65,7 @@ present() { } echo "repo: $REPO" -echo "preset: $PRESET" +echo "profile: ${PRESET:+preset=$PRESET }${DIRECT:+direct-capabilities}" echo "effective capabilities: $(printf '%s ' $EFFECTIVE)" echo