Forthcoming capability
Proposed · no release named Proposed RFC

Owned Artefacts

Every repository grows a cleanup list, and every cleanup list drifts away from whatever produces the files. RFC 0023 proposes optional named artefacts: exact paths a manifest claims to own, and one bounded cleanup operation that previews its scope, asks for consent, and deletes nothing it has not validated.

Status on 19 October 2026

  • Not in v0.1.0-beta4. The released compiler has no artefacts mapping, produces field, or clean_owned operation, and netsuke clean has no --artefact option.
  • Proposed. RFC 0023 has status Proposed, created 19 September 2026, and landed in the documentation-only commit 54837e9.
  • Planned as phase 24. The progressive-enhancement roadmap splits the work into six tasks; none is ticked.
  • No release named. Supported-platform deletion primitives, resource-lease identity, ownership and source conflict rules, and concrete operator limits are to be ratified before implementation.

Why ownership

Today netsuke clean runs ninja -t clean, which removes the file outputs Ninja tracks and nothing else (see the CLI reference). Anything an action writes — a benchmark report, a cache, a prepared environment — falls outside it, so projects reach for a shell recipe, and “shell fragments obscure path, symlink, and error semantics.”

Different retention decisions

A report, a shared tool cache, a temporary directory, and a prepared environment should not share one blunt deletion rule.

Existing owners stay put

Ninja keeps ordinary output cleaning, the runtime keeps its dyndep sidecars, and RFC 0010 keeps command-local temporary directories. Nothing infers ownership from command text.

An assertion, not a proof

“Ownership is an assertion by the manifest author, not proof that a directory contains no valuable files.” The design makes scope inspectable and enforces its bounds without claiming to sandbox recipes.

Declaring ownership

An artefact has a required path and optional kind, role, and create_parent. The smallest declaration owns one directory and needs nothing else — no state, typed input, context, or package provider.

Proposed syntax, RFC 0023 §3 (verbatim)

artefacts:
  build-output:
    path: build
    kind: directory

actions:
  - name: clean-build
    command:
      clean_owned: [build-output]

Proposed syntax, RFC 0023 §3 (verbatim; the command is illustrative)

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

produces “records ownership and expected outputs; it does not make a benchmark incremental or authorize replay of an old result.” A declared output that is missing after the producer succeeds is a producer failure. Partial outputs from a failed run are kept for inspection, never quietly removed or reused as evidence.

Exact paths

path names exactly one workspace-relative object. kind defaults to file; recursive ownership needs an explicit directory. No globs in the first version.

A directory claims its subtree

Including children created later. The preview must say so plainly, and the claim is unsuitable for a directory shared with source files or another owner's mutable state.

One owner, one producer

Overlapping or case-equivalent roots are rejected, as are multiple producers. Roles — generated (default), report, cache, environment — describe intent, not a deletion or caching rule.

Scoped cleanup

Cleanup has two front doors and one implementation. In a recipe, clean_owned: [NAME, ...] is an execution unit in the structured command union; it names artefacts only and “never accepts raw shell paths.” On the command line, clean gains a repeatable --artefact NAME. Both share the same planner, validator, deleter, and structured results.

Proposed commands, RFC 0023 §5 (verbatim)
$ netsuke clean --artefact build-output --dry-run
$ netsuke clean --artefact build-output --force --no-input

Preview

A dry-run lists normalized roots, recursive ownership, existing and missing objects, retained siblings, and any rejected scope. It is “a read-only plan, not a reusable authorization token”: execution resolves and validates again. A plan that exceeds its bounds fails rather than truncating.

Consent

The existing --dry-run, --force, and --no-input flags; no cleanup-specific confirmation framework. A noninteractive destructive run without explicit consent fails before deleting. “--force skips confirmation, not validation, ownership conflicts, or bounds.”

Protected scope

Paths anchor at the workspace capability. Absolute paths, empty paths, the workspace root, parent traversal, and platform escape forms are rejected. The manifest, loaded configuration, known runtime scripts, declared sources, .git, and Netsuke's runtime directories are protected; a directory claim containing one is invalid.

Filesystem safety

Never follow a symlink or Windows reparse point during traversal; no lexical starts_with checks and no ambient rm -rf. Removing a hard link never truncates the shared file. Exceeding a bound in the admission pass causes zero deletions.

Plain clean is unchanged. Without --artefact, netsuke clean keeps its Ninja-output behaviour. With it, only the selected roots are cleaned: no Ninja outputs, environments, caches, or runtime directories join implicitly. Roadmap: 24.2.

Lifecycle and migration

Environments and states

An environment artefact may name the same path as a managed state. When it is selected for cleanup, Netsuke acquires the state's integrity lease and invalidates its readiness records before any deletion, failure paths included. “Probe success from before cleanup cannot establish subsequent readiness.” Neither feature requires the other: the artefact-only path never demands a state declaration.

Leases and ordering

Cleanup takes an exclusive lease over each selected root, ordered canonically, sharing the state-resource identity boundary rather than a second lock system. A selected build that both cleans and produces or consumes the same resource is rejected: “A Ninja pool would serialize access but would not establish the user's intended order.” Separate invocations are the remedy.

Migrating Cuprum

Declare disjoint output roots first, and keep unmatched wildcard cleanup as an ordinary command until a separate safe file-selection contract exists. Unrestricted glob deletion is explicitly not added just to reproduce a long shell command.

Not delivery

No remote delivery, content-addressed store, or provenance attestation. The main roadmap's delivery task 5.4.2 waits for ownership to be modelled; this RFC models it and stops there.

Roadmap: 24.3, lifecycle invalidation and staged migration.

From the shallow end

The guarantee belongs to RFC 0025. No heuristic promotes a redirection to an ownership claim. Across the proposal set, unused state or context declarations never cause runtime probes, installation, network access, or resource acquisition. The quickstart manifest stays a release-acceptance fixture, and basic usage is “a supported destination, not a temporary migration stage.” RFC 0023 calls itself “an opt-in improvement rather than an onboarding prerequisite.”

What stays optional

  • Plain commands, existing targets, and existing clean behaviour remain supported; the hello-world and legacy clean fixtures stay unchanged.
  • No target needs an artefact declaration. “Requiring an artefact declaration for every existing target would make an optional benefit contagious.”
  • A declaration with no producer is still useful for a legacy build directory: declaration, bounded preview, and authorization are enough.

The route in, one step at a time

  1. Keep netsuke clean for Ninja outputs.
  2. Declare one owned directory with only path and kind, and clean it with clean_owned.
  3. Preview with clean --artefact NAME --dry-run before trusting it.
  4. Name producers with produces for reports and other action outputs.
  5. Tie an environment artefact to a state, if the project uses states.
  6. Opt in to the owned-cleanup maturity rule for selected cleanup actions.

Roadmap 24.1 establishes ownership before any deletion is permitted, and task 24.3.2 must “demonstrate that the one-directory feature works without state syntax.”

Confidence

Planned, not existing. RFC 0023 §8 and roadmap phase 24 set these acceptance requirements for code that deletes files.

Properties

Canonical-path uniqueness, scope normalization, protected roots, and duplicate selection.

Adversarial end to end

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.

Previews that write nothing

Zero filesystem writes in dry-run, including no preparation or probe execution; refusal without consent; fresh validation on replay; and bounds that --force cannot bypass.

The design refuses claims it cannot back. “No claim of race freedom is acceptable until adversarial replacement tests pass on each supported platform.” Cleanup “is not an atomic transaction and must never report rollback that it did not perform.” Without source-control metadata Netsuke cannot identify every source file, and the contract must say so. A later cargo clean recipe keeps Cargo's semantics, outside Netsuke's guarantee.

Accessibility and localization

Every human-readable message Netsuke emits is localized by default through its Fluent catalogues, and cleanup is no exception: previews, confirmations, and failure reports arrive in the operator's locale, while artefact names, paths, and reason codes stay exactly as written so they can be matched. RFC 0023 adds requirements that bear directly on anyone deciding, from text alone, whether to delete something, and they apply in Netsuke's accessible output mode as much as anywhere:

  • The preview must state recursive scope “plainly”, and an interactive run asks for confirmation only after displaying it.
  • Partial failures report exact removed, retained, and failed objects rather than a summary verdict.
  • Command and recipe forms return the same structured results, so machine-readable output does not depend on which door was used.
  • RFC 0025 §8 tells maturity diagnostics, including owned-cleanup findings, to “Reuse Fluent localization”, the machinery behind Netsuke's existing locale catalogues.

Task 24.3.2 documents ownership assertions, recursive scope, retained caches, preview limitations, and external-command boundaries. RFC 0023 does not discuss accessibility beyond these requirements.