From 59f2c3918646aa96f94f9004cee3b2b058d64631 Mon Sep 17 00:00:00 2001 From: Leynos Date: Sat, 19 Sep 2026 01:47:08 +0100 Subject: [PATCH 1/3] Propose gradual orchestration contracts Add five proposed RFCs for managed states and functional probes, optional typed task inputs, owned artefacts and cleanup, named contention classes, and opt-in maturity policies. Add roadmap phases 20 to 25 with explicit dependencies and acceptance tasks, preserving the unchanged quickstart and independently usable enhancements. --- docs/contents.md | 18 + docs/rfcs/0013-managed-states-and-probes.md | 321 +++++++++++++ docs/rfcs/0014-typed-task-inputs.md | 231 +++++++++ ...5-artefact-ownership-and-scoped-cleanup.md | 256 ++++++++++ docs/rfcs/0016-named-contention-classes.md | 196 ++++++++ ...ssive-enhancement-and-maturity-policies.md | 262 +++++++++++ docs/roadmap-progressive-enhancement.md | 442 ++++++++++++++++++ 7 files changed, 1726 insertions(+) create mode 100644 docs/rfcs/0013-managed-states-and-probes.md create mode 100644 docs/rfcs/0014-typed-task-inputs.md create mode 100644 docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md create mode 100644 docs/rfcs/0016-named-contention-classes.md create mode 100644 docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md create mode 100644 docs/roadmap-progressive-enhancement.md diff --git a/docs/contents.md b/docs/contents.md index b8e4d493b..f969ba26f 100644 --- a/docs/contents.md +++ b/docs/contents.md @@ -28,6 +28,9 @@ operator, user, and contributor references are easier to find. - [roadmap-composition.md](roadmap-composition.md): Roadmap continuation for local includes, versioned bundles, explicit external acquisition, and composition-specific execution-context integration. +- [roadmap-progressive-enhancement.md](roadmap-progressive-enhancement.md): + Phases 20 to 25 for shallow-end compatibility, optional typed inputs, + contention classes, states and probes, owned cleanup, and maturity policies. - [archive/roadmap-completed-foundations.md](archive/roadmap-completed-foundations.md): Archived completed roadmap foundations with relevance assessments and traceability notes. @@ -77,6 +80,16 @@ operator, user, and contributor references are easier to find. fuzzing](rfcs/0008-code-health.md): Proposed workflow-policy validation, gate self-consistency, health-signal ownership, and scheduled coverage-guided fuzzing. +- [RFC 0013: Managed states and functional probes][rfc-0013]: Optional + preparation contracts, default built-in checks, and bounded external probes. +- [RFC 0014: Optional typed task inputs][rfc-0014]: Gradual input annotation, + shared bundle-parameter validation, and explicit source provenance. +- [RFC 0015: Artefact ownership and scoped cleanup][rfc-0015]: Exact output + ownership, bounded previews, and capability-scoped deletion. +- [RFC 0016: Named contention classes][rfc-0016]: Optional per-edge limits + lowered to Ninja pools, with explicit invocation-only scope. +- [RFC 0017: Progressive enhancement and maturity policies][rfc-0017]: + Shallow-end compatibility and opt-in, scoped, trust-aware enforcement. [rfc-0009]: rfcs/0009-structured-command-working-directories.md [rfc-0012]: rfcs/0012-netsukefile-property-testing.md @@ -84,6 +97,11 @@ operator, user, and contributor references are easier to find. [rfc-0002]: rfcs/0002-repository-relative-includes.md [rfc-0004]: rfcs/0004-digest-pinned-external-bundles.md [rfc-0011]: rfcs/0011-allow-listed-structured-command-shells.md +[rfc-0013]: rfcs/0013-managed-states-and-probes.md +[rfc-0014]: rfcs/0014-typed-task-inputs.md +[rfc-0015]: rfcs/0015-artefact-ownership-and-scoped-cleanup.md +[rfc-0016]: rfcs/0016-named-contention-classes.md +[rfc-0017]: rfcs/0017-progressive-enhancement-and-maturity-policies.md ## Decision records diff --git a/docs/rfcs/0013-managed-states-and-probes.md b/docs/rfcs/0013-managed-states-and-probes.md new file mode 100644 index 000000000..747e0cbe3 --- /dev/null +++ b/docs/rfcs/0013-managed-states-and-probes.md @@ -0,0 +1,321 @@ +# RFC 0013: Managed states and functional probes + +## Preamble + +- **RFC number:** 0013 +- **Status:** Proposed +- **Created:** 2026-09-19 +- **Scope:** Optional preparation contracts, not a new build scheduler +- **Implementation:** [Progressive-enhancement roadmap, phase 23][roadmap] + +## 1. Summary + +Add optional named states for things that must be prepared before a command +runs. A state describes a condition, the evidence needed to check it, and an +optional preparation recipe. Built-in probes serve the normal case; external +functional checks support project-specific conditions without a provider plugin +or a Nagios installation. + +A plain `command: uv sync` remains valid. Adding a state is worthwhile only when +an author needs an explicit precondition, validated reuse, or a shared +preparation contract. No context, typed input, ownership declaration, maturity +policy, or bundle is compulsory. [RFC 0017][maturity] owns this shallow-end +compatibility requirement. + +## 2. Problem and existing boundaries + +A directory timestamp cannot establish that a virtual environment contains the +requested interpreter and packages. A successful previous installation cannot +establish that an external service still works. Conversely, a test that requires +an already-installed extension must not silently build it. + +[RFC 0001][commands] owns structured execution; its working-directory, temporary +resource, and shell amendments remain authoritative. This RFC adds execution +units to that runner, not a template-time subprocess facility. Cargo, uv, and +other ecosystem tools retain dependency resolution and incremental compilation. + +## 3. Progressive authoring + +The following proposed fragment uses a built-in presence probe. It promises only +that `build` is a directory, not that its contents form a valid build: + +```yaml +states: + build-directory: + kind: directory + path: build + prepare: + invoke: python -c "from pathlib import Path; Path('build').mkdir(exist_ok=True)" + +actions: + - name: prepare-directory + command: + ensure_state: build-directory +``` + +`kind` selects a documented built-in probe by default. There is no mandatory +`probe: builtin` boilerplate. Initial kinds are `directory`, `file`, and +`python-venv`. The first two check the declared object type without following +symlinks. They make no content-freshness claim. `python-venv` checks the actual +interpreter, its environment prefix, and any declared interpreter constraint; it +must not advertise package-set verification that it does not implement. + +A richer proposed fragment keeps a functional check separate from preparation: + +```yaml +states: + dev-env: + kind: python-venv + path: .venv + identity: + files: [pyproject.toml, uv.lock, tools/check_dev_env.py] + prepare: + invoke: uv sync --locked --group dev + probe: + external: + invoke: python tools/check_dev_env.py + protocol: nagios + +actions: + - name: test + command: + - ensure_state: dev-env + - invoke: uv run --no-sync pytest +``` + +Here the built-in environment checks still run. The external check adds a +condition; it cannot bypass the built-in checks or forge preparation identity. +The script is project-owned runtime code, not an example of shipped tooling. Its +declared condition must cover any package expectations on which consumers rely. +A lockfile digest records preparation inputs, not current installation +integrity. + +`kind: custom` supports conditions without a suitable built-in kind. It requires +an explicit external probe and cannot acquire stronger verification claims than +that probe supplies. A custom state may omit `path` when it observes a service; +that does not authorize network access or remote mutation. + +## 4. State definition and identity + +State names use the existing declaration-name and namespace rules. Unknown +fields, duplicate names, unresolved references, and unsupported kinds fail +before execution. Definitions contain `kind`, kind-specific configuration, an +optional `path`, `identity`, optional `prepare`, optional `probe`, and optional +`accept_degraded`. + +`identity.files` contains exact capability-scoped paths, not shell patterns. +Missing required files are errors; optional inputs need a future explicit +contract. The normalized definition, probe implementation/version, preparation +plan, relevant declared input values, declared file contents, and resolved +execution settings contribute to identity. Built-ins add their documented +interpreter/platform facts. Identity excludes irrelevant typed inputs. + +Files used by an external probe, including project scripts, must participate in +identity. Bundled runtime files follow RFC 0003's resource inventory and digest +rules. Ambient tools cannot be described as pinned solely because their names +match. Diagnostics distinguish a declared command from a verified executable +identity and disclose unmodelled ambient dependencies. + +A persistent record proves only that a particular preparation completed and +passed verification. It is never a reusable live probe result. Validate mutable +state on every state operation; do not cache success by a directory timestamp, +unqualified success stamp, or default time-to-live. Removing or corrupting a +record means no trusted preparation evidence, not success. + +Without an `identity` declaration, readiness depends on current probes and no +prior preparation record is required. Declaring `identity` adds matching +preparation evidence as a readiness condition. Post-preparation verification +checks the newly prepared candidate identity plus fresh probes, not the stale +record it is about to replace; publication still follows successful checks. + +For identities that require preparation evidence, a mismatch establishes +`not_ready` only after the resource can be inspected successfully. Permission +failure, unsupported inspection, and unreadable identity inputs remain errors or +`unknown`, not repair triggers. A custom functional condition without such +identity inputs can become ready through verification alone. + +## 5. Operations and outcomes + +The proposed command union adds exactly three single-key operations: + +- `require_state: NAME` verifies readiness and never prepares anything. +- `ensure_state: NAME` verifies, prepares once when definitely not ready and + preparation is declared and authorized, then verifies again. +- `prepare_state: NAME` explicitly runs preparation and subsequent verification, + delegating any incremental work to the selected ecosystem tool. + +An operation holds its state lease as described in section 8. Preparation must +finish successfully and post-verification must establish readiness before +Netsuke records success. There is no automatic repair loop or fallback runner. +Failure stops subsequent commands in the action. An absent preparation recipe +produces an actionable unmet-precondition result, not an inferred installer. + +The result algebra is `ready`, `not_ready`, `degraded`, and `unknown`. +`degraded` fails a required readiness condition by default and never triggers +repair. A state may explicitly set `accept_degraded: true` only within operator +policy; the warning remains observable. `unknown` never authorizes automatic +preparation or satisfies a required condition. An explicit `prepare_state` is a +separate authorized mutation request, not a consequence of a failed probe. +Cancellation remains cancellation. + +Combining built-in and external checks requires every condition to pass. +Inspection errors take precedence over a possible repair decision: an unknown +check prevents repair even when another check reports not-ready. A preparation +identity mismatch and a functional failure retain distinct reason codes. + +## 6. External probe protocol + +The optional `nagios` protocol uses the conventional plugin exit statuses[^1]: + +| Exit | Probe result | Default state-operation behaviour | +| --- | --- | --- | +| 0 | `ready` | Continue only if every other condition passes. | +| 1 | `degraded` | Stop without repair; explicit acceptance may continue. | +| 2 | `not_ready` | An ensure may prepare once; a requirement never prepares. | +| 3 | `unknown` | Stop without repair. | + +Table 1: Nagios-style statuses and their Netsuke interpretation. + +Other exits, signals, spawn failure, timeout, malformed output encoding, and +output-budget exhaustion produce `unknown` with a distinct execution reason. A +process's exit status is authoritative; a success-looking message cannot convert +a failing exit into readiness. The first stdout line supplies a human summary. +Remaining stdout and stderr are bounded diagnostic data. A `|` suffix may be +retained as uninterpreted performance data, but it never controls state, +identity, scheduling, or authorization. No metric parser or Nagios daemon is +required. + +Proposed defaults are a ten-second wall-clock deadline and 64 KiB combined +stdout/stderr. Optional `probe.timeout_seconds` and `probe.max_output_bytes` +are positive integers; trusted operator limits cap requested bounds. Drain both +streams +concurrently, enforce limits during collection, and terminate and reap the owned +process tree on timeout, cancellation, or excess output. No shell redirection or +unbounded capture is necessary. A probe cannot change command-local runtime +bindings in the consuming action. + +Only ordinary structured invocation fields needed for a functional check are +accepted inside `probe.external`: `invoke`, `env`, `cwd`, and allowed `shell` +selection. Reject pipelines, stream files, runtime capture, nested state +operations, and cleanup there. Explicit named-shell probes remain possible under +RFC 0011; direct invocation is the default. Probe return codes are not Netsuke's +public CLI exit codes: the existing structured-result mapping owns that +translation. + +Probes should be observational and idempotent, but executing arbitrary code does +not prove either property. The runner must not claim a read-only or network +sandbox that it does not supply. An operator can forbid external probes or +restrict their executable identities. Project configuration and bundle content +cannot weaken those restrictions. Lack of authorization is an error, not a +reason to bypass the probe. Do not run probes or preparation merely to render +help, inspect context, check a manifest, generate a graph, or preview work. + +## 7. Execution settings and security + +Use the ordinary inherited environment, exact per-command overlays, trusted +shell selection, and capability-scoped working-directory rules. A new named +context system is not a prerequisite. If named contexts later become available, +resolve them through the same command plan rather than a state-private resolver. + +Probe and preparation commands may intentionally use different tools. Record +both effective settings and diagnose accidental environment-root mismatches; do +not replace an unavailable requested interpreter with an ambient one. External +code has the same trust implications as a build recipe. State annotations do not +make an untrusted checkout safe to execute. + +Redact probe arguments and output through the shared diagnostic policy, bound +messages, and escape terminal control sequences. Do not export raw probe output +as metric labels. Built-ins must use the injected environment and filesystem +seams, not process-global mutation or a separate configuration reader. + +## 8. Scheduling, mutation, and durable records + +Keep state operations inside ordinary Ninja-scheduled action edges. They are not +graph-discovery operations, and their results cannot change manifest-time +conditions or add undeclared dependencies. The action-plan codec must represent +them explicitly and reject unknown versions during replay. + +State evidence is not itself a Ninja output. A state-using action must either be +an always-run action or explicitly opt into always-run execution; reject an +incremental file target with state operations until a separate +runtime-validation contract can guarantee that its checks actually run. This +avoids silently skipping a readiness check because an unrelated output is up to +date. + +For managed mutable paths, acquire a workspace-scoped advisory lease for every +state referenced by an action before its first command, in canonical resource +order, and retain the leases through the last consumer in that action. These are +integrity locks for cooperating invocations, not a replacement scheduler. Bound +acquisition and never acquire a lease while recursively invoking Netsuke. Reject +different state identities claiming the same mutable path within one selected +build closure; isolated paths are the first-version remedy. + +A standalone preparation action does not hold a lease for its dependants. +Consumers therefore need their own `require_state` or `ensure_state` operation. +Shared preparation may be skipped after fresh validation, but readiness cannot +be memoized across another action's mutation. Unmanaged commands and other +programs do not participate; the guarantee must say so explicitly. + +Named contention classes in [RFC 0016][contention] can reduce contention before +action dispatch. They do not replace state leases across invocations. Shared +state identity and filesystem alias handling must use existing capability +anchors; ambiguous aliases or unsupported locking filesystems fail rather than +pretend to serialize access. + +Store records in a versioned, bounded runtime namespace separate from dyndep +sidecars. Publish records atomically only after verification. Interrupted +preparation leaves no success record; partial resources remain unverified and +may need explicit remediation. Cleanup invalidates records under the same lease. +Do not automatically remove an environment after failure or run undeclared +teardown commands. Artefact ownership is a separate optional contract. + +## 9. Verification and acceptance + +Unit and behavioural tests must cover all four results, all three operations, +post-preparation failure, identity changes, damaged records, denied probes, and +absence of a preparation recipe. Property tests must establish that unknown +results never authorize repair and only verified success creates a record. + +External-probe fixtures must cover every exit code, empty and multiline output, +performance suffixes, non-UTF-8 data, terminal escapes, huge simultaneous +output, hung descendants, cancellation, and a failure message containing +apparent success text. Assert bounded collection and complete process reaping. + +End-to-end tests must delete a package/interpreter component after successful +preparation, change identity inputs, contend on the same resource from separate +processes, and interrupt between preparation and record publication. A normal +hello-world build must start zero probes and create no state records. + +The Cuprum canary must preserve its restricted extension-test selection and +verify that `require_state` reports a missing extension without running Maturin. +Measure setup reduction without counting a stale probe result as a cache hit. + +## 10. Alternatives, migration, and outstanding decisions + +Timestamp-only stamps are insufficient for live readiness. A mandatory Nix-like +store would require a different workflow and is out of scope. External checks +alone would force every project to reinvent common checks; built-ins alone would +force plugin development for ordinary functional conditions. + +Adoption is per action. Existing commands and environment management remain +supported, and no package installer becomes a prerequisite for unrelated work. +Allocate the manifest and persisted-plan versions during acceptance alongside +RFC 0001; examples here are proposed fragments, not current-release promises. + +Before implementation, ratify the precise `python-venv` inspection contract, +portable lease implementation, runtime-record retention limits, and operator +probe-policy fields. A future uv-specific package-integrity probe should reuse +uv's supported interfaces rather than implement another resolver. None of these +choices may make bare command recipes depend on state machinery. + +## 11. Recommendation + +Deliver built-in probes and explicit operation semantics first, then external +functional checks with the same result algebra and process boundary. Keep +readiness, preparation evidence, and artefact ownership distinct. + +[roadmap]: ../roadmap-progressive-enhancement.md#23-verified-preparation-without-mandatory-environments +[maturity]: 0017-progressive-enhancement-and-maturity-policies.md +[commands]: 0001-structured-command-blocks.md +[contention]: 0016-named-contention-classes.md +[^1]: [Nagios plugin development guidelines](https://nagios-plugins.org/doc/guidelines.html), plugin return codes. diff --git a/docs/rfcs/0014-typed-task-inputs.md b/docs/rfcs/0014-typed-task-inputs.md new file mode 100644 index 000000000..dbfa4f989 --- /dev/null +++ b/docs/rfcs/0014-typed-task-inputs.md @@ -0,0 +1,231 @@ +# RFC 0014: Optional typed task inputs + +## Preamble + +- **RFC number:** 0014 +- **Status:** Proposed +- **Created:** 2026-09-19 +- **Scope:** Public task configuration with gradual adoption +- **Implementation:** [Progressive-enhancement roadmap, phase 21][roadmap] + +## 1. Summary + +Add an optional `inputs` mapping for validated, discoverable task configuration. +Ordinary `vars` remain valid and keep their current semantics. An author can +promote one externally meaningful value without annotating every variable, +rewriting every command, adopting bundles, or declaring an execution context. + +Use the same type vocabulary, value validation, and redaction contract as +[RFC 0003's bundle parameters][bundles]. Do not create a rival parameter system. +The [maturity-policy RFC][maturity] permits explicit organizations or projects +to require selected contracts; it does not turn annotations into a default gate. + +## 2. Problem and current boundaries + +Raw flag strings combine user intent, argument transport, and tool-specific +syntax. The distinction matters when compilation workers and test workers use +different switches. Validation should reject an invalid worker count before any +command starts, while argv handling should remain RFC 0001's responsibility. + +The current manifest supports variables. RFC 0003 already proposes typed bundle +parameters, and roadmap phase 5 owns profile and context integration with +OrthoConfig. This RFC supplies task-input semantics and their integration, not +another configuration loader, profile store, or command metadata generator. + +## 3. The shallow end and one-value promotion + +This existing-style fragment remains legitimate without annotation: + +```yaml +vars: + workers: 2 + +actions: + - name: test + command: "pytest -n {{ workers }}" +``` + +The following proposed fragment adds a contract for that one input: + +```yaml +inputs: + workers: + type: integer + minimum: 1 + default: 2 + description: Number of pytest workers + expose: non-secret + +actions: + - name: test + command: + invoke: pytest -n {{ inputs.workers }} +``` + +Changing `workers` to `inputs.workers` makes the promotion explicit. There is no +implicit alias, mutation of `vars`, or requirement to annotate unrelated values. +Structured invocation is recommended for dynamic arguments, but adopting an +input does not silently convert a legacy shell string into direct execution. + +## 4. Definition and value model + +The initial `type` vocabulary is exactly `string`, `bool`, `integer`, `path`, +`sequence`, `sequence`, and `mapping`. A string +with `choices` represents an enum. An argument list uses `sequence`; +there is no competing `argv` or `enum` type in the initial grammar. + +A definition contains required `type`, optional `default`, `description`, and +`expose`, plus applicable constraints. No default means required. `minimum` and +`maximum` constrain integers; `choices` contains a nonempty, duplicate-free list +of values of the declared scalar type. Reject inverted ranges, mismatched +constraints, unknown fields, and defaults that violate their own contract. + +Definitions and defaults are literal data, like bundle descriptors, not Jinja +programs. Computed internal values belong in ordinary `vars` after input +resolution. No executable, clock, filesystem, or network default expressions are +necessary. Required but unused root inputs still fail definition resolution; +authoring an optional facility must not introduce an unrelated required input. + +Use the existing supported integer range and reject out-of-range values. A +Boolean is not an integer; `1.0` is not silently accepted as `1`. Strings remain +strings, including empty strings. Null is not an omitted optional value unless a +future shared nullable-type contract explicitly introduces it. + +`path` is a typed path value, not a filesystem capability or permission grant. +Validation checks encoding and lexical shape without creating files. A consumer +that opens, executes, or deletes the path applies its own workspace and operator +capability constraints at use time. Collections are homogeneous, bounded, and +validated element by element; sequence order is significant. + +`inputs` is immutable during manifest expansion. Introducing this reserved +namespace must preserve manifests that do not opt into the new schema. When an +opted-in manifest already defines a conflicting `vars.inputs`, emit a targeted +migration diagnostic rather than overwrite it silently. + +## 5. Sources, precedence, and provenance + +Introduce one proposed CLI option, `--input NAME=VALUE`, on commands that +compile a manifest. Register it through the canonical command metadata before +public examples or implementation. Names select declared root inputs only. +Qualified private bundle parameters remain inaccessible; the root passes +selected values through the existing `with` boundary. + +CLI parsing is type-directed, not shell splitting or arbitrary YAML evaluation: + +- Strings and paths retain the text after the first `=` exactly. +- Integers use the documented signed decimal grammar without floats. +- Booleans accept exactly `true` and `false`. +- Sequences and mappings accept JSON of the declared shape, with bounded size + and explicit duplicate-key rejection. + +Reject unknown names and duplicate CLI occurrences. Do not interpret an input as +an executable shell fragment. Direct argv interpolation must preserve spaces, +metacharacters, empty elements, and sequence boundaries. The shell still owns +interpretation inside an explicitly selected shell recipe. + +Precedence, highest first, is explicit CLI input, explicitly selected profile +input, resolved configuration input, then manifest default. Existing OrthoConfig +rules determine precedence within configuration sources; this RFC does not +reorder system, user, project, and explicitly selected configuration files. The +profile integration task must reconcile its own overlay order with this contract +before implementation. No automatically inferred environment variables supply +task inputs in the first version. + +Validate the effective value once, retaining the winning source and bounded +shadowed-source provenance. Duplicate declarations and malformed source data +remain errors even when a higher-precedence value exists. Configuration syntax +cannot create undeclared inputs or weaken their constraints. Operator ceilings +apply after selection and may reject a request; they are not defaults that a +project can override. + +Profiles bind input values; they do not replace input definitions. Inspection +must identify the effective profile, source, and validation contract without +running a build or acquiring tool environments. Persistent generated plans fix +their resolved values: replay never re-reads a different ambient profile. + +## 6. Composition and identity + +Includes follow RFC 0002's duplicate and provenance rules. The initial root +`inputs` mapping supplies the root interface. Included fragments can reference +it within their established scope; they cannot silently overwrite definitions. A +bundle receives explicit values through `with` and exposes them internally as +`bundle.params`, not the importer's whole `inputs` object. + +Extract or reuse one feature-owned normalized parameter contract for root inputs +and bundle parameters. Share parsing, validation, scalar normalization, +constraint diagnostics, and redaction. Bundle selection, locks, private exports, +and namespace resolution remain composition's responsibility. Root input support +must not wait for external acquisition or require any bundle. + +Resolved values used by an action or state contribute to its existing +fingerprint. Preserve collection order and canonicalize mapping-key order. Do +not replace action hashing or invent a separate build cache. Unused inputs must +not alter a resolved command's argv; conservative graph-level invalidation may +remain until dependency tracking can narrow it safely. + +Raw argument-list escape hatches remain legal. Tool adapters that claim control +over workers, interpreter selection, or environment location must reject +conflicting owned switches in those lists. Typed data alone does not guarantee +that a tool-specific flag bag respects the declared policy. + +## 7. Diagnostics, redaction, and limits + +Follow RFC 0003: values are redacted by default. Only the exact declaration +`expose: non-secret` permits ordinary inspection of a value, subject to stronger +operator policy. Show the name, expected type, constraint, source location, and +remedy without echoing a rejected secret-looking value. Human output, JSON, +verbose output, snapshots, and telemetry share that boundary. + +Redaction is not secret storage. This RFC does not introduce a secret type or +promise confidentiality for values deliberately passed as process arguments or +persisted in action plans. Future secret support must address process listings, +plan storage, and low-entropy digest leakage rather than merely hide UI text. + +Use the existing evaluation budgets for definition count, collection depth, +element count, input byte size, and diagnostics. Apply bounds before expensive +allocation or evaluation. A maturity setting cannot disable type correctness, +resource limits, or capability checks for an opted-in declaration. + +## 8. Compatibility, tests, and rollout + +Allocate the manifest version with the shared schema acceptance work. Older +readers must reject new syntax with a useful version remedy rather than ignore +it. Existing untyped manifests keep their parse, execution, and diagnostic +behaviour under the default policy. No release should force a broad migration +merely because this optional vocabulary exists. + +Test the same value corpus through bundle and task-input validation. Include +false-as-integer, overflow, whitespace, empty strings, duplicate keys, invalid +constraints, required values, unknown names, hostile argv elements, Windows +paths, and collection-order preservation. Property-test source precedence, +normalization stability, and redaction of rejected values. + +Integration tests must cover profile selection, generated-plan replay, namespace +privacy, environment-independent defaults, and values passed across an +include/bundle boundary. Check one-variable promotion in a real command and +retain the unmodified hello-world fixture. Compare actual child argv, not just +rendered YAML. Add examples and metadata through the existing documentation and +schema pipelines, without a parallel input-help renderer. + +## 9. Alternatives and outstanding decisions + +Mandatory annotations would obstruct onboarding. Continuing with only raw flag +strings would leave validation and discovery to each recipe. A second schema +language for task inputs would duplicate RFC 0003 and make composition harder. +Implicit promotion of every variable would unexpectedly create a public API. + +Before implementation, ratify the CLI option's placement in canonical metadata, +profile overlay details, and the exact existing integer and path-normalization +contracts to share. Whether richer dependent defaults or custom validators are +needed remains deferred until canaries demonstrate demand. Arbitrary validation +code is not part of the initial input schema. + +## 10. Recommendation + +Deliver one optional root interface with the existing bundle type vocabulary, +explicit promotion, deterministic source selection, and default redaction. Keep +internal variables untyped unless their author chooses otherwise. + +[roadmap]: ../roadmap-progressive-enhancement.md#21-typed-inputs-with-one-parameter-contract +[bundles]: 0003-versioned-local-bundles.md#6-parameter-model +[maturity]: 0017-progressive-enhancement-and-maturity-policies.md diff --git a/docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md b/docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md new file mode 100644 index 000000000..42d955a36 --- /dev/null +++ b/docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md @@ -0,0 +1,256 @@ +# RFC 0015: Artefact ownership and scoped cleanup + +## Preamble + +- **RFC number:** 0015 +- **Status:** Proposed +- **Created:** 2026-09-19 +- **Scope:** Optional owned-output declarations and bounded deletion +- **Implementation:** [Progressive-enhancement roadmap, phase 24][roadmap] + +## 1. Summary + +Add optional named artefacts and one standardized scoped-cleanup operation. An +author who declares disposable output ownership gains bounded enumeration, a +useful preview, explicit confirmation, and capability-scoped deletion. Plain +commands, existing targets, and existing `clean` behaviour remain supported. + +Ownership is an assertion by the manifest author, not proof that a directory +contains no valuable files. The implementation must make the scope inspectable +and enforce its boundaries without claiming to sandbox arbitrary recipes. +[RFC 0017][maturity] makes this an opt-in improvement rather than an onboarding +prerequisite. + +## 2. Problem and existing boundaries + +Repositories repeat cleanup lists and broad deletion commands. The lists drift +away from producers, and shell fragments obscure path, symlink, and error +semantics. A benchmark report, shared tool cache, temporary directory, and +prepared environment also need different retention decisions. + +Netsuke already has target outputs and delegates ordinary output cleaning to +Ninja. Its runtime owns dyndep sidecars under a separate bounded-retention +contract. RFC 0010 owns command-local temporary directories. This RFC must not +replace those mechanisms or infer ownership from arbitrary command text. + +## 3. Progressive authoring + +The following proposed fragment adds one explicit disposable directory and a +cleanup action without a state, typed input, context, or package provider: + +```yaml +artefacts: + build-output: + path: build + kind: directory + +actions: + - name: clean-build + command: + clean_owned: [build-output] +``` + +`path` names exactly one workspace-relative object. `kind` is `file` by default; +recursive ownership requires explicit `directory`. A directory declaration +claims the entire subtree, including existing and subsequently created children. +The preview must say this plainly. It is unsuitable for a directory shared with +source files or another owner's mutable state. + +A producer can identify a report without changing whether the action runs: + +```yaml +artefacts: + benchmark-report: + path: dist/benchmarks/microbenchmarks.json + role: report + create_parent: true + +actions: + - name: benchmark + produces: [benchmark-report] + command: + invoke: python benchmarks/run.py --output dist/benchmarks/microbenchmarks.json +``` + +The command is illustrative. `produces` records ownership and expected outputs; +it does not make a benchmark incremental or authorize replay of an old result. +Directory creation occurs only when that producer executes, through the same +path boundary used for cleanup. + +## 4. Declaration, ownership, and output contracts + +An artefact contains required `path`, optional `kind`, optional `role`, and +optional `create_parent`. Initial roles are `generated`, `report`, `cache`, and +`environment`, with `generated` the default. A role describes intent, not an +automatic deletion or caching rule. No role disappears merely because another +action failed. Exact paths, rather than arbitrary glob expressions, keep the +first implementation bounded and reviewable. + +Use existing namespace, duplicate, and source-provenance rules. Each normalized +path has one owning declaration. Reject duplicate or overlapping ownership +roots, including case-equivalent names on the relevant filesystem. Parent +creation is not an ownership claim over the parent or its other contents. A +directory and a separately declared child are overlapping owners in this first +version; declare one or use disjoint exact files. + +A `produces` reference identifies one producer. Reject multiple producers for +the same artefact unless an existing explicit target contract already supplies +one shared producer. An explicit artefact may refer to an existing target output +only when both identify that same producer and compatible object type. It +augments metadata; it must not create a second Ninja producer edge. + +Verify declared required outputs after successful production. Missing outputs +are producer failures, not success. Failure may leave partial owned outputs; +record the failure but retain those outputs for inspection and explicit cleanup. +Do not automatically remove them or reuse them as successful build evidence. + +Declarations without a producer remain useful for externally generated, +explicitly disposable paths. Inspection must distinguish declared ownership from +observed successful production. Neither status attests that the author chose a +safe directory. No producer receipt is required merely to remove a declared +legacy build directory, but declaration, bounded preview, and authorization are. + +This does not introduce remote artefact delivery, a content-addressed store, or +a provenance attestation system. Roadmap phase 5 retains its delivery boundary. + +## 5. Scoped-cleanup operation and public command integration + +Add `clean_owned: [NAME, ...]` as an explicit execution unit in the structured +command union. The list is nonempty, order-insensitive after name resolution, +and duplicate references normalize to one selection. Unknown references fail. +The operation may name artefacts only; it never accepts raw shell paths. + +Extend the existing `clean` command with repeatable `--artefact NAME` selection. +Register the extension in canonical CLI metadata before implementing it. Without +that option, preserve existing Ninja-output cleanup. With explicit artefact +selection, clean only the selected ownership roots; do not implicitly add every +Ninja output, environment, cache, or runtime directory. + +These proposed commands demonstrate preview and explicit noninteractive consent: + +```bash +netsuke clean --artefact build-output --dry-run +netsuke clean --artefact build-output --force --no-input +``` + +Both command and recipe forms use the same planner, validator, deleter, and +structured results. Use the existing mutation metadata for `--dry-run`, +`--force`, and `--no-input`; do not invent a cleanup-specific confirmation +framework. An interactive run can request confirmation after displaying scope. A +noninteractive destructive run without explicit consent fails before deletion. +`--force` skips confirmation, not validation, ownership conflicts, or bounds. + +A dry-run is a read-only plan, not a reusable authorization token. Execution +resolves and validates scope again, and must not consume a stale user-supplied +list as trusted filesystem authority. The preview lists normalized roots, +recursive ownership, existing objects, missing objects, retained siblings, and +any rejected scope. It must not truncate away selected objects and then claim to +show a complete destructive plan: exceeding bounds fails the plan. + +## 6. Filesystem safety contract + +Anchor paths at the effective workspace capability, not the caller's ambient +working directory or the location of an included fragment. Reject absolute +paths, empty paths, the workspace root, parent traversal, incompatible +encodings, and platform-specific escape forms before touching the filesystem. + +Protect the effective manifest, loaded configuration, local runtime scripts +known to the compiled plan, declared source paths, VCS metadata such as `.git`, +and Netsuke's reserved runtime directories. A directory claim containing a +protected object is invalid. Without source-control metadata Netsuke cannot +identify every source file; the contract must disclose this limitation rather +than infer arbitrary source ownership. + +Never follow a symlink or Windows reparse point during recursive traversal. A +selected final symlink may be unlinked as an object within the declared root, +but never traversed to its destination. Revalidate its identity and parent +capability at deletion. Reject unsupported mount, junction, or filesystem cases +rather than fall back to lexical `starts_with` checks or ambient `rm -rf`. + +Use handle-relative traversal and deletion with platform-specific identity +checks. A path checked before enumeration is not automatically safe at deletion. +Detect replacement of selected roots and parents, and stop affected work. No +claim of race freedom is acceptable until adversarial replacement tests pass on +each supported platform; unavailable guarantees must produce explicit refusal. +Hard-linked file removal unlinks the selected directory entry, never truncates +the shared underlying file. + +Enforce operator-capped entry, depth, byte, and elapsed-time limits. Perform the +bounded admission pass before the first deletion; exceeding a bound there causes +zero deletions. Recheck while deleting because the tree may change. Delete files +before directories in a stable order. Already-missing objects are successful +no-ops. Permission, replacement, or interruption errors may follow partial +progress; report exact removed, retained, and failed objects. Cleanup is not an +atomic transaction and must never report rollback that it did not perform. + +## 7. Interaction with states, concurrency, and replay + +A separately declared environment artefact may name the same normalized path as +a [managed state][states]; the resource registry identifies that association. +Ownership remains optional for state use. When an environment is +selected for cleanup, acquire its integrity lease and invalidate its readiness +records before any deletion, including failure paths. Probe success from before +cleanup cannot establish subsequent readiness. + +Reject a selected build closure that both cleans and produces or consumes the +same declared resource. A Ninja pool would serialize access but would not +establish the user's intended order. Separate invocations are the initial +remedy. Coordinated state leases protect cooperating invocations; unrelated +external programs remain outside the guarantee. + +Pure artefact cleanup also needs an exclusive lease over each selected root, +ordered canonically. Producers participating in ownership use the same lease. It +must share the state-resource identity boundary rather than introduce a second +incompatible lock system. The lightweight artefact-only path cannot require +authoring a state declaration. + +Persist ownership definitions and provenance in the versioned action plan, not +captured directory listings. Replay obtains fresh capabilities and enumerates +again. Existing dyndep retention and command-private temporary cleanup retain +their original owners; `clean_owned` cannot target their reserved locations. A +later explicit `cargo clean` recipe still has Cargo's own semantics and is not +covered by Netsuke's scoped-deletion guarantee. + +## 8. Acceptance and migration + +Keep the existing hello-world and legacy `clean` fixtures unchanged. Add a +one-directory example whose complete ownership declaration needs only `path` and +`kind`. Test explicit file production, parent creation, stale reports, missing +outputs, and unchanged always-run benchmark behaviour. + +Property tests must cover canonical-path uniqueness, scope normalization, +protected roots, and duplicate selection. End-to-end tests must exercise +symlinks, reparse points, root replacement, hard links, permission errors, case +collisions, interrupted partial deletion, entry-budget overflow, and missing +paths. External sentinel files must remain unchanged. Assert zero filesystem +writes in dry-run, including no preparation or probe execution. + +Migrate Cuprum incrementally: explicitly declare disjoint output roots first; +keep unmatched wildcard cleanup as an ordinary command until a separate safe +file-selection contract exists. Do not add unrestricted glob deletion merely to +reproduce a long shell command in the initial release. Deleting legacy output +trees remains an explicit author decision shown in preview. + +## 9. Alternatives and outstanding decisions + +A mandatory out-of-tree store would change onboarding and project layout. +Inferring ownership from redirections or tool names would be unreliable. +Wrapping `rm -rf` would provide neither platform consistency nor a capability +boundary. Requiring an artefact declaration for every existing target would make +an optional benefit contagious. + +Before implementation, ratify the supported-platform deletion primitives, +resource-lease identity, ownership/source conflict rules, and concrete operator +limits. Atomic trash-and-rename, adoption receipts, wildcard collections, role +selectors, and remote delivery remain separate possible extensions, not +prerequisites for a useful exact-path cleanup operation. + +## 10. Recommendation + +Start with exact files and explicitly owned directory trees, one bounded cleanup +implementation, and transparent scope. Preserve ordinary recipes and existing +cleaning while giving annotated outputs stronger, testable guarantees. + +[roadmap]: ../roadmap-progressive-enhancement.md#24-owned-artefacts-and-bounded-cleanup +[maturity]: 0017-progressive-enhancement-and-maturity-policies.md +[states]: 0013-managed-states-and-probes.md diff --git a/docs/rfcs/0016-named-contention-classes.md b/docs/rfcs/0016-named-contention-classes.md new file mode 100644 index 000000000..e26b9277a --- /dev/null +++ b/docs/rfcs/0016-named-contention-classes.md @@ -0,0 +1,196 @@ +# RFC 0016: Named contention classes + +## Preamble + +- **RFC number:** 0016 +- **Status:** Proposed +- **Created:** 2026-09-19 +- **Scope:** Optional concurrency limits lowered to Ninja pools +- **Implementation:** [Progressive-enhancement roadmap, phase 22][roadmap] + +## 1. Summary + +Allow an action to name a contention class when it should share a bounded number +of concurrent execution slots with other actions. Lower that declaration to +Ninja pools. Keep Ninja as the scheduler, preserve dependencies as ordering and +data requirements, and leave unannotated actions unchanged. + +A simple build needs no resource model. One class and one scalar annotation must +suffice to prevent two cooperating native-build actions from overlapping. No +toolchain, state, context, ownership declaration, or typed input is required. + +## 2. Problem and existing capabilities + +Repositories use serial aggregates and independent worker flags to manage shared +caches and expensive native builds. Ordering requirements, contention, and +subprocess parallelism are different concepts. Serializing an aggregate can +unnecessarily block independent work while failing to constrain unrelated +actions that use the same resource. + +The current intermediate representation (IR) already has `Action.pool`. This RFC +supplies the declaration, resolution, bounds, provenance, and backend contract +around that existing concept, not a second resource scheduler. Ninja pools limit +concurrent edges and remain subject to Ninja's global job limit.[^1] They do not +limit the number of threads spawned by one edge. + +## 3. Progressive authoring + +This proposed fragment serializes two native-build commands while leaving other +actions eligible for normal scheduling: + +```yaml +contention_classes: + native-build: + capacity: 1 + +actions: + - name: rust-test + contention: native-build + command: cargo test + - name: extension + contention: native-build + command: maturin develop +``` + +The absence of `contention` retains the ordinary backend scheduling policy. +Plain legacy commands can opt in: structured-command adoption is not required +for a pool annotation. Capacity is a positive integer; there is no required +worker-count calculation or platform-detection preamble. + +## 4. Definition and resolution + +`contention_classes` maps names to definitions containing `capacity`. An +executable action or target may set one scalar `contention` reference. Literal +capacities work independently; typed input expressions may be added through [RFC +0014][inputs] without making that feature a prerequisite. + +Reject zero, negative, fractional, Boolean, unbounded, and out-of-range +capacities. Unknown fields, duplicate declarations, missing references, and +reserved internal names fail validation. Apply an operator maximum as a hard +ceiling and report the effective capacity and its provenance; malformed values +never become a default or silently coerce to integers. + +An action can belong to at most one class in the initial release. A list is an +error, not an implicit request for several locks. An explicit rule annotation +can supply a default when an action has none; conflicting explicit references +must fail rather than depend on include order. The implementation must settle +this interaction with the actual recipe inheritance contract before enabling +rule-level annotations. Initial delivery may support action/target annotations +only, with an explicit unsupported-field diagnostic on rules. + +Dependency-only aggregates cannot hold a slot because they execute no command. +Reject aggregate annotations with a remedy naming the executable children; do +not infer recursive inheritance across their dependency closure. An annotation +on a consumer neither constrains its prerequisites nor changes their order. + +## 5. Scheduling and lowering + +Resolve public names to stable internal class identities before backend +emission. Emit one Ninja pool per used class and attach its identity to the +corresponding executable edges. Unused classes emit no pool or runtime work. The +complete multi-command action holds one edge slot until it finishes or fails; +releasing slots between commands would weaken the declared contract. + +A class changes eligibility for concurrent dispatch, not the dependency graph's +meaning. It does not add dependencies, deduplicate actions, guarantee fairness, +or determine completion order among eligible actions. Preserve the existing +serial-dependency dyndep contract rather than replace it with pool-based order. + +Do not implement weighted CPU or memory tokens, multiple independent resource +acquisition, distributed locks, or a second ready queue. An action requiring +several related resources can use one explicitly named conservative class. +Unrelated classes cannot express overlapping exclusion sets in this version; +combining their names into a new class would not enforce the original limits. + +Ninja's special `console` pool cannot simultaneously be combined with a custom +pool on the same edge. Reject a request that requires both guarantees. Do not +silently discard either console behaviour or contention limits. + +Serialize resolved class definitions and references in generated plans and +include scheduling metadata in the existing graph/plan identity. Reject unknown +persisted variants or missing pool definitions before replay. Backends without +pool support must report the unsupported guarantee rather than ignore it. No API +may pass an unchecked public class name straight into Ninja source. + +## 6. Scope, worker budgets, and state integration + +The concurrency guarantee applies to one Ninja invocation. Two independent +Netsuke invocations do not share a pool. A depth-one class also does not stop an +external Cargo process from using the same directory. Document the guarantee in +human and structured inspection, not only in this RFC. + +Internal workers remain separate. A class with capacity one may still launch +many compiler or test threads. Tool adapters can consume typed worker inputs, +but the pool is neither a CPU quota nor a memory bound. Operator job ceilings +and the backend's jobserver behaviour remain authoritative. + +[Managed states][states] use integrity leases when cooperating invocations +mutate or consume the same managed path. A contention class can avoid +dispatching unnecessary competitors but does not replace those leases. State +operations inside an action retain its slot; they must not recursively start +Netsuke and wait for the same class. Bounded lock acquisition must not hide a +scheduling deadlock or provide an unbounded retry loop. + +Resource exclusivity is not semantic ordering. Cleaning and consuming a path in +the same selected closure remains an ownership conflict even with capacity one. + +## 7. Namespaces, inspection, and defaults + +Use RFC 0002's provenance and duplicate rules for includes. Bundle-private +classes remain private under RFC 0003. Sharing a class across bundles requires +an explicit importer binding to a declared root class; identical local names +must not accidentally serialize unrelated bundles. Local manifest delivery must +not wait for this optional bundle-binding integration. + +Inspection identifies each action's resolved class, requested and effective +capacity, source declaration, and invocation-only scope. JSON uses the existing +versioned metadata envelope. Diagnostics use bounded identifiers and source +spans; metric labels must not contain arbitrary class names or paths. + +The default manifest gains no pool, mandatory annotation, or new warning. +[Maturity policies][maturity] may require a class for explicitly selected +actions, but may not infer such a requirement from a command containing the word +`cargo` or silently alter capacity. + +## 8. Acceptance and compatibility + +Compare unannotated graph and Ninja output against existing fixtures. Add a +small legacy-command example before any larger structured-runner canary. Test +unknown classes, capacity bounds, conflicting definitions, console conflicts, +aggregates, namespace privacy, and persisted-plan compatibility. + +Use controlled child processes and a shared test journal to measure live +concurrency. Under global `-j` greater than one, assert that a capacity-one +class never overlaps, a capacity-two class never has three active edges, and an +unrelated action can proceed while that class is occupied. Check the entire +command-sequence interval rather than just process launch timestamps. + +Exercise failure, cancellation, and early-stop behaviour without timing-only +sleep assertions. Property-test stable naming, declaration-order independence, +and reference resolution. A separate two-invocation test must demonstrate the +boundary: pools alone do not claim process-wide mutual exclusion. + +## 9. Alternatives and outstanding decisions + +Serial dependencies encode order, not reusable contention policy. A lock command +inside every recipe repeats infrastructure and hides it from scheduling. A +generalized multi-resource scheduler would duplicate Ninja and create a much +larger correctness burden. Mandatory resource budgets would undermine the +shallow end without necessarily controlling subprocesses. + +Ratify the operator ceiling field, class-name lowering, console interaction, and +any rule-default semantics before implementing the public grammar. Reserve +multi-resource allocation and cross-host scheduling for separate evidence-led +proposals. Ordinary native compilation limits must not depend on those features. + +## 10. Recommendation + +Expose a deliberately small public contract over Ninja pools, with one optional +class per executable edge and explicit scope. Keep dependencies, internal worker +counts, and cross-invocation integrity locks separate. + +[roadmap]: ../roadmap-progressive-enhancement.md#22-named-contention-without-a-second-scheduler +[inputs]: 0014-typed-task-inputs.md +[states]: 0013-managed-states-and-probes.md +[maturity]: 0017-progressive-enhancement-and-maturity-policies.md +[^1]: [Ninja manual: pools](https://ninja-build.org/manual.html#ref_pool). diff --git a/docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md b/docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md new file mode 100644 index 000000000..e3f01525b --- /dev/null +++ b/docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md @@ -0,0 +1,262 @@ +# RFC 0017: Progressive enhancement and maturity policies + +## Preamble + +- **RFC number:** 0017 +- **Status:** Proposed +- **Created:** 2026-09-19 +- **Scope:** Shallow-end compatibility and opt-in contract enforcement +- **Implementation:** [Progressive-enhancement roadmap, phases 20 and + 25][roadmap] + +## 1. Summary + +Make progressive enhancement a compatibility requirement, not introductory +marketing. Netsuke must remain useful as a small command runner and build +compiler. States, typed inputs, named execution settings, owned artefacts, and +contention declarations add local guarantees only when explicitly adopted. + +Add optional maturity policies for projects and operators that want to require +particular declarations. Policy adoption must resemble enabling selected type +checker rules: ordinary unannotated manifests remain legitimate, and stricter +coverage is not a new installation prerequisite. No maturity score, automatic +promotion, or mandatory project tier is necessary. + +## 2. The shallow-end contract + +The current quickstart's first manifest remains a release acceptance fixture: + +```yaml +netsuke_version: "1.0.0" + +targets: + - name: hello.txt + command: "echo 'Hello from Netsuke!' > hello.txt" + +defaults: + - hello.txt +``` + +The manifest requires Netsuke and Ninja, not a typed input, context registry, +state provider, ownership model, package manager, policy file, or network +connection. The current list-of-mappings target shape and manifest version must +not be replaced with an invented onboarding dialect. + +These requirements apply to every RFC in this proposal set: + +- A plain command retains its existing ambient environment, working-directory, + shell, interpolation, and scheduling semantics. Simplicity must not silently + change a legacy shell string into direct argv execution. +- Opting one action into a feature does not require annotating its neighbours or + aggregates. An unannotated aggregate may depend on an annotated action. +- Unused state or context declarations never cause runtime probes, installation, + network access, or resource acquisition. Existing template-time capability + behaviour remains governed by its own policy; these additions introduce no new + inspection-time execution. +- Ordinary variables remain supported. No heuristic promotes `uv sync` to a + state, a redirection to an ownership claim, or a Cargo command to a pool. +- The straightforward command escape hatch remains available under the default + policy. A selected stricter policy may reject it with a local explanation, but + a plugin must not be the only way to execute an unusual tool. +- New opt-in schemas must validate correctly. Optional adoption is not + permission to ignore malformed typed inputs, unsafe paths, or unknown state + operations. + +The first page of onboarding must teach only the existing core. Subsequent +examples introduce one feature when it solves a visible problem, show the +simpler alternative, and state the added guarantee and its limits. Basic usage +is a supported destination, not a temporary migration stage. + +## 3. Relationship to existing plans + +RFC 0001 owns structured execution, RFCs 0002 to 0004 own composition, and +roadmap phase 5 owns profiles, inspection, and their OrthoConfig integration. +Issue #592's semantic linter owns reusable manifest analysis. This RFC adds +maturity-rule selection and trust-aware enforcement over that analysis; it must +not establish a competing linter, parser, configuration loader, or JSON +envelope. + +[RFC 0013][states] supplies preparation contracts, [RFC 0014][inputs] supplies +input contracts, [RFC 0015][artefacts] supplies ownership, and +[RFC 0016][contention] supplies pool-backed contention. Named execution contexts +remain a compatible extension point, not a sixth prerequisite hidden in these +five RFCs. The maturity schema may add a context rule only after that separate +surface has an accepted definition and an implementation. + +Do not repurpose RFC 0008's repository health tiers or RFC 0005's release +admission policy as user-manifest maturity. Their purposes, trust sources, and +failure meanings differ. + +## 4. Proposed policy shape + +A proposed root policy can require one useful guarantee at a time: + +```yaml +maturity: + rules: + - id: structured-commands + severity: warn + select: [publish] + - id: owned-cleanup + severity: error + select: [clean-build] +``` + +The default is an empty rule list. There is no default warning about missing +annotations. Every rule contains `id`, `severity`, and an explicit `select` list +of action/target identities. Initial severities are `off`, `warn`, and `error`. +Exact names keep the first scope contract small; bounded patterns can follow +only with a reviewed match and namespace contract. + +Unknown rules, duplicate rule/subject combinations, unknown selectors, and +unsupported severities fail validation. A policy cannot claim enforcement of a +feature that the installed version does not understand. Emit a +version/capability remedy rather than silently skip the rule. + +Rule-specific `subjects` lists refine what the rule checks, where required. +Selectors name the graph nodes to inspect, not arbitrary paths or command-text +regular expressions. Evaluation uses resolved declarations and provenance. + +## 5. Initial rule contracts + +The initial rules have deliberately narrow, checkable meanings: + +| Rule | Required evidence on selected nodes | +| --- | --- | +| `structured-commands` | Every resolved executable recipe unit is structured; legacy shell strings fail coverage. Explicit structured shell selection is not a claim of direct-argv safety. | +| `typed-inputs` | Each explicitly named configuration subject in `subjects` resolves to a typed input contract, not only an untyped variable. Internal variables are not automatically public inputs. | +| `owned-cleanup` | A selected cleanup action contains explicit `clean_owned` operations or nonexecuting aggregation only; arbitrary executable deletion recipes cannot satisfy the declaration contract. | +| `verified-states` | Each state named in `subjects` has a `require_state` or `ensure_state` operation before its first non-state command unit; a probe in a different action is insufficient. | +| `contention-declared` | Each selected executable edge resolves an explicit valid contention class. Dependency-only aggregates are not executable subjects. | + +Table 1: Initial declaration-coverage rules, not whole-program safety proofs. + +`typed-inputs` and `verified-states` require nonempty explicit `subjects`. The +first implementation must not guess which variables are external knobs or which +arbitrary command consumes an environment. For `verified-states`, the policy's +selected action/subject pair asserts that consumption relationship; analysis +requires the readiness operations before the first non-state command unit, +rather than guessing a consumption point inside a shell command. This is a +conservative opt-in recipe-order contract, not inferred whole-program dataflow. + +The ordinary schema enforces correctness regardless of rule severity. For +example, an invalid path in `clean_owned` remains an error even when +`owned-cleanup` is off. A structured command using a shell is still subject to +existing shell policy. Passing these rules does not establish hermeticity, +reproducibility, correct cleanup ownership, or safe untrusted-code execution. + +## 6. Scope and non-contagion + +Resolve policy against the selected build closure, retaining distinct definition +and invocation provenance. A rule selecting `publish` applies when that node +will execute; it does not make an unrelated `hello.txt` build fail coverage. +Global syntax and reference errors remain errors even outside the closure. +`netsuke check` can inspect the whole manifest and report each explicit scope. + +Aggregate selection must not implicitly make every dependency strict. If a +future closure selector is added, its spelling must be explicit and inspection +must enumerate its bounded expansion. Rule references within a selected recipe +remain part of that recipe and cannot hide legacy execution from its coverage +check. Imported private nodes retain qualified provenance without becoming +publicly selectable through an accidental export. + +A root may impose policy on imported public actions. An imported fragment or +bundle cannot relax its importer. Bundle-local strengthening applies only to +that instance, never unrelated root targets. Unknown private selectors fail; +implicit namespace wildcarding is not permitted. + +## 7. Trust-aware composition and profiles + +Use the existing configuration provenance and trusted operator boundary. An +automatically discovered project file, imported bundle, or explicitly chosen +`--config` file is not thereby trusted to weaken operator policy. + +For overlapping rule/subject scopes, combine severity monotonically: `off < warn +< error`. Project and bundle declarations can strengthen but cannot lower an +operator floor. Expand and normalize scopes before combining them so renaming a +selector or splitting a rule cannot hide an overlap. Constraints specific to a +rule also combine without widening allowed behaviour. + +Profiles may select reviewed policy sets using the existing profile machinery. +Record the policy source and effective rule set; merely choosing a development +profile must not disable an operator error. Do not introduce a public +`--ignore-policy` escape hatch. An author may remove a project-owned optional +rule when no stronger authority requires it, but cannot bypass the runtime +capability boundary. + +The initial release needs no universal `strict` preset. A future preset must +have a versioned, enumerable rule set and cannot gain new blocking rules on an +unrelated software upgrade. Report-only adoption precedes error enforcement. +Narrow exceptions require an independently reviewed future contract; do not ship +a blanket suppression file that silently makes strict mode meaningless. + +## 8. Evaluation and diagnostics + +Run declaration coverage through the semantic linter's typed inventory. Separate +pure policy evaluation from manifest loading, capability observation, and runner +effects. Evaluate applicable errors before starting any selected user action. +Inspection and dry-run do not execute state probes to satisfy a maturity rule; +they check declaration evidence only. + +A warning reports a gap without changing a successful command's exit status. An +error uses the existing validation/policy failure class and stops execution. +Human and JSON output include rule ID, severity, selected subject, definition +span, policy-source span, and one local remedy. Reuse Fluent localization, +structured-result envelopes, and redaction metadata; do not leak input values or +probe output in diagnostics or metric labels. + +`context --json` describes supported rules and effective settings through the +existing metadata surface. `check --json` reports findings. Neither depends on a +new `explain` command, whose separate roadmap evaluation remains unresolved. +Supported basic manifests must have no new maturity messages under default +settings, including verbose warnings that imply untyped usage is deprecated. + +## 9. Acceptance, learning loop, and rollout + +Retain the exact quickstart fixture and a small three-command project. Snapshot +parsed declarations, generated commands, default diagnostics, and selected +execution behaviour before and after every feature. Compare observed child +arguments and filesystem effects, not just apparent YAML similarity. + +Add one-feature-only examples: a typed worker input without a context, a state +without typed inputs, one cleanup root without a state, and one pool on a legacy +command. Combine annotated and unannotated actions under an ordinary aggregate. +Assert that unrelated invocation starts no probes and creates no state records. + +Property-test severity monotonicity, scope composition, order independence, +namespace resolution, and inability to weaken operator constraints. End-to-end +tests must cover profiles, imported policies, unknown rule versions, warning +versus error exits, selected versus whole-manifest checks, and generated-plan +replay under the applicable trusted policy. + +Document and measure onboarding separately from the Cuprum migration. The +quickstart may not gain required declarations. Each enhancement must demonstrate +its local benefit and explicitly identify any remaining shell helper; moving +boilerplate to an unreviewed imaginary bundle does not count as simplification. +Do not claim a usability improvement from line count alone. + +## 10. Alternatives and outstanding decisions + +Mandatory maturity levels would make advanced features contagious. Automatically +promoting projects by size or feature count would alter semantics unexpectedly. +A global strict mode with an evolving implicit rule list would make upgrades +break otherwise unchanged manifests. Separate validators per feature would +duplicate source handling and reporting. + +Ratify policy-source placement within the shared configuration contract, +selected-closure inspection metadata, and the semantic linter's reusable +inventory boundary before implementation. Rule-specific exceptions, named +presets, pattern selectors, and context-coverage rules remain deferred. + +## 11. Recommendation + +Ratify the shallow-end compatibility contract before expanding the language. +Deliver a small, opt-in, scope-explicit rule mechanism over the shared linter, +with stronger policies controlled by the appropriate authority rather than +imposed on every Netsuke user. + +[roadmap]: ../roadmap-progressive-enhancement.md +[states]: 0013-managed-states-and-probes.md +[inputs]: 0014-typed-task-inputs.md +[artefacts]: 0015-artefact-ownership-and-scoped-cleanup.md +[contention]: 0016-named-contention-classes.md diff --git a/docs/roadmap-progressive-enhancement.md b/docs/roadmap-progressive-enhancement.md new file mode 100644 index 000000000..9dc77a026 --- /dev/null +++ b/docs/roadmap-progressive-enhancement.md @@ -0,0 +1,442 @@ +# Netsuke progressive-enhancement roadmap + +This document continues the [active roadmap](roadmap.md) and the +[composition roadmap](roadmap-composition.md) with phases 20 to 25. It tracks +proposed work, not implemented features. Existing task identifiers and +completion states remain unchanged. Dependencies, rather than phase-number +order, determine delivery sequencing. + +The product hypothesis is that Netsuke can remove repeated orchestration +machinery while retaining its shallow end. The unchanged quickstart must remain +useful. Each feature must work independently with ordinary commands before a +combined Cuprum migration serves as its integration canary. External bundles, +new named execution contexts, and a universal strict mode are not prerequisites. + +## Contract ownership and integration boundaries + +- [RFC 0013](rfcs/0013-managed-states-and-probes.md) owns states, built-in and + external probes, preparation evidence, and operation semantics. +- [RFC 0014](rfcs/0014-typed-task-inputs.md) owns optional root inputs and + shares parameter validation with RFC 0003, rather than duplicating it. +- [RFC 0015](rfcs/0015-artefact-ownership-and-scoped-cleanup.md) owns declared + artefact scope and standardized cleanup. +- [RFC 0016](rfcs/0016-named-contention-classes.md) owns public contention + declarations lowered to Ninja pools. +- [RFC 0017](rfcs/0017-progressive-enhancement-and-maturity-policies.md) owns + shallow-end compatibility and opt-in policy composition. + +RFC 0001 and phases 12 to 14 retain ownership of command parsing, argv, +execution, process cleanup, capability-scoped paths, and persisted action plans. +Phase 11 retains trusted shell selection. Phases 16 to 19 retain include and +bundle composition. Phase 5 and OrthoConfig retain generic profile, schema, +metadata, redaction, and result machinery. The semantic linter tracked by issue +#592 retains the reusable manifest-analysis boundary. No new task may duplicate +those implementations merely to avoid an explicit integration dependency. + +New public grammar is proposed, not shipped: RFC 0014 proposes `--input +NAME=VALUE` on manifest-compiling commands; RFC 0015 proposes `clean --artefact +NAME`. Register both with the canonical vocabulary and metadata before delivery. +Use existing `check`, `context --json`, `--dry-run`, `--force`, and `--no-input` +contracts. Do not introduce an unreviewed `explain` command or new exit-code +system. + +Every implementation task includes relevant unit and behavioural tests. Use +Proptest for normalization and algebraic invariants, bounded Kani harnesses for +pure transition logic where useful, and subprocess end-to-end tests for process, +filesystem, locking, and backend boundaries. Reuse installed or cached tooling; +this roadmap does not require new source-built proof tools for ordinary gates. + +## 20. Preserve the shallow end before adding contracts + +Hypothesis: optional semantic annotations can add guarantees without increasing +the prerequisites or required vocabulary of a first Netsuke build. + +### 20.1. Establish compatibility and schema admission fixtures + +Outcome: a release can demonstrate unchanged basic behaviour rather than merely +assert it. The fixtures expose whether later syntax has become contagious. + +- [ ] 20.1.1. Ratify the progressive-enhancement contracts and version gates. + - [ ] Review RFCs 0013 to 0017, resolve their outstanding schema decisions, + and record accepted decisions through the normal ADR process. + - [ ] Coordinate manifest and persisted-plan version allocation with 12.1.1, + 16.1.1, and 17.1.1 without requiring bundle implementation first. + - [ ] Record operation-union ownership, feature-specific capability reporting, + and rejection of unsupported syntax. See RFC 0017 sections 2 and 3. +- [ ] 20.1.2. Add unchanged-basic-workflow acceptance fixtures. Requires 20.1.1. + - [ ] Preserve the exact quickstart manifest, scalar shell recipes, ordinary + variables, and the list-of-mappings action/target structure. + - [ ] Test actual child commands, default diagnostics, files, and absence of + state records, probe execution, installation, and new network activity. + - [ ] Add a mixed annotated/unannotated aggregate fixture. Success: enabling + one feature does not require declarations on unrelated nodes. + +### 20.2. Make the common boundaries independently consumable + +Outcome: features can reuse schema and reporting facilities without forcing all +other advanced features into their minimum implementation. + +- [ ] 20.2.1. Publish a feature and metadata integration contract. Requires + 20.1.1; coordinate with phase 5's context/profile work. + - [ ] Specify optional feature reporting and unknown-version diagnostics in + the existing JSON envelope and command metadata source of truth. + - [ ] Allocate shared typed-parameter validation to one owner with 17.1.3; + keep bundle resolution and root input sourcing separate. + - [ ] Define the common capability-scoped resource identity and lease seam + used by states and owned producers/cleanup, including its reuse limits. +- [ ] 20.2.2. Add progressive documentation acceptance checks. Requires 20.1.2 + and 20.2.1. + - [ ] Keep the first-page example unchanged and stage one-feature-only + examples outside the mandatory onboarding path. + - [ ] Check examples against accepted schemas when those schemas land; + distinguish proposed fragments from runnable examples until then. + - [ ] Record the before/after onboarding vocabulary and execution footprint. + Reject required advanced declarations without an explicit compatibility + decision, not an unnoticed documentation rewrite. + +- [ ] 20.2.3. Implement optional capability-scoped resource leases. Requires + 20.2.1. + - [ ] Supply one bounded, canonically ordered lease implementation for + cooperating producers, state consumers, and scoped cleanup. + - [ ] Test cross-process contention, cancellation, path aliases, missing + resource roots, and unsupported filesystems; create no runtime lease records + for features that an invocation does not use. + - [ ] Keep the seam independent of state declarations and Ninja scheduling. + Feature tasks integrate it rather than creating competing lock systems. + +## 21. Typed inputs with one parameter contract + +Hypothesis: optional typed public inputs make task configuration predictable +without annotating internal variables or creating a second profile system. + +### 21.1. Validate values before executing a manifest + +Outcome: root inputs and bundle parameters accept and reject the same values, +with errors local to the responsible declaration or source. + +- [ ] 21.1.1. Implement the normalized parameter contract. Requires 20.2.1. + - [ ] Reuse or extract RFC 0003 section 6's types, constraints, default + validation, exposure metadata, and bounded diagnostics. + - [ ] Test Boolean/integer distinction, overflow, collection bounds, duplicate + keys, choices, empty values, and path capability non-authority. + - [ ] Share a conformance corpus with bundle work without requiring bundle + loading. See RFC 0014 sections 4 and 7. +- [ ] 21.1.2. Add optional root input declarations and immutable resolution. + Requires 21.1.1 and 20.1.2. + - [ ] Parse `inputs`, preserve ordinary `vars`, detect namespace collisions, + and validate before Jinja expansion. + - [ ] Support one-value promotion without implicit aliases or executable + defaults. Retain declaration and reference spans. + - [ ] Property-test normalization stability and run the unchanged-basic + fixture. See RFC 0014 sections 3, 4, and 8. + +### 21.2. Bind explicit callers and profiles to the same interface + +Outcome: a user can identify which source supplied a value without learning a +new configuration stack. Precedence cases decide whether the contract is clear. + +- [ ] 21.2.1. Wire type-directed CLI and configuration input sources. Requires + 21.1.2 and 20.2.1. + - [ ] Register `--input NAME=VALUE` in canonical metadata; reject unknown and + duplicate names, shell splitting, and malformed typed JSON collections. + - [ ] Integrate existing configuration/profile provenance and ratify overlay + order with phase 5; apply operator constraints after source selection. + - [ ] Test every source precedence pair and default redaction in errors, + verbose output, and JSON. See RFC 0014 sections 5 and 7. +- [ ] 21.2.2. Preserve resolved inputs through graph and plan generation. + Requires 21.2.1 and 12.3.1 for structured-plan integration. + - [ ] Add used values to existing fingerprints and preserve argv splicing + through RFC 0001; do not create a second hashing or cache system. + - [ ] Freeze values for persisted-plan replay and test a later changed ambient + profile cannot replace them. + - [ ] Test include/bundle boundaries when 16.3.3 and 17.4.3 are available; + keep this composition matrix separate from local input delivery. + +### 21.3. Demonstrate useful annotation without broad migration + +Outcome: the worker-count canary validates one public knob and leaves all +unrelated task configuration unchanged. + +- [ ] 21.3.1. Publish and execute the typed-worker canary. Requires 21.2.2. + - [ ] Observe real Cargo/nextest or fixture-equivalent argv for distinct build + and test workers, including argument-list conflict rejection. + - [ ] Add a compact guide example and effective-value provenance output. + - [ ] Run one-input-only and mixed untyped/typed fixtures. Success: no + context, state, or bundle is required to use a validated worker count. + +## 22. Named contention without a second scheduler + +Hypothesis: one optional contention annotation can control shared build pressure +without confusing dependency order or subprocess worker limits. + +### 22.1. Resolve one class per executable edge + +Outcome: the public model is small enough to lower directly to existing backend +pool machinery, with no hidden overlapping-resource scheduler. + +- [ ] 22.1.1. Add bounded contention declarations and reference validation. + Requires 20.1.1 and 20.2.1. + - [ ] Implement positive integer capacities, one scalar action/target class, + operator ceilings, reserved names, and source-local errors. + - [ ] Reject aggregate annotations, multiple classes, and unsupported + rule-default forms. See RFC 0016 sections 3 and 4. + - [ ] Add a legacy-command fixture; typed input support is optional and + integrates only after 21.1.2. +- [ ] 22.1.2. Lower resolved classes into Ninja pool definitions. Requires + 22.1.1. + - [ ] Reuse `Action.pool`, emit stable bounded names, and preserve dependency + and dyndep semantics. Reject console/custom-pool conflicts. + - [ ] Include complete action sequences and persisted scheduling metadata; + integrate with 12.3.1 rather than reimplementing the codec. + - [ ] Property-test declaration-order independence and unannotated-output + compatibility. See RFC 0016 section 5. + +### 22.2. Verify concurrency and communicate its limits + +Outcome: measured edge concurrency, rather than syntax snapshots alone, proves +the guarantee and exposes its invocation-only boundary. + +- [ ] 22.2.1. Add controlled-process concurrency tests. Requires 22.1.2. + - [ ] Assert capacities one and two under larger global `-j`, and show an + unrelated action can run while a class is occupied. + - [ ] Exercise multi-command edges, failure, cancellation, and console + rejection without relying solely on sleeps. + - [ ] Demonstrate that separate Ninja invocations do not share the limit. See + RFC 0016 sections 6 and 8. +- [ ] 22.2.2. Publish class inspection and the native-build canary. Requires + 22.2.1 and 20.2.2. + - [ ] Show requested/effective capacity, source, and invocation-only scope. + - [ ] Document that slots are not CPU/memory quotas or internal worker counts. + - [ ] Add explicit shared-class bundle binding only after 17.4.3; local class + support must not depend on external acquisition. + +## 23. Verified preparation without mandatory environments + +Hypothesis: built-in readiness checks plus optional external functional probes +remove preparation glue while preserving explicit preconditions and tool choice. + +### 23.1. Define readiness separately from execution and repair + +Outcome: a pure transition model establishes when preparation is permitted +before process execution or durable records complicate the implementation. + +- [ ] 23.1.1. Implement state definitions and the readiness algebra. Requires + 20.1.1 and 20.2.1. + - [ ] Model kinds, declared identity inputs, optional preparation, all four + outcomes, and the three operations in RFC 0013 sections 3 to 5. + - [ ] Property-test that unknown never authorizes repair, degraded requires + explicit acceptance, and ensure performs at most one preparation attempt. + - [ ] Reject unsupported incremental-target state checks instead of letting + Ninja skip readiness verification. See RFC 0013 section 8. +- [ ] 23.1.2. Implement default built-in probes through existing seams. Requires + 23.1.1 and 12.2.3 where interpreter execution is needed. + - [ ] Deliver directory, file, and precisely specified Python-environment + probes without requiring third-party plugins. + - [ ] Distinguish object presence, interpreter validity, package verification, + and preparation identity; never overstate a probe's evidence. + - [ ] Test missing, corrupted, unreadable, replaced, and incompatible objects + through injected environment/filesystem adapters. + +### 23.2. Execute leased state operations with durable evidence + +Outcome: cooperating invocations can validate and prepare mutable state without +stale success records or a second scheduling loop. + +- [ ] 23.2.1. Integrate state units into the structured runner and plan codec. + Requires 23.1.2, 12.3.1, and the phase 12 process-lifecycle contract. + - [ ] Execute require, ensure, and prepare with fail-fast consumer ordering; + never probe during check, graph generation, help, or dry-run. + - [ ] Preserve resolved argv, environment, cwd, provenance, and typed result + mapping. Do not add state-private command execution. + - [ ] Reject unsupported replay versions. See RFC 0013 sections 5 and 7. +- [ ] 23.2.2. Implement bounded integrity leases and atomic state records. + Requires 23.2.1 and 20.2.3. + - [ ] Hold canonically ordered resource leases through each action's state + consumers; reject conflicting identities for one mutable path. + - [ ] Publish success only after post-verification and invalidate observations + across mutation. Bound retention, acquisition, and interrupted recovery. + - [ ] Test separate processes, damaged records, replacement races, and + interruption before publication. See RFC 0013 sections 4 and 8. + +### 23.3. Admit external functional checks without implicit repair + +Outcome: a repository-owned executable can supply a bounded readiness check +without requiring a provider implementation or a Nagios service. + +- [ ] 23.3.1. Implement the optional Nagios-style protocol adapter. Requires + 23.2.1. + - [ ] Map exits 0 to 3 exactly as RFC 0013 section 6 specifies and preserve + distinct spawn, signal, timeout, protocol, and output-limit reasons. + - [ ] Combine built-in and external evidence without allowing stdout to forge + identity or override a failing result. + - [ ] Test all codes, partial output, empty summaries, performance suffixes, + bad encoding, and contradictory success text. +- [ ] 23.3.2. Enforce probe budgets, authority, and process cleanup. Requires + 23.3.1 and the shared bounded process-tree termination facility. + - [ ] Apply operator-capped deadlines and combined output limits during + concurrent collection; kill and reap hung descendants on cancellation. + - [ ] Reject unauthorized external code and unsupported command fields; + preserve allowed-shell policy and redact diagnostic data. + - [ ] Use hostile fixture probes to prove bounded behaviour. An observational + convention must not be described as an operating-system sandbox. + +### 23.4. Validate state value in a real migration + +Outcome: the canary removes redundant setup while retaining intentionally strict +preconditions, and default builds pay no state-management cost. + +- [ ] 23.4.1. Add the Cuprum preparation and extension-guard canaries. Requires + 23.2.2 and 23.3.2. + - [ ] Verify an environment, remove a required component, and demonstrate + fresh detection rather than a stale timestamp or probe cache hit. + - [ ] Preserve the exact restricted extension suite and prove require-only + execution never invokes Maturin to repair a missing extension. + - [ ] Test ordinary commands beside stateful actions and assert zero probes + for the unchanged hello-world selection. +- [ ] 23.4.2. Publish state diagnostics, guarantees, and the probe author guide. + Requires 23.4.1 and 20.2.2. + - [ ] Document smallest built-in use, custom probes, result meanings, trust, + lease scope, failure recovery, and the lack of automatic teardown. + - [ ] Add metadata and localization through existing surfaces. + - [ ] Integrate optional typed inputs, named contexts, and bundle runtime + resources only through their accepted owners, not mandatory prerequisites. + +## 24. Owned artefacts and bounded cleanup + +Hypothesis: explicit disposable ownership can make cleanup predictable without +requiring a new output-store layout or annotating every existing target. + +### 24.1. Establish ownership before permitting deletion + +Outcome: one path has one explicit owner, and report registration does not +silently create caching or overlapping output producers. + +- [ ] 24.1.1. Implement exact-path artefact definitions and producer references. + Requires 20.1.1 and 20.2.1. + - [ ] Add file/directory kind, descriptive roles, optional parent creation, + and `produces` metadata. See RFC 0015 sections 3 and 4. + - [ ] Reject overlapping/case-equivalent ownership and duplicate producers; + reconcile explicit declarations with existing target outputs. + - [ ] Test missing outputs, retained partial outputs, and always-run reports. +- [ ] 24.1.2. Implement the bounded read-only cleanup planner. Requires 24.1.1. + - [ ] Resolve explicit names to workspace capabilities, protect source and + runtime paths, and enumerate complete bounded scope without deletion. + - [ ] Make directory-subtree ownership and pre-existing contents visible; + reject invalid, over-budget, or incomplete previews. + - [ ] Property-test path normalization and scope uniqueness. See RFC 0015 + sections 5 and 6. + +### 24.2. Delete only validated scope through one implementation + +Outcome: recipe cleanup and explicit CLI cleanup share confirmation, +confinement, partial-failure reporting, and fresh execution-time validation. + +- [ ] 24.2.1. Implement capability-scoped deletion and ownership leases. + Requires 24.1.2 and 20.2.3. + - [ ] Use supported handle-relative deletion, no-follow traversal, identity + rechecks, and canonical lease ordering shared with owned producers. + - [ ] Test symlinks, reparse points, hard links, replacement, permissions, + interruption, and external sentinels on supported hosts. + - [ ] Report partial progress honestly and refuse unsupported guarantees. Do + not substitute lexical containment or shell deletion. +- [ ] 24.2.2. Wire the operation and explicit `clean --artefact` selection. + Requires 24.2.1, 12.3.1, and existing mutation metadata integration. + - [ ] Add `clean_owned`, canonical CLI metadata, `--dry-run`, `--force`, and + `--no-input` handling through the shared planner/deleter. + - [ ] Preserve ordinary `clean` behaviour when explicit artefact selection is + absent; never implicitly include caches or environments. + - [ ] Test zero writes in preview, refusal without consent, fresh replay + validation, and bounds that `--force` cannot bypass. + +### 24.3. Integrate lifecycle invalidation and staged migration + +Outcome: cleanup cannot leave trusted readiness evidence for an environment it +has removed, and ordinary workflows retain their independent cleanup choices. + +- [ ] 24.3.1. Integrate state invalidation and selected-closure conflicts. + Requires 24.2.2 and 23.2.2. + - [ ] Invalidate state records before deleting their owned environments under + the same resource lease, including partial-failure paths. + - [ ] Reject simultaneous cleanup and declared production/consumption of the + same resource; a pool must not be treated as semantic ordering. + - [ ] Keep dyndep and command-private temporary cleanup under their existing + owners. See RFC 0015 section 7. +- [ ] 24.3.2. Publish exact-path cleanup examples and a Cuprum canary. Requires + 24.2.2 and 20.2.2; state examples also require 24.3.1. + - [ ] Migrate disjoint output roots without demanding an artefact for every + target, and retain unsupported wildcard cleanup as an explicit recipe. + - [ ] Document ownership assertions, recursive scope, retained caches, preview + limitations, and external-command boundaries. + - [ ] Demonstrate that the one-directory feature works without state syntax. + +## 25. Opt-in maturity policies without compulsory strictness + +Hypothesis: explicit scoped enforcement can support stricter projects without +changing default onboarding, pretending to prove safety, or duplicating linting. + +### 25.1. Reuse semantic analysis for narrowly defined coverage rules + +Outcome: one typed inventory supports both the semantic linter and policy +findings, with no command-text guessing or competing parser. + +- [ ] 25.1.1. Define the reusable policy inventory boundary with issue #592. + Requires 20.2.1 and the semantic linter's accepted inventory contract. + - [ ] Retain resolved recipe units, references, ownership, and provenance; + distinguish policy coverage from schema/runtime correctness. + - [ ] Implement pure rule/subject selection without executing probes or user + actions. See RFC 0017 sections 3 to 6. + - [ ] Add fixtures proving an unrelated selected target does not inherit + another action's coverage requirements. +- [ ] 25.1.2. Implement the initial rules as independently gated checks. + Requires 25.1.1 and each rule's relevant delivered feature. + - [ ] Add structured commands after 12.3.3, typed subjects after 21.2.1, + contention after 22.1.2, states after 23.2.1, and cleanup after 24.2.2. + - [ ] Reject unknown/unsupported rule IDs rather than claim incomplete + enforcement. No rule waits for an unrelated feature. + - [ ] Test exact coverage semantics and remedies from RFC 0017 section 5; do + not certify hermeticity from declaration presence. + +### 25.2. Compose enforcement without weakening trusted constraints + +Outcome: profile and import layering cannot make a project-owned warning replace +an operator error or hide a rule through scope rewriting. + +- [ ] 25.2.1. Add opt-in policy declarations and trust-aware severity merging. + Requires 25.1.1 and the existing configuration provenance boundary. + - [ ] Keep default rules empty; combine overlapping scopes monotonically and + retain the strongest applicable authority. + - [ ] Integrate profiles through phase 5's owner, with no automatic promotion, + blanket bypass, or new configuration loader. + - [ ] Property-test overlap, order independence, scope normalization, and + inability to weaken operator policy. See RFC 0017 section 7. +- [ ] 25.2.2. Wire preflight and existing human/JSON reporting. Requires 25.1.2 + and 25.2.1. + - [ ] Evaluate applicable errors before execution, keep warnings nonblocking, + and preserve ordinary structural errors regardless of coverage severity. + - [ ] Use `check`, `context --json`, shared exit classes, localization, and + redaction; introduce no separate `explain` command. + - [ ] Test selected-closure versus whole-manifest checks and persisted plans + under applicable trusted policy. See RFC 0017 section 8. + +### 25.3. Prove progressive enhancement end to end + +Outcome: small projects and the Monster Makefile can both use Netsuke without +sharing the same configuration burden. + +- [ ] 25.3.1. Run the progressive and strict-policy acceptance matrix. Requires + 25.2.2, 21.3.1, 22.2.2, 23.4.2, and 24.3.2. + - [ ] Run unchanged hello-world, one-feature-only cases, mixed aggregates, + report-only adoption, and explicit scoped errors. + - [ ] Add imported-policy coverage after 17.4.3 without blocking local + adoption; reject unknown private selectors and weakening imports. + - [ ] Assert zero default maturity warnings, no unrelated probes, unchanged + legacy command semantics, and no mandatory external tools or bundles. +- [ ] 25.3.2. Publish release guidance from measured canary results. Requires + 25.3.1 and 20.2.2. + - [ ] Describe gradual adoption and reversal, exact guarantees, operator + authority, and the limits of declaration-only checks. + - [ ] Compare repository-maintained machinery across manifests, helpers, and + real bundles; do not hide complexity behind nonexistent packages. + - [ ] Keep all first-page prerequisites and required declarations unchanged. + Reconsider any feature that cannot demonstrate local benefit before broad + promotion; no universal strict preset is required for release. From 12002328ecc73718f09fd81083af8b45318fb703 Mon Sep 17 00:00:00 2001 From: leynos Date: Sat, 19 Sep 2026 13:16:18 +0200 Subject: [PATCH 2/3] Format progressive-enhancement docs with make fmt Run the canonical `make fmt` over the seven new documents so they match the repository Markdown conventions: prose re-wrapped to 80 columns and tables padded to mdtablefix-canonical form. No wording changed. `mdtablefix --wrap` moved the `#592` issue reference to the start of a line, where CommonMark parses `#592` as an ATX level-1 heading, so markdownlint reported MD022 and MD025 against the roadmap. The committed text carried the same latent mis-parse; only the rewrapping made it visible. Backtick the reference, matching the existing convention for issue identifiers in `docs/`. Co-Authored-By: Claude Code --- docs/rfcs/0013-managed-states-and-probes.md | 134 +++++++++--------- docs/rfcs/0014-typed-task-inputs.md | 96 +++++++------ ...5-artefact-ownership-and-scoped-cleanup.md | 103 +++++++------- docs/rfcs/0016-named-contention-classes.md | 68 ++++----- ...ssive-enhancement-and-maturity-policies.md | 106 +++++++------- docs/roadmap-progressive-enhancement.md | 42 +++--- 6 files changed, 282 insertions(+), 267 deletions(-) diff --git a/docs/rfcs/0013-managed-states-and-probes.md b/docs/rfcs/0013-managed-states-and-probes.md index 747e0cbe3..1b423cda8 100644 --- a/docs/rfcs/0013-managed-states-and-probes.md +++ b/docs/rfcs/0013-managed-states-and-probes.md @@ -16,8 +16,8 @@ optional preparation recipe. Built-in probes serve the normal case; external functional checks support project-specific conditions without a provider plugin or a Nagios installation. -A plain `command: uv sync` remains valid. Adding a state is worthwhile only when -an author needs an explicit precondition, validated reuse, or a shared +A plain `command: uv sync` remains valid. Adding a state is worthwhile only +when an author needs an explicit precondition, validated reuse, or a shared preparation contract. No context, typed input, ownership declaration, maturity policy, or bundle is compulsory. [RFC 0017][maturity] owns this shallow-end compatibility requirement. @@ -26,18 +26,19 @@ compatibility requirement. A directory timestamp cannot establish that a virtual environment contains the requested interpreter and packages. A successful previous installation cannot -establish that an external service still works. Conversely, a test that requires -an already-installed extension must not silently build it. +establish that an external service still works. Conversely, a test that +requires an already-installed extension must not silently build it. -[RFC 0001][commands] owns structured execution; its working-directory, temporary -resource, and shell amendments remain authoritative. This RFC adds execution -units to that runner, not a template-time subprocess facility. Cargo, uv, and -other ecosystem tools retain dependency resolution and incremental compilation. +[RFC 0001][commands] owns structured execution; its working-directory, +temporary resource, and shell amendments remain authoritative. This RFC adds +execution units to that runner, not a template-time subprocess facility. Cargo, +uv, and other ecosystem tools retain dependency resolution and incremental +compilation. ## 3. Progressive authoring -The following proposed fragment uses a built-in presence probe. It promises only -that `build` is a directory, not that its contents form a valid build: +The following proposed fragment uses a built-in presence probe. It promises +only that `build` is a directory, not that its contents form a valid build: ```yaml states: @@ -57,8 +58,8 @@ actions: `probe: builtin` boilerplate. Initial kinds are `directory`, `file`, and `python-venv`. The first two check the declared object type without following symlinks. They make no content-freshness claim. `python-venv` checks the actual -interpreter, its environment prefix, and any declared interpreter constraint; it -must not advertise package-set verification that it does not implement. +interpreter, its environment prefix, and any declared interpreter constraint; +it must not advertise package-set verification that it does not implement. A richer proposed fragment keeps a functional check separate from preparation: @@ -85,15 +86,15 @@ actions: Here the built-in environment checks still run. The external check adds a condition; it cannot bypass the built-in checks or forge preparation identity. -The script is project-owned runtime code, not an example of shipped tooling. Its -declared condition must cover any package expectations on which consumers rely. -A lockfile digest records preparation inputs, not current installation +The script is project-owned runtime code, not an example of shipped tooling. +Its declared condition must cover any package expectations on which consumers +rely. A lockfile digest records preparation inputs, not current installation integrity. -`kind: custom` supports conditions without a suitable built-in kind. It requires -an explicit external probe and cannot acquire stronger verification claims than -that probe supplies. A custom state may omit `path` when it observes a service; -that does not authorize network access or remote mutation. +`kind: custom` supports conditions without a suitable built-in kind. It +requires an explicit external probe and cannot acquire stronger verification +claims than that probe supplies. A custom state may omit `path` when it +observes a service; that does not authorize network access or remote mutation. ## 4. State definition and identity @@ -167,48 +168,48 @@ identity mismatch and a functional failure retain distinct reason codes. The optional `nagios` protocol uses the conventional plugin exit statuses[^1]: -| Exit | Probe result | Default state-operation behaviour | -| --- | --- | --- | -| 0 | `ready` | Continue only if every other condition passes. | -| 1 | `degraded` | Stop without repair; explicit acceptance may continue. | -| 2 | `not_ready` | An ensure may prepare once; a requirement never prepares. | -| 3 | `unknown` | Stop without repair. | +| Exit | Probe result | Default state-operation behaviour | +| ---- | ------------ | --------------------------------------------------------- | +| 0 | `ready` | Continue only if every other condition passes. | +| 1 | `degraded` | Stop without repair; explicit acceptance may continue. | +| 2 | `not_ready` | An ensure may prepare once; a requirement never prepares. | +| 3 | `unknown` | Stop without repair. | Table 1: Nagios-style statuses and their Netsuke interpretation. Other exits, signals, spawn failure, timeout, malformed output encoding, and output-budget exhaustion produce `unknown` with a distinct execution reason. A -process's exit status is authoritative; a success-looking message cannot convert -a failing exit into readiness. The first stdout line supplies a human summary. -Remaining stdout and stderr are bounded diagnostic data. A `|` suffix may be -retained as uninterpreted performance data, but it never controls state, +process's exit status is authoritative; a success-looking message cannot +convert a failing exit into readiness. The first stdout line supplies a human +summary. Remaining stdout and stderr are bounded diagnostic data. A `|` suffix +may be retained as uninterpreted performance data, but it never controls state, identity, scheduling, or authorization. No metric parser or Nagios daemon is required. Proposed defaults are a ten-second wall-clock deadline and 64 KiB combined stdout/stderr. Optional `probe.timeout_seconds` and `probe.max_output_bytes` are positive integers; trusted operator limits cap requested bounds. Drain both -streams -concurrently, enforce limits during collection, and terminate and reap the owned -process tree on timeout, cancellation, or excess output. No shell redirection or -unbounded capture is necessary. A probe cannot change command-local runtime -bindings in the consuming action. +streams concurrently, enforce limits during collection, and terminate and reap +the owned process tree on timeout, cancellation, or excess output. No shell +redirection or unbounded capture is necessary. A probe cannot change +command-local runtime bindings in the consuming action. Only ordinary structured invocation fields needed for a functional check are accepted inside `probe.external`: `invoke`, `env`, `cwd`, and allowed `shell` selection. Reject pipelines, stream files, runtime capture, nested state -operations, and cleanup there. Explicit named-shell probes remain possible under -RFC 0011; direct invocation is the default. Probe return codes are not Netsuke's -public CLI exit codes: the existing structured-result mapping owns that -translation. - -Probes should be observational and idempotent, but executing arbitrary code does -not prove either property. The runner must not claim a read-only or network -sandbox that it does not supply. An operator can forbid external probes or -restrict their executable identities. Project configuration and bundle content -cannot weaken those restrictions. Lack of authorization is an error, not a -reason to bypass the probe. Do not run probes or preparation merely to render -help, inspect context, check a manifest, generate a graph, or preview work. +operations, and cleanup there. Explicit named-shell probes remain possible +under RFC 0011; direct invocation is the default. Probe return codes are not +Netsuke's public CLI exit codes: the existing structured-result mapping owns +that translation. + +Probes should be observational and idempotent, but executing arbitrary code +does not prove either property. The runner must not claim a read-only or +network sandbox that it does not supply. An operator can forbid external probes +or restrict their executable identities. Project configuration and bundle +content cannot weaken those restrictions. Lack of authorization is an error, +not a reason to bypass the probe. Do not run probes or preparation merely to +render help, inspect context, check a manifest, generate a graph, or preview +work. ## 7. Execution settings and security @@ -220,8 +221,8 @@ resolve them through the same command plan rather than a state-private resolver. Probe and preparation commands may intentionally use different tools. Record both effective settings and diagnose accidental environment-root mismatches; do not replace an unavailable requested interpreter with an ambient one. External -code has the same trust implications as a build recipe. State annotations do not -make an untrusted checkout safe to execute. +code has the same trust implications as a build recipe. State annotations do +not make an untrusted checkout safe to execute. Redact probe arguments and output through the shared diagnostic policy, bound messages, and escape terminal control sequences. Do not export raw probe output @@ -230,13 +231,13 @@ seams, not process-global mutation or a separate configuration reader. ## 8. Scheduling, mutation, and durable records -Keep state operations inside ordinary Ninja-scheduled action edges. They are not -graph-discovery operations, and their results cannot change manifest-time +Keep state operations inside ordinary Ninja-scheduled action edges. They are +not graph-discovery operations, and their results cannot change manifest-time conditions or add undeclared dependencies. The action-plan codec must represent them explicitly and reject unknown versions during replay. -State evidence is not itself a Ninja output. A state-using action must either be -an always-run action or explicitly opt into always-run execution; reject an +State evidence is not itself a Ninja output. A state-using action must either +be an always-run action or explicitly opt into always-run execution; reject an incremental file target with state operations until a separate runtime-validation contract can guarantee that its checks actually run. This avoids silently skipping a readiness check because an unrelated output is up to @@ -244,11 +245,11 @@ date. For managed mutable paths, acquire a workspace-scoped advisory lease for every state referenced by an action before its first command, in canonical resource -order, and retain the leases through the last consumer in that action. These are -integrity locks for cooperating invocations, not a replacement scheduler. Bound -acquisition and never acquire a lease while recursively invoking Netsuke. Reject -different state identities claiming the same mutable path within one selected -build closure; isolated paths are the first-version remedy. +order, and retain the leases through the last consumer in that action. These +are integrity locks for cooperating invocations, not a replacement scheduler. +Bound acquisition and never acquire a lease while recursively invoking Netsuke. +Reject different state identities claiming the same mutable path within one +selected build closure; isolated paths are the first-version remedy. A standalone preparation action does not hold a lease for its dependants. Consumers therefore need their own `require_state` or `ensure_state` operation. @@ -265,9 +266,10 @@ pretend to serialize access. Store records in a versioned, bounded runtime namespace separate from dyndep sidecars. Publish records atomically only after verification. Interrupted preparation leaves no success record; partial resources remain unverified and -may need explicit remediation. Cleanup invalidates records under the same lease. -Do not automatically remove an environment after failure or run undeclared -teardown commands. Artefact ownership is a separate optional contract. +may need explicit remediation. Cleanup invalidates records under the same +lease. Do not automatically remove an environment after failure or run +undeclared teardown commands. Artefact ownership is a separate optional +contract. ## 9. Verification and acceptance @@ -287,15 +289,16 @@ processes, and interrupt between preparation and record publication. A normal hello-world build must start zero probes and create no state records. The Cuprum canary must preserve its restricted extension-test selection and -verify that `require_state` reports a missing extension without running Maturin. -Measure setup reduction without counting a stale probe result as a cache hit. +verify that `require_state` reports a missing extension without running +Maturin. Measure setup reduction without counting a stale probe result as a +cache hit. ## 10. Alternatives, migration, and outstanding decisions Timestamp-only stamps are insufficient for live readiness. A mandatory Nix-like store would require a different workflow and is out of scope. External checks -alone would force every project to reinvent common checks; built-ins alone would -force plugin development for ordinary functional conditions. +alone would force every project to reinvent common checks; built-ins alone +would force plugin development for ordinary functional conditions. Adoption is per action. Existing commands and environment management remain supported, and no package installer becomes a prerequisite for unrelated work. @@ -318,4 +321,5 @@ readiness, preparation evidence, and artefact ownership distinct. [maturity]: 0017-progressive-enhancement-and-maturity-policies.md [commands]: 0001-structured-command-blocks.md [contention]: 0016-named-contention-classes.md -[^1]: [Nagios plugin development guidelines](https://nagios-plugins.org/doc/guidelines.html), plugin return codes. +[^1]: [Nagios plugin development guidelines](https://nagios-plugins.org/doc/guidelines.html), + plugin return codes. diff --git a/docs/rfcs/0014-typed-task-inputs.md b/docs/rfcs/0014-typed-task-inputs.md index dbfa4f989..2e590bdd4 100644 --- a/docs/rfcs/0014-typed-task-inputs.md +++ b/docs/rfcs/0014-typed-task-inputs.md @@ -10,15 +10,17 @@ ## 1. Summary -Add an optional `inputs` mapping for validated, discoverable task configuration. -Ordinary `vars` remain valid and keep their current semantics. An author can -promote one externally meaningful value without annotating every variable, -rewriting every command, adopting bundles, or declaring an execution context. +Add an optional `inputs` mapping for validated, discoverable task +configuration. Ordinary `vars` remain valid and keep their current semantics. +An author can promote one externally meaningful value without annotating every +variable, rewriting every command, adopting bundles, or declaring an execution +context. Use the same type vocabulary, value validation, and redaction contract as -[RFC 0003's bundle parameters][bundles]. Do not create a rival parameter system. -The [maturity-policy RFC][maturity] permits explicit organizations or projects -to require selected contracts; it does not turn annotations into a default gate. +[RFC 0003's bundle parameters][bundles]. Do not create a rival parameter +system. The [maturity-policy RFC][maturity] permits explicit organizations or +projects to require selected contracts; it does not turn annotations into a +default gate. ## 2. Problem and current boundaries @@ -62,10 +64,11 @@ actions: invoke: pytest -n {{ inputs.workers }} ``` -Changing `workers` to `inputs.workers` makes the promotion explicit. There is no -implicit alias, mutation of `vars`, or requirement to annotate unrelated values. -Structured invocation is recommended for dynamic arguments, but adopting an -input does not silently convert a legacy shell string into direct execution. +Changing `workers` to `inputs.workers` makes the promotion explicit. There is +no implicit alias, mutation of `vars`, or requirement to annotate unrelated +values. Structured invocation is recommended for dynamic arguments, but +adopting an input does not silently convert a legacy shell string into direct +execution. ## 4. Definition and value model @@ -76,26 +79,27 @@ there is no competing `argv` or `enum` type in the initial grammar. A definition contains required `type`, optional `default`, `description`, and `expose`, plus applicable constraints. No default means required. `minimum` and -`maximum` constrain integers; `choices` contains a nonempty, duplicate-free list -of values of the declared scalar type. Reject inverted ranges, mismatched +`maximum` constrain integers; `choices` contains a nonempty, duplicate-free +list of values of the declared scalar type. Reject inverted ranges, mismatched constraints, unknown fields, and defaults that violate their own contract. Definitions and defaults are literal data, like bundle descriptors, not Jinja programs. Computed internal values belong in ordinary `vars` after input -resolution. No executable, clock, filesystem, or network default expressions are -necessary. Required but unused root inputs still fail definition resolution; -authoring an optional facility must not introduce an unrelated required input. +resolution. No executable, clock, filesystem, or network default expressions +are necessary. Required but unused root inputs still fail definition +resolution; authoring an optional facility must not introduce an unrelated +required input. Use the existing supported integer range and reject out-of-range values. A -Boolean is not an integer; `1.0` is not silently accepted as `1`. Strings remain -strings, including empty strings. Null is not an omitted optional value unless a -future shared nullable-type contract explicitly introduces it. +Boolean is not an integer; `1.0` is not silently accepted as `1`. Strings +remain strings, including empty strings. Null is not an omitted optional value +unless a future shared nullable-type contract explicitly introduces it. `path` is a typed path value, not a filesystem capability or permission grant. Validation checks encoding and lexical shape without creating files. A consumer -that opens, executes, or deletes the path applies its own workspace and operator -capability constraints at use time. Collections are homogeneous, bounded, and -validated element by element; sequence order is significant. +that opens, executes, or deletes the path applies its own workspace and +operator capability constraints at use time. Collections are homogeneous, +bounded, and validated element by element; sequence order is significant. `inputs` is immutable during manifest expansion. Introducing this reserved namespace must preserve manifests that do not opt into the new schema. When an @@ -118,18 +122,18 @@ CLI parsing is type-directed, not shell splitting or arbitrary YAML evaluation: - Sequences and mappings accept JSON of the declared shape, with bounded size and explicit duplicate-key rejection. -Reject unknown names and duplicate CLI occurrences. Do not interpret an input as -an executable shell fragment. Direct argv interpolation must preserve spaces, -metacharacters, empty elements, and sequence boundaries. The shell still owns -interpretation inside an explicitly selected shell recipe. +Reject unknown names and duplicate CLI occurrences. Do not interpret an input +as an executable shell fragment. Direct argv interpolation must preserve +spaces, metacharacters, empty elements, and sequence boundaries. The shell +still owns interpretation inside an explicitly selected shell recipe. Precedence, highest first, is explicit CLI input, explicitly selected profile -input, resolved configuration input, then manifest default. Existing OrthoConfig -rules determine precedence within configuration sources; this RFC does not -reorder system, user, project, and explicitly selected configuration files. The -profile integration task must reconcile its own overlay order with this contract -before implementation. No automatically inferred environment variables supply -task inputs in the first version. +input, resolved configuration input, then manifest default. Existing +OrthoConfig rules determine precedence within configuration sources; this RFC +does not reorder system, user, project, and explicitly selected configuration +files. The profile integration task must reconcile its own overlay order with +this contract before implementation. No automatically inferred environment +variables supply task inputs in the first version. Validate the effective value once, retaining the winning source and bounded shadowed-source provenance. Duplicate declarations and malformed source data @@ -147,15 +151,15 @@ their resolved values: replay never re-reads a different ambient profile. Includes follow RFC 0002's duplicate and provenance rules. The initial root `inputs` mapping supplies the root interface. Included fragments can reference -it within their established scope; they cannot silently overwrite definitions. A -bundle receives explicit values through `with` and exposes them internally as +it within their established scope; they cannot silently overwrite definitions. +A bundle receives explicit values through `with` and exposes them internally as `bundle.params`, not the importer's whole `inputs` object. -Extract or reuse one feature-owned normalized parameter contract for root inputs -and bundle parameters. Share parsing, validation, scalar normalization, -constraint diagnostics, and redaction. Bundle selection, locks, private exports, -and namespace resolution remain composition's responsibility. Root input support -must not wait for external acquisition or require any bundle. +Extract or reuse one feature-owned normalized parameter contract for root +inputs and bundle parameters. Share parsing, validation, scalar normalization, +constraint diagnostics, and redaction. Bundle selection, locks, private +exports, and namespace resolution remain composition's responsibility. Root +input support must not wait for external acquisition or require any bundle. Resolved values used by an action or state contribute to its existing fingerprint. Preserve collection order and canonicalize mapping-key order. Do @@ -171,10 +175,10 @@ that a tool-specific flag bag respects the declared policy. ## 7. Diagnostics, redaction, and limits Follow RFC 0003: values are redacted by default. Only the exact declaration -`expose: non-secret` permits ordinary inspection of a value, subject to stronger -operator policy. Show the name, expected type, constraint, source location, and -remedy without echoing a rejected secret-looking value. Human output, JSON, -verbose output, snapshots, and telemetry share that boundary. +`expose: non-secret` permits ordinary inspection of a value, subject to +stronger operator policy. Show the name, expected type, constraint, source +location, and remedy without echoing a rejected secret-looking value. Human +output, JSON, verbose output, snapshots, and telemetry share that boundary. Redaction is not secret storage. This RFC does not introduce a secret type or promise confidentiality for values deliberately passed as process arguments or @@ -200,9 +204,9 @@ constraints, required values, unknown names, hostile argv elements, Windows paths, and collection-order preservation. Property-test source precedence, normalization stability, and redaction of rejected values. -Integration tests must cover profile selection, generated-plan replay, namespace -privacy, environment-independent defaults, and values passed across an -include/bundle boundary. Check one-variable promotion in a real command and +Integration tests must cover profile selection, generated-plan replay, +namespace privacy, environment-independent defaults, and values passed across +an include/bundle boundary. Check one-variable promotion in a real command and retain the unmodified hello-world fixture. Compare actual child argv, not just rendered YAML. Add examples and metadata through the existing documentation and schema pipelines, without a parallel input-help renderer. diff --git a/docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md b/docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md index 42d955a36..a350972ae 100644 --- a/docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md +++ b/docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md @@ -50,11 +50,11 @@ actions: clean_owned: [build-output] ``` -`path` names exactly one workspace-relative object. `kind` is `file` by default; -recursive ownership requires explicit `directory`. A directory declaration -claims the entire subtree, including existing and subsequently created children. -The preview must say this plainly. It is unsuitable for a directory shared with -source files or another owner's mutable state. +`path` names exactly one workspace-relative object. `kind` is `file` by +default; recursive ownership requires explicit `directory`. A directory +declaration claims the entire subtree, including existing and subsequently +created children. The preview must say this plainly. It is unsuitable for a +directory shared with source files or another owner's mutable state. A producer can identify a report without changing whether the action runs: @@ -95,20 +95,22 @@ version; declare one or use disjoint exact files. A `produces` reference identifies one producer. Reject multiple producers for the same artefact unless an existing explicit target contract already supplies -one shared producer. An explicit artefact may refer to an existing target output -only when both identify that same producer and compatible object type. It -augments metadata; it must not create a second Ninja producer edge. +one shared producer. An explicit artefact may refer to an existing target +output only when both identify that same producer and compatible object type. +It augments metadata; it must not create a second Ninja producer edge. Verify declared required outputs after successful production. Missing outputs are producer failures, not success. Failure may leave partial owned outputs; -record the failure but retain those outputs for inspection and explicit cleanup. -Do not automatically remove them or reuse them as successful build evidence. +record the failure but retain those outputs for inspection and explicit +cleanup. Do not automatically remove them or reuse them as successful build +evidence. Declarations without a producer remain useful for externally generated, -explicitly disposable paths. Inspection must distinguish declared ownership from -observed successful production. Neither status attests that the author chose a -safe directory. No producer receipt is required merely to remove a declared -legacy build directory, but declaration, bounded preview, and authorization are. +explicitly disposable paths. Inspection must distinguish declared ownership +from observed successful production. Neither status attests that the author +chose a safe directory. No producer receipt is required merely to remove a +declared legacy build directory, but declaration, bounded preview, and +authorization are. This does not introduce remote artefact delivery, a content-addressed store, or a provenance attestation system. Roadmap phase 5 retains its delivery boundary. @@ -120,11 +122,11 @@ command union. The list is nonempty, order-insensitive after name resolution, and duplicate references normalize to one selection. Unknown references fail. The operation may name artefacts only; it never accepts raw shell paths. -Extend the existing `clean` command with repeatable `--artefact NAME` selection. -Register the extension in canonical CLI metadata before implementing it. Without -that option, preserve existing Ninja-output cleanup. With explicit artefact -selection, clean only the selected ownership roots; do not implicitly add every -Ninja output, environment, cache, or runtime directory. +Extend the existing `clean` command with repeatable `--artefact NAME` +selection. Register the extension in canonical CLI metadata before implementing +it. Without that option, preserve existing Ninja-output cleanup. With explicit +artefact selection, clean only the selected ownership roots; do not implicitly +add every Ninja output, environment, cache, or runtime directory. These proposed commands demonstrate preview and explicit noninteractive consent: @@ -136,16 +138,16 @@ netsuke clean --artefact build-output --force --no-input Both command and recipe forms use the same planner, validator, deleter, and structured results. Use the existing mutation metadata for `--dry-run`, `--force`, and `--no-input`; do not invent a cleanup-specific confirmation -framework. An interactive run can request confirmation after displaying scope. A -noninteractive destructive run without explicit consent fails before deletion. +framework. An interactive run can request confirmation after displaying scope. +A noninteractive destructive run without explicit consent fails before deletion. `--force` skips confirmation, not validation, ownership conflicts, or bounds. A dry-run is a read-only plan, not a reusable authorization token. Execution resolves and validates scope again, and must not consume a stale user-supplied list as trusted filesystem authority. The preview lists normalized roots, recursive ownership, existing objects, missing objects, retained siblings, and -any rejected scope. It must not truncate away selected objects and then claim to -show a complete destructive plan: exceeding bounds fails the plan. +any rejected scope. It must not truncate away selected objects and then claim +to show a complete destructive plan: exceeding bounds fails the plan. ## 6. Filesystem safety contract @@ -168,29 +170,30 @@ capability at deletion. Reject unsupported mount, junction, or filesystem cases rather than fall back to lexical `starts_with` checks or ambient `rm -rf`. Use handle-relative traversal and deletion with platform-specific identity -checks. A path checked before enumeration is not automatically safe at deletion. -Detect replacement of selected roots and parents, and stop affected work. No -claim of race freedom is acceptable until adversarial replacement tests pass on -each supported platform; unavailable guarantees must produce explicit refusal. -Hard-linked file removal unlinks the selected directory entry, never truncates -the shared underlying file. - -Enforce operator-capped entry, depth, byte, and elapsed-time limits. Perform the -bounded admission pass before the first deletion; exceeding a bound there causes -zero deletions. Recheck while deleting because the tree may change. Delete files -before directories in a stable order. Already-missing objects are successful -no-ops. Permission, replacement, or interruption errors may follow partial -progress; report exact removed, retained, and failed objects. Cleanup is not an -atomic transaction and must never report rollback that it did not perform. +checks. A path checked before enumeration is not automatically safe at +deletion. Detect replacement of selected roots and parents, and stop affected +work. No claim of race freedom is acceptable until adversarial replacement +tests pass on each supported platform; unavailable guarantees must produce +explicit refusal. Hard-linked file removal unlinks the selected directory +entry, never truncates the shared underlying file. + +Enforce operator-capped entry, depth, byte, and elapsed-time limits. Perform +the bounded admission pass before the first deletion; exceeding a bound there +causes zero deletions. Recheck while deleting because the tree may change. +Delete files before directories in a stable order. Already-missing objects are +successful no-ops. Permission, replacement, or interruption errors may follow +partial progress; report exact removed, retained, and failed objects. Cleanup +is not an atomic transaction and must never report rollback that it did not +perform. ## 7. Interaction with states, concurrency, and replay A separately declared environment artefact may name the same normalized path as a [managed state][states]; the resource registry identifies that association. -Ownership remains optional for state use. When an environment is -selected for cleanup, acquire its integrity lease and invalidate its readiness -records before any deletion, including failure paths. Probe success from before -cleanup cannot establish subsequent readiness. +Ownership remains optional for state use. When an environment is selected for +cleanup, acquire its integrity lease and invalidate its readiness records +before any deletion, including failure paths. Probe success from before cleanup +cannot establish subsequent readiness. Reject a selected build closure that both cleans and produces or consumes the same declared resource. A Ninja pool would serialize access but would not @@ -199,10 +202,10 @@ remedy. Coordinated state leases protect cooperating invocations; unrelated external programs remain outside the guarantee. Pure artefact cleanup also needs an exclusive lease over each selected root, -ordered canonically. Producers participating in ownership use the same lease. It -must share the state-resource identity boundary rather than introduce a second -incompatible lock system. The lightweight artefact-only path cannot require -authoring a state declaration. +ordered canonically. Producers participating in ownership use the same lease. +It must share the state-resource identity boundary rather than introduce a +second incompatible lock system. The lightweight artefact-only path cannot +require authoring a state declaration. Persist ownership definitions and provenance in the versioned action plan, not captured directory listings. Replay obtains fresh capabilities and enumerates @@ -236,8 +239,8 @@ trees remains an explicit author decision shown in preview. A mandatory out-of-tree store would change onboarding and project layout. Inferring ownership from redirections or tool names would be unreliable. Wrapping `rm -rf` would provide neither platform consistency nor a capability -boundary. Requiring an artefact declaration for every existing target would make -an optional benefit contagious. +boundary. Requiring an artefact declaration for every existing target would +make an optional benefit contagious. Before implementation, ratify the supported-platform deletion primitives, resource-lease identity, ownership/source conflict rules, and concrete operator @@ -247,9 +250,9 @@ prerequisites for a useful exact-path cleanup operation. ## 10. Recommendation -Start with exact files and explicitly owned directory trees, one bounded cleanup -implementation, and transparent scope. Preserve ordinary recipes and existing -cleaning while giving annotated outputs stronger, testable guarantees. +Start with exact files and explicitly owned directory trees, one bounded +cleanup implementation, and transparent scope. Preserve ordinary recipes and +existing cleaning while giving annotated outputs stronger, testable guarantees. [roadmap]: ../roadmap-progressive-enhancement.md#24-owned-artefacts-and-bounded-cleanup [maturity]: 0017-progressive-enhancement-and-maturity-policies.md diff --git a/docs/rfcs/0016-named-contention-classes.md b/docs/rfcs/0016-named-contention-classes.md index e26b9277a..f223ac0db 100644 --- a/docs/rfcs/0016-named-contention-classes.md +++ b/docs/rfcs/0016-named-contention-classes.md @@ -10,28 +10,28 @@ ## 1. Summary -Allow an action to name a contention class when it should share a bounded number -of concurrent execution slots with other actions. Lower that declaration to -Ninja pools. Keep Ninja as the scheduler, preserve dependencies as ordering and -data requirements, and leave unannotated actions unchanged. +Allow an action to name a contention class when it should share a bounded +number of concurrent execution slots with other actions. Lower that declaration +to Ninja pools. Keep Ninja as the scheduler, preserve dependencies as ordering +and data requirements, and leave unannotated actions unchanged. -A simple build needs no resource model. One class and one scalar annotation must -suffice to prevent two cooperating native-build actions from overlapping. No -toolchain, state, context, ownership declaration, or typed input is required. +A simple build needs no resource model. One class and one scalar annotation +must suffice to prevent two cooperating native-build actions from overlapping. +No toolchain, state, context, ownership declaration, or typed input is required. ## 2. Problem and existing capabilities -Repositories use serial aggregates and independent worker flags to manage shared -caches and expensive native builds. Ordering requirements, contention, and -subprocess parallelism are different concepts. Serializing an aggregate can +Repositories use serial aggregates and independent worker flags to manage +shared caches and expensive native builds. Ordering requirements, contention, +and subprocess parallelism are different concepts. Serializing an aggregate can unnecessarily block independent work while failing to constrain unrelated actions that use the same resource. -The current intermediate representation (IR) already has `Action.pool`. This RFC -supplies the declaration, resolution, bounds, provenance, and backend contract -around that existing concept, not a second resource scheduler. Ninja pools limit -concurrent edges and remain subject to Ninja's global job limit.[^1] They do not -limit the number of threads spawned by one edge. +The current intermediate representation (IR) already has `Action.pool`. This +RFC supplies the declaration, resolution, bounds, provenance, and backend +contract around that existing concept, not a second resource scheduler. Ninja +pools limit concurrent edges and remain subject to Ninja's global job +limit.[^1] They do not limit the number of threads spawned by one edge. ## 3. Progressive authoring @@ -61,8 +61,8 @@ worker-count calculation or platform-detection preamble. `contention_classes` maps names to definitions containing `capacity`. An executable action or target may set one scalar `contention` reference. Literal -capacities work independently; typed input expressions may be added through [RFC -0014][inputs] without making that feature a prerequisite. +capacities work independently; typed input expressions may be added through +[RFC 0014][inputs] without making that feature a prerequisite. Reject zero, negative, fractional, Boolean, unbounded, and out-of-range capacities. Unknown fields, duplicate declarations, missing references, and @@ -87,9 +87,9 @@ on a consumer neither constrains its prerequisites nor changes their order. Resolve public names to stable internal class identities before backend emission. Emit one Ninja pool per used class and attach its identity to the -corresponding executable edges. Unused classes emit no pool or runtime work. The -complete multi-command action holds one edge slot until it finishes or fails; -releasing slots between commands would weaken the declared contract. +corresponding executable edges. Unused classes emit no pool or runtime work. +The complete multi-command action holds one edge slot until it finishes or +fails; releasing slots between commands would weaken the declared contract. A class changes eligibility for concurrent dispatch, not the dependency graph's meaning. It does not add dependencies, deduplicate actions, guarantee fairness, @@ -109,15 +109,15 @@ silently discard either console behaviour or contention limits. Serialize resolved class definitions and references in generated plans and include scheduling metadata in the existing graph/plan identity. Reject unknown persisted variants or missing pool definitions before replay. Backends without -pool support must report the unsupported guarantee rather than ignore it. No API -may pass an unchecked public class name straight into Ninja source. +pool support must report the unsupported guarantee rather than ignore it. No +API may pass an unchecked public class name straight into Ninja source. ## 6. Scope, worker budgets, and state integration The concurrency guarantee applies to one Ninja invocation. Two independent -Netsuke invocations do not share a pool. A depth-one class also does not stop an -external Cargo process from using the same directory. Document the guarantee in -human and structured inspection, not only in this RFC. +Netsuke invocations do not share a pool. A depth-one class also does not stop +an external Cargo process from using the same directory. Document the guarantee +in human and structured inspection, not only in this RFC. Internal workers remain separate. A class with capacity one may still launch many compiler or test threads. Tool adapters can consume typed worker inputs, @@ -172,22 +172,22 @@ boundary: pools alone do not claim process-wide mutual exclusion. ## 9. Alternatives and outstanding decisions -Serial dependencies encode order, not reusable contention policy. A lock command -inside every recipe repeats infrastructure and hides it from scheduling. A -generalized multi-resource scheduler would duplicate Ninja and create a much -larger correctness burden. Mandatory resource budgets would undermine the -shallow end without necessarily controlling subprocesses. +Serial dependencies encode order, not reusable contention policy. A lock +command inside every recipe repeats infrastructure and hides it from +scheduling. A generalized multi-resource scheduler would duplicate Ninja and +create a much larger correctness burden. Mandatory resource budgets would +undermine the shallow end without necessarily controlling subprocesses. -Ratify the operator ceiling field, class-name lowering, console interaction, and -any rule-default semantics before implementing the public grammar. Reserve +Ratify the operator ceiling field, class-name lowering, console interaction, +and any rule-default semantics before implementing the public grammar. Reserve multi-resource allocation and cross-host scheduling for separate evidence-led proposals. Ordinary native compilation limits must not depend on those features. ## 10. Recommendation Expose a deliberately small public contract over Ninja pools, with one optional -class per executable edge and explicit scope. Keep dependencies, internal worker -counts, and cross-invocation integrity locks separate. +class per executable edge and explicit scope. Keep dependencies, internal +worker counts, and cross-invocation integrity locks separate. [roadmap]: ../roadmap-progressive-enhancement.md#22-named-contention-without-a-second-scheduler [inputs]: 0014-typed-task-inputs.md diff --git a/docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md b/docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md index e3f01525b..088d207ef 100644 --- a/docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md +++ b/docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md @@ -51,13 +51,13 @@ These requirements apply to every RFC in this proposal set: aggregates. An unannotated aggregate may depend on an annotated action. - Unused state or context declarations never cause runtime probes, installation, network access, or resource acquisition. Existing template-time capability - behaviour remains governed by its own policy; these additions introduce no new - inspection-time execution. + behaviour remains governed by its own policy; these additions introduce no + new inspection-time execution. - Ordinary variables remain supported. No heuristic promotes `uv sync` to a state, a redirection to an ownership claim, or a Cargo command to a pool. - The straightforward command escape hatch remains available under the default - policy. A selected stricter policy may reject it with a local explanation, but - a plugin must not be the only way to execute an unusual tool. + policy. A selected stricter policy may reject it with a local explanation, + but a plugin must not be the only way to execute an unusual tool. - New opt-in schemas must validate correctly. Optional adoption is not permission to ignore malformed typed inputs, unsafe paths, or unknown state operations. @@ -78,10 +78,10 @@ envelope. [RFC 0013][states] supplies preparation contracts, [RFC 0014][inputs] supplies input contracts, [RFC 0015][artefacts] supplies ownership, and -[RFC 0016][contention] supplies pool-backed contention. Named execution contexts -remain a compatible extension point, not a sixth prerequisite hidden in these -five RFCs. The maturity schema may add a context rule only after that separate -surface has an accepted definition and an implementation. +[RFC 0016][contention] supplies pool-backed contention. Named execution +contexts remain a compatible extension point, not a sixth prerequisite hidden +in these five RFCs. The maturity schema may add a context rule only after that +separate surface has an accepted definition and an implementation. Do not repurpose RFC 0008's repository health tiers or RFC 0005's release admission policy as user-manifest maturity. Their purposes, trust sources, and @@ -103,10 +103,10 @@ maturity: ``` The default is an empty rule list. There is no default warning about missing -annotations. Every rule contains `id`, `severity`, and an explicit `select` list -of action/target identities. Initial severities are `off`, `warn`, and `error`. -Exact names keep the first scope contract small; bounded patterns can follow -only with a reviewed match and namespace contract. +annotations. Every rule contains `id`, `severity`, and an explicit `select` +list of action/target identities. Initial severities are `off`, `warn`, and +`error`. Exact names keep the first scope contract small; bounded patterns can +follow only with a reviewed match and namespace contract. Unknown rules, duplicate rule/subject combinations, unknown selectors, and unsupported severities fail validation. A policy cannot claim enforcement of a @@ -121,13 +121,13 @@ regular expressions. Evaluation uses resolved declarations and provenance. The initial rules have deliberately narrow, checkable meanings: -| Rule | Required evidence on selected nodes | -| --- | --- | -| `structured-commands` | Every resolved executable recipe unit is structured; legacy shell strings fail coverage. Explicit structured shell selection is not a claim of direct-argv safety. | -| `typed-inputs` | Each explicitly named configuration subject in `subjects` resolves to a typed input contract, not only an untyped variable. Internal variables are not automatically public inputs. | -| `owned-cleanup` | A selected cleanup action contains explicit `clean_owned` operations or nonexecuting aggregation only; arbitrary executable deletion recipes cannot satisfy the declaration contract. | -| `verified-states` | Each state named in `subjects` has a `require_state` or `ensure_state` operation before its first non-state command unit; a probe in a different action is insufficient. | -| `contention-declared` | Each selected executable edge resolves an explicit valid contention class. Dependency-only aggregates are not executable subjects. | +| Rule | Required evidence on selected nodes | +| --------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- | +| `structured-commands` | Every resolved executable recipe unit is structured; legacy shell strings fail coverage. Explicit structured shell selection is not a claim of direct-argv safety. | +| `typed-inputs` | Each explicitly named configuration subject in `subjects` resolves to a typed input contract, not only an untyped variable. Internal variables are not automatically public inputs. | +| `owned-cleanup` | A selected cleanup action contains explicit `clean_owned` operations or nonexecuting aggregation only; arbitrary executable deletion recipes cannot satisfy the declaration contract. | +| `verified-states` | Each state named in `subjects` has a `require_state` or `ensure_state` operation before its first non-state command unit; a probe in a different action is insufficient. | +| `contention-declared` | Each selected executable edge resolves an explicit valid contention class. Dependency-only aggregates are not executable subjects. | Table 1: Initial declaration-coverage rules, not whole-program safety proofs. @@ -147,11 +147,12 @@ reproducibility, correct cleanup ownership, or safe untrusted-code execution. ## 6. Scope and non-contagion -Resolve policy against the selected build closure, retaining distinct definition -and invocation provenance. A rule selecting `publish` applies when that node -will execute; it does not make an unrelated `hello.txt` build fail coverage. -Global syntax and reference errors remain errors even outside the closure. -`netsuke check` can inspect the whole manifest and report each explicit scope. +Resolve policy against the selected build closure, retaining distinct +definition and invocation provenance. A rule selecting `publish` applies when +that node will execute; it does not make an unrelated `hello.txt` build fail +coverage. Global syntax and reference errors remain errors even outside the +closure. `netsuke check` can inspect the whole manifest and report each +explicit scope. Aggregate selection must not implicitly make every dependency strict. If a future closure selector is added, its spelling must be explicit and inspection @@ -171,11 +172,11 @@ Use the existing configuration provenance and trusted operator boundary. An automatically discovered project file, imported bundle, or explicitly chosen `--config` file is not thereby trusted to weaken operator policy. -For overlapping rule/subject scopes, combine severity monotonically: `off < warn -< error`. Project and bundle declarations can strengthen but cannot lower an -operator floor. Expand and normalize scopes before combining them so renaming a -selector or splitting a rule cannot hide an overlap. Constraints specific to a -rule also combine without widening allowed behaviour. +For overlapping rule/subject scopes, combine severity monotonically: +`off < warn < error`. Project and bundle declarations can strengthen but cannot +lower an operator floor. Expand and normalize scopes before combining them so +renaming a selector or splitting a rule cannot hide an overlap. Constraints +specific to a rule also combine without widening allowed behaviour. Profiles may select reviewed policy sets using the existing profile machinery. Record the policy source and effective rule set; merely choosing a development @@ -187,27 +188,27 @@ capability boundary. The initial release needs no universal `strict` preset. A future preset must have a versioned, enumerable rule set and cannot gain new blocking rules on an unrelated software upgrade. Report-only adoption precedes error enforcement. -Narrow exceptions require an independently reviewed future contract; do not ship -a blanket suppression file that silently makes strict mode meaningless. +Narrow exceptions require an independently reviewed future contract; do not +ship a blanket suppression file that silently makes strict mode meaningless. ## 8. Evaluation and diagnostics -Run declaration coverage through the semantic linter's typed inventory. Separate -pure policy evaluation from manifest loading, capability observation, and runner -effects. Evaluate applicable errors before starting any selected user action. -Inspection and dry-run do not execute state probes to satisfy a maturity rule; -they check declaration evidence only. +Run declaration coverage through the semantic linter's typed inventory. +Separate pure policy evaluation from manifest loading, capability observation, +and runner effects. Evaluate applicable errors before starting any selected +user action. Inspection and dry-run do not execute state probes to satisfy a +maturity rule; they check declaration evidence only. A warning reports a gap without changing a successful command's exit status. An error uses the existing validation/policy failure class and stops execution. Human and JSON output include rule ID, severity, selected subject, definition span, policy-source span, and one local remedy. Reuse Fluent localization, -structured-result envelopes, and redaction metadata; do not leak input values or -probe output in diagnostics or metric labels. +structured-result envelopes, and redaction metadata; do not leak input values +or probe output in diagnostics or metric labels. `context --json` describes supported rules and effective settings through the -existing metadata surface. `check --json` reports findings. Neither depends on a -new `explain` command, whose separate roadmap evaluation remains unresolved. +existing metadata surface. `check --json` reports findings. Neither depends on +a new `explain` command, whose separate roadmap evaluation remains unresolved. Supported basic manifests must have no new maturity messages under default settings, including verbose warnings that imply untyped usage is deprecated. @@ -219,9 +220,10 @@ execution behaviour before and after every feature. Compare observed child arguments and filesystem effects, not just apparent YAML similarity. Add one-feature-only examples: a typed worker input without a context, a state -without typed inputs, one cleanup root without a state, and one pool on a legacy -command. Combine annotated and unannotated actions under an ordinary aggregate. -Assert that unrelated invocation starts no probes and creates no state records. +without typed inputs, one cleanup root without a state, and one pool on a +legacy command. Combine annotated and unannotated actions under an ordinary +aggregate. Assert that unrelated invocation starts no probes and creates no +state records. Property-test severity monotonicity, scope composition, order independence, namespace resolution, and inability to weaken operator constraints. End-to-end @@ -230,18 +232,18 @@ versus error exits, selected versus whole-manifest checks, and generated-plan replay under the applicable trusted policy. Document and measure onboarding separately from the Cuprum migration. The -quickstart may not gain required declarations. Each enhancement must demonstrate -its local benefit and explicitly identify any remaining shell helper; moving -boilerplate to an unreviewed imaginary bundle does not count as simplification. -Do not claim a usability improvement from line count alone. +quickstart may not gain required declarations. Each enhancement must +demonstrate its local benefit and explicitly identify any remaining shell +helper; moving boilerplate to an unreviewed imaginary bundle does not count as +simplification. Do not claim a usability improvement from line count alone. ## 10. Alternatives and outstanding decisions -Mandatory maturity levels would make advanced features contagious. Automatically -promoting projects by size or feature count would alter semantics unexpectedly. -A global strict mode with an evolving implicit rule list would make upgrades -break otherwise unchanged manifests. Separate validators per feature would -duplicate source handling and reporting. +Mandatory maturity levels would make advanced features contagious. +Automatically promoting projects by size or feature count would alter semantics +unexpectedly. A global strict mode with an evolving implicit rule list would +make upgrades break otherwise unchanged manifests. Separate validators per +feature would duplicate source handling and reporting. Ratify policy-source placement within the shared configuration contract, selected-closure inspection metadata, and the semantic linter's reusable diff --git a/docs/roadmap-progressive-enhancement.md b/docs/roadmap-progressive-enhancement.md index 9dc77a026..cb3681162 100644 --- a/docs/roadmap-progressive-enhancement.md +++ b/docs/roadmap-progressive-enhancement.md @@ -26,25 +26,27 @@ new named execution contexts, and a universal strict mode are not prerequisites. shallow-end compatibility and opt-in policy composition. RFC 0001 and phases 12 to 14 retain ownership of command parsing, argv, -execution, process cleanup, capability-scoped paths, and persisted action plans. -Phase 11 retains trusted shell selection. Phases 16 to 19 retain include and -bundle composition. Phase 5 and OrthoConfig retain generic profile, schema, +execution, process cleanup, capability-scoped paths, and persisted action +plans. Phase 11 retains trusted shell selection. Phases 16 to 19 retain include +and bundle composition. Phase 5 and OrthoConfig retain generic profile, schema, metadata, redaction, and result machinery. The semantic linter tracked by issue -#592 retains the reusable manifest-analysis boundary. No new task may duplicate -those implementations merely to avoid an explicit integration dependency. +`#592` retains the reusable manifest-analysis boundary. No new task may +duplicate those implementations merely to avoid an explicit integration +dependency. -New public grammar is proposed, not shipped: RFC 0014 proposes `--input -NAME=VALUE` on manifest-compiling commands; RFC 0015 proposes `clean --artefact -NAME`. Register both with the canonical vocabulary and metadata before delivery. -Use existing `check`, `context --json`, `--dry-run`, `--force`, and `--no-input` -contracts. Do not introduce an unreviewed `explain` command or new exit-code -system. +New public grammar is proposed, not shipped: RFC 0014 proposes +`--input NAME=VALUE` on manifest-compiling commands; RFC 0015 proposes +`clean --artefact NAME`. Register both with the canonical vocabulary and +metadata before delivery. Use existing `check`, `context --json`, `--dry-run`, +`--force`, and `--no-input` contracts. Do not introduce an unreviewed `explain` +command or new exit-code system. Every implementation task includes relevant unit and behavioural tests. Use Proptest for normalization and algebraic invariants, bounded Kani harnesses for -pure transition logic where useful, and subprocess end-to-end tests for process, -filesystem, locking, and backend boundaries. Reuse installed or cached tooling; -this roadmap does not require new source-built proof tools for ordinary gates. +pure transition logic where useful, and subprocess end-to-end tests for +process, filesystem, locking, and backend boundaries. Reuse installed or cached +tooling; this roadmap does not require new source-built proof tools for +ordinary gates. ## 20. Preserve the shallow end before adding contracts @@ -166,8 +168,8 @@ unrelated task configuration unchanged. ## 22. Named contention without a second scheduler -Hypothesis: one optional contention annotation can control shared build pressure -without confusing dependency order or subprocess worker limits. +Hypothesis: one optional contention annotation can control shared build +pressure without confusing dependency order or subprocess worker limits. ### 22.1. Resolve one class per executable edge @@ -282,8 +284,8 @@ without requiring a provider implementation or a Nagios service. ### 23.4. Validate state value in a real migration -Outcome: the canary removes redundant setup while retaining intentionally strict -preconditions, and default builds pay no state-management cost. +Outcome: the canary removes redundant setup while retaining intentionally +strict preconditions, and default builds pay no state-management cost. - [ ] 23.4.1. Add the Cuprum preparation and extension-guard canaries. Requires 23.2.2 and 23.3.2. @@ -398,8 +400,8 @@ findings, with no command-text guessing or competing parser. ### 25.2. Compose enforcement without weakening trusted constraints -Outcome: profile and import layering cannot make a project-owned warning replace -an operator error or hide a rule through scope rewriting. +Outcome: profile and import layering cannot make a project-owned warning +replace an operator error or hide a rule through scope rewriting. - [ ] 25.2.1. Add opt-in policy declarations and trust-aware severity merging. Requires 25.1.1 and the existing configuration provenance boundary. From d0ab7070c65c8e51bf54a4018dc5c8783f289e3a Mon Sep 17 00:00:00 2001 From: leynos Date: Sat, 19 Sep 2026 14:42:41 +0200 Subject: [PATCH 3/3] Renumber progressive-enhancement RFCs to 0021-0025 RFC 0013 to 0020 are reserved by the 6.1.1 child-RFC split plan (PR #697) for the Ansible stdlib filter RFCs, which puts this set in collision at 0013 to 0017. Nothing above RFC 0012 is merged, and the style guide forbids renumbering after publication, so resolve the overlap now by moving this set above #697's ceiling. Rename the five files and update every reference in the two documents that cite them. The change is a pure number substitution: each file is byte-identical to its predecessor once the digits are normalised back. Gates: make fmt (no-op), check-fmt, markdownlint and nixie all pass. Co-Authored-By: Claude Code --- docs/contents.md | 20 +++---- ...s.md => 0021-managed-states-and-probes.md} | 12 ++--- ...sk-inputs.md => 0022-typed-task-inputs.md} | 6 +-- ...-artefact-ownership-and-scoped-cleanup.md} | 10 ++-- ...es.md => 0024-named-contention-classes.md} | 12 ++--- ...sive-enhancement-and-maturity-policies.md} | 18 +++---- docs/roadmap-progressive-enhancement.md | 54 +++++++++---------- 7 files changed, 66 insertions(+), 66 deletions(-) rename docs/rfcs/{0013-managed-states-and-probes.md => 0021-managed-states-and-probes.md} (98%) rename docs/rfcs/{0014-typed-task-inputs.md => 0022-typed-task-inputs.md} (98%) rename docs/rfcs/{0015-artefact-ownership-and-scoped-cleanup.md => 0023-artefact-ownership-and-scoped-cleanup.md} (98%) rename docs/rfcs/{0016-named-contention-classes.md => 0024-named-contention-classes.md} (97%) rename docs/rfcs/{0017-progressive-enhancement-and-maturity-policies.md => 0025-progressive-enhancement-and-maturity-policies.md} (96%) diff --git a/docs/contents.md b/docs/contents.md index f969ba26f..67099f59d 100644 --- a/docs/contents.md +++ b/docs/contents.md @@ -80,15 +80,15 @@ operator, user, and contributor references are easier to find. fuzzing](rfcs/0008-code-health.md): Proposed workflow-policy validation, gate self-consistency, health-signal ownership, and scheduled coverage-guided fuzzing. -- [RFC 0013: Managed states and functional probes][rfc-0013]: Optional +- [RFC 0021: Managed states and functional probes][rfc-0021]: Optional preparation contracts, default built-in checks, and bounded external probes. -- [RFC 0014: Optional typed task inputs][rfc-0014]: Gradual input annotation, +- [RFC 0022: Optional typed task inputs][rfc-0022]: Gradual input annotation, shared bundle-parameter validation, and explicit source provenance. -- [RFC 0015: Artefact ownership and scoped cleanup][rfc-0015]: Exact output +- [RFC 0023: Artefact ownership and scoped cleanup][rfc-0023]: Exact output ownership, bounded previews, and capability-scoped deletion. -- [RFC 0016: Named contention classes][rfc-0016]: Optional per-edge limits +- [RFC 0024: Named contention classes][rfc-0024]: Optional per-edge limits lowered to Ninja pools, with explicit invocation-only scope. -- [RFC 0017: Progressive enhancement and maturity policies][rfc-0017]: +- [RFC 0025: Progressive enhancement and maturity policies][rfc-0025]: Shallow-end compatibility and opt-in, scoped, trust-aware enforcement. [rfc-0009]: rfcs/0009-structured-command-working-directories.md @@ -97,11 +97,11 @@ operator, user, and contributor references are easier to find. [rfc-0002]: rfcs/0002-repository-relative-includes.md [rfc-0004]: rfcs/0004-digest-pinned-external-bundles.md [rfc-0011]: rfcs/0011-allow-listed-structured-command-shells.md -[rfc-0013]: rfcs/0013-managed-states-and-probes.md -[rfc-0014]: rfcs/0014-typed-task-inputs.md -[rfc-0015]: rfcs/0015-artefact-ownership-and-scoped-cleanup.md -[rfc-0016]: rfcs/0016-named-contention-classes.md -[rfc-0017]: rfcs/0017-progressive-enhancement-and-maturity-policies.md +[rfc-0021]: rfcs/0021-managed-states-and-probes.md +[rfc-0022]: rfcs/0022-typed-task-inputs.md +[rfc-0023]: rfcs/0023-artefact-ownership-and-scoped-cleanup.md +[rfc-0024]: rfcs/0024-named-contention-classes.md +[rfc-0025]: rfcs/0025-progressive-enhancement-and-maturity-policies.md ## Decision records diff --git a/docs/rfcs/0013-managed-states-and-probes.md b/docs/rfcs/0021-managed-states-and-probes.md similarity index 98% rename from docs/rfcs/0013-managed-states-and-probes.md rename to docs/rfcs/0021-managed-states-and-probes.md index 1b423cda8..c97906cff 100644 --- a/docs/rfcs/0013-managed-states-and-probes.md +++ b/docs/rfcs/0021-managed-states-and-probes.md @@ -1,8 +1,8 @@ -# RFC 0013: Managed states and functional probes +# RFC 0021: Managed states and functional probes ## Preamble -- **RFC number:** 0013 +- **RFC number:** 0021 - **Status:** Proposed - **Created:** 2026-09-19 - **Scope:** Optional preparation contracts, not a new build scheduler @@ -19,7 +19,7 @@ or a Nagios installation. A plain `command: uv sync` remains valid. Adding a state is worthwhile only when an author needs an explicit precondition, validated reuse, or a shared preparation contract. No context, typed input, ownership declaration, maturity -policy, or bundle is compulsory. [RFC 0017][maturity] owns this shallow-end +policy, or bundle is compulsory. [RFC 0025][maturity] owns this shallow-end compatibility requirement. ## 2. Problem and existing boundaries @@ -257,7 +257,7 @@ Shared preparation may be skipped after fresh validation, but readiness cannot be memoized across another action's mutation. Unmanaged commands and other programs do not participate; the guarantee must say so explicitly. -Named contention classes in [RFC 0016][contention] can reduce contention before +Named contention classes in [RFC 0024][contention] can reduce contention before action dispatch. They do not replace state leases across invocations. Shared state identity and filesystem alias handling must use existing capability anchors; ambiguous aliases or unsupported locking filesystems fail rather than @@ -318,8 +318,8 @@ functional checks with the same result algebra and process boundary. Keep readiness, preparation evidence, and artefact ownership distinct. [roadmap]: ../roadmap-progressive-enhancement.md#23-verified-preparation-without-mandatory-environments -[maturity]: 0017-progressive-enhancement-and-maturity-policies.md +[maturity]: 0025-progressive-enhancement-and-maturity-policies.md [commands]: 0001-structured-command-blocks.md -[contention]: 0016-named-contention-classes.md +[contention]: 0024-named-contention-classes.md [^1]: [Nagios plugin development guidelines](https://nagios-plugins.org/doc/guidelines.html), plugin return codes. diff --git a/docs/rfcs/0014-typed-task-inputs.md b/docs/rfcs/0022-typed-task-inputs.md similarity index 98% rename from docs/rfcs/0014-typed-task-inputs.md rename to docs/rfcs/0022-typed-task-inputs.md index 2e590bdd4..fd3de28f6 100644 --- a/docs/rfcs/0014-typed-task-inputs.md +++ b/docs/rfcs/0022-typed-task-inputs.md @@ -1,8 +1,8 @@ -# RFC 0014: Optional typed task inputs +# RFC 0022: Optional typed task inputs ## Preamble -- **RFC number:** 0014 +- **RFC number:** 0022 - **Status:** Proposed - **Created:** 2026-09-19 - **Scope:** Public task configuration with gradual adoption @@ -232,4 +232,4 @@ internal variables untyped unless their author chooses otherwise. [roadmap]: ../roadmap-progressive-enhancement.md#21-typed-inputs-with-one-parameter-contract [bundles]: 0003-versioned-local-bundles.md#6-parameter-model -[maturity]: 0017-progressive-enhancement-and-maturity-policies.md +[maturity]: 0025-progressive-enhancement-and-maturity-policies.md diff --git a/docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md b/docs/rfcs/0023-artefact-ownership-and-scoped-cleanup.md similarity index 98% rename from docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md rename to docs/rfcs/0023-artefact-ownership-and-scoped-cleanup.md index a350972ae..872ad6cf4 100644 --- a/docs/rfcs/0015-artefact-ownership-and-scoped-cleanup.md +++ b/docs/rfcs/0023-artefact-ownership-and-scoped-cleanup.md @@ -1,8 +1,8 @@ -# RFC 0015: Artefact ownership and scoped cleanup +# RFC 0023: Artefact ownership and scoped cleanup ## Preamble -- **RFC number:** 0015 +- **RFC number:** 0023 - **Status:** Proposed - **Created:** 2026-09-19 - **Scope:** Optional owned-output declarations and bounded deletion @@ -18,7 +18,7 @@ commands, existing targets, and existing `clean` behaviour remain supported. Ownership is an assertion by the manifest author, not proof that a directory contains no valuable files. The implementation must make the scope inspectable and enforce its boundaries without claiming to sandbox arbitrary recipes. -[RFC 0017][maturity] makes this an opt-in improvement rather than an onboarding +[RFC 0025][maturity] makes this an opt-in improvement rather than an onboarding prerequisite. ## 2. Problem and existing boundaries @@ -255,5 +255,5 @@ cleanup implementation, and transparent scope. Preserve ordinary recipes and existing cleaning while giving annotated outputs stronger, testable guarantees. [roadmap]: ../roadmap-progressive-enhancement.md#24-owned-artefacts-and-bounded-cleanup -[maturity]: 0017-progressive-enhancement-and-maturity-policies.md -[states]: 0013-managed-states-and-probes.md +[maturity]: 0025-progressive-enhancement-and-maturity-policies.md +[states]: 0021-managed-states-and-probes.md diff --git a/docs/rfcs/0016-named-contention-classes.md b/docs/rfcs/0024-named-contention-classes.md similarity index 97% rename from docs/rfcs/0016-named-contention-classes.md rename to docs/rfcs/0024-named-contention-classes.md index f223ac0db..5a2e14cda 100644 --- a/docs/rfcs/0016-named-contention-classes.md +++ b/docs/rfcs/0024-named-contention-classes.md @@ -1,8 +1,8 @@ -# RFC 0016: Named contention classes +# RFC 0024: Named contention classes ## Preamble -- **RFC number:** 0016 +- **RFC number:** 0024 - **Status:** Proposed - **Created:** 2026-09-19 - **Scope:** Optional concurrency limits lowered to Ninja pools @@ -62,7 +62,7 @@ worker-count calculation or platform-detection preamble. `contention_classes` maps names to definitions containing `capacity`. An executable action or target may set one scalar `contention` reference. Literal capacities work independently; typed input expressions may be added through -[RFC 0014][inputs] without making that feature a prerequisite. +[RFC 0022][inputs] without making that feature a prerequisite. Reject zero, negative, fractional, Boolean, unbounded, and out-of-range capacities. Unknown fields, duplicate declarations, missing references, and @@ -190,7 +190,7 @@ class per executable edge and explicit scope. Keep dependencies, internal worker counts, and cross-invocation integrity locks separate. [roadmap]: ../roadmap-progressive-enhancement.md#22-named-contention-without-a-second-scheduler -[inputs]: 0014-typed-task-inputs.md -[states]: 0013-managed-states-and-probes.md -[maturity]: 0017-progressive-enhancement-and-maturity-policies.md +[inputs]: 0022-typed-task-inputs.md +[states]: 0021-managed-states-and-probes.md +[maturity]: 0025-progressive-enhancement-and-maturity-policies.md [^1]: [Ninja manual: pools](https://ninja-build.org/manual.html#ref_pool). diff --git a/docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md b/docs/rfcs/0025-progressive-enhancement-and-maturity-policies.md similarity index 96% rename from docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md rename to docs/rfcs/0025-progressive-enhancement-and-maturity-policies.md index 088d207ef..1681d71a5 100644 --- a/docs/rfcs/0017-progressive-enhancement-and-maturity-policies.md +++ b/docs/rfcs/0025-progressive-enhancement-and-maturity-policies.md @@ -1,8 +1,8 @@ -# RFC 0017: Progressive enhancement and maturity policies +# RFC 0025: Progressive enhancement and maturity policies ## Preamble -- **RFC number:** 0017 +- **RFC number:** 0025 - **Status:** Proposed - **Created:** 2026-09-19 - **Scope:** Shallow-end compatibility and opt-in contract enforcement @@ -76,9 +76,9 @@ maturity-rule selection and trust-aware enforcement over that analysis; it must not establish a competing linter, parser, configuration loader, or JSON envelope. -[RFC 0013][states] supplies preparation contracts, [RFC 0014][inputs] supplies -input contracts, [RFC 0015][artefacts] supplies ownership, and -[RFC 0016][contention] supplies pool-backed contention. Named execution +[RFC 0021][states] supplies preparation contracts, [RFC 0022][inputs] supplies +input contracts, [RFC 0023][artefacts] supplies ownership, and +[RFC 0024][contention] supplies pool-backed contention. Named execution contexts remain a compatible extension point, not a sixth prerequisite hidden in these five RFCs. The maturity schema may add a context rule only after that separate surface has an accepted definition and an implementation. @@ -258,7 +258,7 @@ with stronger policies controlled by the appropriate authority rather than imposed on every Netsuke user. [roadmap]: ../roadmap-progressive-enhancement.md -[states]: 0013-managed-states-and-probes.md -[inputs]: 0014-typed-task-inputs.md -[artefacts]: 0015-artefact-ownership-and-scoped-cleanup.md -[contention]: 0016-named-contention-classes.md +[states]: 0021-managed-states-and-probes.md +[inputs]: 0022-typed-task-inputs.md +[artefacts]: 0023-artefact-ownership-and-scoped-cleanup.md +[contention]: 0024-named-contention-classes.md diff --git a/docs/roadmap-progressive-enhancement.md b/docs/roadmap-progressive-enhancement.md index cb3681162..9d9582a1b 100644 --- a/docs/roadmap-progressive-enhancement.md +++ b/docs/roadmap-progressive-enhancement.md @@ -14,15 +14,15 @@ new named execution contexts, and a universal strict mode are not prerequisites. ## Contract ownership and integration boundaries -- [RFC 0013](rfcs/0013-managed-states-and-probes.md) owns states, built-in and +- [RFC 0021](rfcs/0021-managed-states-and-probes.md) owns states, built-in and external probes, preparation evidence, and operation semantics. -- [RFC 0014](rfcs/0014-typed-task-inputs.md) owns optional root inputs and +- [RFC 0022](rfcs/0022-typed-task-inputs.md) owns optional root inputs and shares parameter validation with RFC 0003, rather than duplicating it. -- [RFC 0015](rfcs/0015-artefact-ownership-and-scoped-cleanup.md) owns declared +- [RFC 0023](rfcs/0023-artefact-ownership-and-scoped-cleanup.md) owns declared artefact scope and standardized cleanup. -- [RFC 0016](rfcs/0016-named-contention-classes.md) owns public contention +- [RFC 0024](rfcs/0024-named-contention-classes.md) owns public contention declarations lowered to Ninja pools. -- [RFC 0017](rfcs/0017-progressive-enhancement-and-maturity-policies.md) owns +- [RFC 0025](rfcs/0025-progressive-enhancement-and-maturity-policies.md) owns shallow-end compatibility and opt-in policy composition. RFC 0001 and phases 12 to 14 retain ownership of command parsing, argv, @@ -34,8 +34,8 @@ metadata, redaction, and result machinery. The semantic linter tracked by issue duplicate those implementations merely to avoid an explicit integration dependency. -New public grammar is proposed, not shipped: RFC 0014 proposes -`--input NAME=VALUE` on manifest-compiling commands; RFC 0015 proposes +New public grammar is proposed, not shipped: RFC 0022 proposes +`--input NAME=VALUE` on manifest-compiling commands; RFC 0023 proposes `clean --artefact NAME`. Register both with the canonical vocabulary and metadata before delivery. Use existing `check`, `context --json`, `--dry-run`, `--force`, and `--no-input` contracts. Do not introduce an unreviewed `explain` @@ -59,12 +59,12 @@ Outcome: a release can demonstrate unchanged basic behaviour rather than merely assert it. The fixtures expose whether later syntax has become contagious. - [ ] 20.1.1. Ratify the progressive-enhancement contracts and version gates. - - [ ] Review RFCs 0013 to 0017, resolve their outstanding schema decisions, + - [ ] Review RFCs 0021 to 0025, resolve their outstanding schema decisions, and record accepted decisions through the normal ADR process. - [ ] Coordinate manifest and persisted-plan version allocation with 12.1.1, 16.1.1, and 17.1.1 without requiring bundle implementation first. - [ ] Record operation-union ownership, feature-specific capability reporting, - and rejection of unsupported syntax. See RFC 0017 sections 2 and 3. + and rejection of unsupported syntax. See RFC 0025 sections 2 and 3. - [ ] 20.1.2. Add unchanged-basic-workflow acceptance fixtures. Requires 20.1.1. - [ ] Preserve the exact quickstart manifest, scalar shell recipes, ordinary variables, and the list-of-mappings action/target structure. @@ -122,7 +122,7 @@ with errors local to the responsible declaration or source. - [ ] Test Boolean/integer distinction, overflow, collection bounds, duplicate keys, choices, empty values, and path capability non-authority. - [ ] Share a conformance corpus with bundle work without requiring bundle - loading. See RFC 0014 sections 4 and 7. + loading. See RFC 0022 sections 4 and 7. - [ ] 21.1.2. Add optional root input declarations and immutable resolution. Requires 21.1.1 and 20.1.2. - [ ] Parse `inputs`, preserve ordinary `vars`, detect namespace collisions, @@ -130,7 +130,7 @@ with errors local to the responsible declaration or source. - [ ] Support one-value promotion without implicit aliases or executable defaults. Retain declaration and reference spans. - [ ] Property-test normalization stability and run the unchanged-basic - fixture. See RFC 0014 sections 3, 4, and 8. + fixture. See RFC 0022 sections 3, 4, and 8. ### 21.2. Bind explicit callers and profiles to the same interface @@ -144,7 +144,7 @@ new configuration stack. Precedence cases decide whether the contract is clear. - [ ] Integrate existing configuration/profile provenance and ratify overlay order with phase 5; apply operator constraints after source selection. - [ ] Test every source precedence pair and default redaction in errors, - verbose output, and JSON. See RFC 0014 sections 5 and 7. + verbose output, and JSON. See RFC 0022 sections 5 and 7. - [ ] 21.2.2. Preserve resolved inputs through graph and plan generation. Requires 21.2.1 and 12.3.1 for structured-plan integration. - [ ] Add used values to existing fingerprints and preserve argv splicing @@ -181,7 +181,7 @@ pool machinery, with no hidden overlapping-resource scheduler. - [ ] Implement positive integer capacities, one scalar action/target class, operator ceilings, reserved names, and source-local errors. - [ ] Reject aggregate annotations, multiple classes, and unsupported - rule-default forms. See RFC 0016 sections 3 and 4. + rule-default forms. See RFC 0024 sections 3 and 4. - [ ] Add a legacy-command fixture; typed input support is optional and integrates only after 21.1.2. - [ ] 22.1.2. Lower resolved classes into Ninja pool definitions. Requires @@ -191,7 +191,7 @@ pool machinery, with no hidden overlapping-resource scheduler. - [ ] Include complete action sequences and persisted scheduling metadata; integrate with 12.3.1 rather than reimplementing the codec. - [ ] Property-test declaration-order independence and unannotated-output - compatibility. See RFC 0016 section 5. + compatibility. See RFC 0024 section 5. ### 22.2. Verify concurrency and communicate its limits @@ -204,7 +204,7 @@ the guarantee and exposes its invocation-only boundary. - [ ] Exercise multi-command edges, failure, cancellation, and console rejection without relying solely on sleeps. - [ ] Demonstrate that separate Ninja invocations do not share the limit. See - RFC 0016 sections 6 and 8. + RFC 0024 sections 6 and 8. - [ ] 22.2.2. Publish class inspection and the native-build canary. Requires 22.2.1 and 20.2.2. - [ ] Show requested/effective capacity, source, and invocation-only scope. @@ -225,11 +225,11 @@ before process execution or durable records complicate the implementation. - [ ] 23.1.1. Implement state definitions and the readiness algebra. Requires 20.1.1 and 20.2.1. - [ ] Model kinds, declared identity inputs, optional preparation, all four - outcomes, and the three operations in RFC 0013 sections 3 to 5. + outcomes, and the three operations in RFC 0021 sections 3 to 5. - [ ] Property-test that unknown never authorizes repair, degraded requires explicit acceptance, and ensure performs at most one preparation attempt. - [ ] Reject unsupported incremental-target state checks instead of letting - Ninja skip readiness verification. See RFC 0013 section 8. + Ninja skip readiness verification. See RFC 0021 section 8. - [ ] 23.1.2. Implement default built-in probes through existing seams. Requires 23.1.1 and 12.2.3 where interpreter execution is needed. - [ ] Deliver directory, file, and precisely specified Python-environment @@ -250,7 +250,7 @@ stale success records or a second scheduling loop. never probe during check, graph generation, help, or dry-run. - [ ] Preserve resolved argv, environment, cwd, provenance, and typed result mapping. Do not add state-private command execution. - - [ ] Reject unsupported replay versions. See RFC 0013 sections 5 and 7. + - [ ] Reject unsupported replay versions. See RFC 0021 sections 5 and 7. - [ ] 23.2.2. Implement bounded integrity leases and atomic state records. Requires 23.2.1 and 20.2.3. - [ ] Hold canonically ordered resource leases through each action's state @@ -258,7 +258,7 @@ stale success records or a second scheduling loop. - [ ] Publish success only after post-verification and invalidate observations across mutation. Bound retention, acquisition, and interrupted recovery. - [ ] Test separate processes, damaged records, replacement races, and - interruption before publication. See RFC 0013 sections 4 and 8. + interruption before publication. See RFC 0021 sections 4 and 8. ### 23.3. Admit external functional checks without implicit repair @@ -267,7 +267,7 @@ without requiring a provider implementation or a Nagios service. - [ ] 23.3.1. Implement the optional Nagios-style protocol adapter. Requires 23.2.1. - - [ ] Map exits 0 to 3 exactly as RFC 0013 section 6 specifies and preserve + - [ ] Map exits 0 to 3 exactly as RFC 0021 section 6 specifies and preserve distinct spawn, signal, timeout, protocol, and output-limit reasons. - [ ] Combine built-in and external evidence without allowing stdout to forge identity or override a failing result. @@ -316,7 +316,7 @@ silently create caching or overlapping output producers. - [ ] 24.1.1. Implement exact-path artefact definitions and producer references. Requires 20.1.1 and 20.2.1. - [ ] Add file/directory kind, descriptive roles, optional parent creation, - and `produces` metadata. See RFC 0015 sections 3 and 4. + and `produces` metadata. See RFC 0023 sections 3 and 4. - [ ] Reject overlapping/case-equivalent ownership and duplicate producers; reconcile explicit declarations with existing target outputs. - [ ] Test missing outputs, retained partial outputs, and always-run reports. @@ -325,7 +325,7 @@ silently create caching or overlapping output producers. runtime paths, and enumerate complete bounded scope without deletion. - [ ] Make directory-subtree ownership and pre-existing contents visible; reject invalid, over-budget, or incomplete previews. - - [ ] Property-test path normalization and scope uniqueness. See RFC 0015 + - [ ] Property-test path normalization and scope uniqueness. See RFC 0023 sections 5 and 6. ### 24.2. Delete only validated scope through one implementation @@ -362,7 +362,7 @@ has removed, and ordinary workflows retain their independent cleanup choices. - [ ] Reject simultaneous cleanup and declared production/consumption of the same resource; a pool must not be treated as semantic ordering. - [ ] Keep dyndep and command-private temporary cleanup under their existing - owners. See RFC 0015 section 7. + owners. See RFC 0023 section 7. - [ ] 24.3.2. Publish exact-path cleanup examples and a Cuprum canary. Requires 24.2.2 and 20.2.2; state examples also require 24.3.1. - [ ] Migrate disjoint output roots without demanding an artefact for every @@ -386,7 +386,7 @@ findings, with no command-text guessing or competing parser. - [ ] Retain resolved recipe units, references, ownership, and provenance; distinguish policy coverage from schema/runtime correctness. - [ ] Implement pure rule/subject selection without executing probes or user - actions. See RFC 0017 sections 3 to 6. + actions. See RFC 0025 sections 3 to 6. - [ ] Add fixtures proving an unrelated selected target does not inherit another action's coverage requirements. - [ ] 25.1.2. Implement the initial rules as independently gated checks. @@ -395,7 +395,7 @@ findings, with no command-text guessing or competing parser. contention after 22.1.2, states after 23.2.1, and cleanup after 24.2.2. - [ ] Reject unknown/unsupported rule IDs rather than claim incomplete enforcement. No rule waits for an unrelated feature. - - [ ] Test exact coverage semantics and remedies from RFC 0017 section 5; do + - [ ] Test exact coverage semantics and remedies from RFC 0025 section 5; do not certify hermeticity from declaration presence. ### 25.2. Compose enforcement without weakening trusted constraints @@ -410,7 +410,7 @@ replace an operator error or hide a rule through scope rewriting. - [ ] Integrate profiles through phase 5's owner, with no automatic promotion, blanket bypass, or new configuration loader. - [ ] Property-test overlap, order independence, scope normalization, and - inability to weaken operator policy. See RFC 0017 section 7. + inability to weaken operator policy. See RFC 0025 section 7. - [ ] 25.2.2. Wire preflight and existing human/JSON reporting. Requires 25.1.2 and 25.2.1. - [ ] Evaluate applicable errors before execution, keep warnings nonblocking, @@ -418,7 +418,7 @@ replace an operator error or hide a rule through scope rewriting. - [ ] Use `check`, `context --json`, shared exit classes, localization, and redaction; introduce no separate `explain` command. - [ ] Test selected-closure versus whole-manifest checks and persisted plans - under applicable trusted policy. See RFC 0017 section 8. + under applicable trusted policy. See RFC 0025 section 8. ### 25.3. Prove progressive enhancement end to end