Forthcoming capability
Proposed · no release named Proposed RFC

Typed Inputs

A worker count of zero should fail before pytest starts, not halfway through a test run. RFC 0022 proposes an optional inputs mapping: types, defaults, and choices for the handful of values a manifest's callers are meant to set, validated before any command runs. Ordinary vars stay exactly as they are.

Status on 19 October 2026

  • Not in v0.1.0-beta4. The released compiler has no inputs mapping and the CLI has no --input option. Nothing on this page is implemented.
  • Proposed. RFC 0022 has status Proposed, created 19 September 2026, and landed in the documentation-only commit 54837e9. It builds on the parameter model of RFC 0003, which is itself still Proposed.
  • Planned as phase 21. The progressive-enhancement roadmap splits the work into five tasks; none is ticked.
  • No release named. The CLI option's placement in canonical metadata, profile overlay details, and the exact integer and path-normalization contracts are to be ratified before implementation. “No release should force a broad migration merely because this optional vocabulary exists.”

Why typed inputs

Today vars are global strings, numbers, Booleans, or lists handed to Jinja (see templating). Nothing says which of them a caller may set, what type they must be, or what range is sensible. The RFC's diagnosis: “Raw flag strings combine user intent, argument transport, and tool-specific syntax.”

Fail before the first command

Validation should reject an invalid worker count before anything starts, while argv handling stays with RFC 0001's structured commands.

One parameter contract

Root inputs reuse RFC 0003's bundle-parameter types, validation, and redaction. The RFC explicitly refuses “a rival parameter system”.

Not another config loader

Profiles and their OrthoConfig integration stay with roadmap phase 5. This RFC supplies task-input semantics, not another loader, profile store, or command metadata generator.

Declaring inputs

Adoption is one value at a time. The RFC's own example promotes a single worker count and leaves everything else untyped.

Existing-style fragment, still valid (RFC 0022 §3)

vars:
  workers: 2

actions:
  - name: test
    command: "pytest -n {{ workers }}"

Proposed syntax, RFC 0022 §3 (verbatim)

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 }}

Writing inputs.workers instead of workers is the promotion. There is no implicit alias and no mutation of vars. Adopting an input also does not silently turn a legacy shell string into direct execution; the example switches to invoke because the RFC recommends structured invocation for dynamic arguments.

Field Meaning
type Required. Exactly one of string, bool, integer, path, sequence<string>, sequence<path>, or mapping<string, string>. No enum or argv type.
default Literal data, never a Jinja program. No default means required.
minimum, maximum Integer bounds. Inverted ranges and defaults that break their own contract are rejected.
choices A nonempty, duplicate-free list. A string with choices is the enum.
description Optional human description.
expose Values are redacted by default; only the exact declaration expose: non-secret allows ordinary inspection.

Adapted: field names from RFC 0022 §4, not an RFC example

inputs:
  mode:
    type: string
    choices: [debug, release]
    default: debug
    expose: non-secret

Validation before execution

Roadmap 21.1 validates values before Jinja expansion, so a bad value never reaches a template, let alone a process. Root inputs and bundle parameters are to accept and reject the same values.

Strict, not helpful

“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 value. Out-of-range integers are rejected.

A path is not a permission

path checks encoding and lexical shape without creating files. Whatever later opens, runs, or deletes that path applies its own workspace and operator constraints — including scoped cleanup, which accepts artefact names, never raw paths.

Immutable namespace

inputs cannot change during manifest expansion. A manifest that already defines vars.inputs and opts in gets a targeted migration diagnostic rather than a silent overwrite; one that does not opt in is untouched.

Bounded

Existing evaluation budgets cap definition count, collection depth, element count, byte size, and diagnostics, applied before expensive allocation. Collections are homogeneous and validated element by element.

Callers and profiles

The RFC proposes one CLI option, --input NAME=VALUE, on commands that compile a manifest. Parsing is type-directed, not shell splitting or YAML evaluation: strings and paths keep the text after the first = exactly; integers are signed decimal, no floats; Booleans are exactly true or false; sequences and mappings are JSON with duplicate keys rejected. Unknown names and repeated occurrences are errors.

Adapted: the RFC names the option but gives no full command line
$ netsuke build --input workers=4

Against the declaration above, --input workers=0 would fail minimum: 1 before any command starts.

Precedence, highest first

  1. Explicit CLI input
  2. Explicitly selected profile input
  3. Resolved configuration input
  4. Manifest default

No environment variable supplies an input in the first version. The value is validated once and keeps its winning source and bounded provenance for the sources it shadowed. Operator ceilings apply after selection; a project cannot override them.

Profiles, bundles, plans

“Profiles bind input values; they do not replace input definitions.” A bundle receives explicit values through with, as bundle.params, never the importer's whole inputs. Root inputs must not wait on bundles.

Resolved values used by an action or a state join its existing fingerprint; unused inputs must not change a command's argv. Generated plans freeze their values, so replay never reads a different ambient profile.

Roadmap: 21.2, bind explicit callers and profiles to the same interface.

From the shallow end

The guarantee belongs to RFC 0025. Ordinary variables remain supported. Across the whole proposal set, unused state or context declarations never cause runtime probes, installation, network access, or resource acquisition, and the additions introduce no new inspection-time execution. The quickstart manifest stays a release-acceptance fixture, and basic usage is “a supported destination, not a temporary migration stage.” Supported basic manifests get no new maturity messages under default settings, including any hint that untyped usage is deprecated.

What stays optional

  • Existing untyped manifests keep their parse, execution, and diagnostic behaviour under the default policy.
  • Promoting one value needs no context, state, bundle, or annotation of its neighbours. Internal variables stay untyped unless their author says otherwise.
  • Older Netsuke releases must reject the new syntax with a useful version remedy, not ignore it.

Why not mandatory

“Mandatory annotations would obstruct onboarding.” And the opposite mistake: “Implicit promotion of every variable would unexpectedly create a public API.” The design sits between them — public only when an author says so.

The route in, one step at a time

  1. Keep vars. Nothing changes.
  2. Promote the one value callers actually set, as inputs.workers above.
  3. Set it from the command line with --input, or bind it in a profile.
  4. Pass it on to a bundle explicitly through with.
  5. If a project wants it enforced, the opt-in typed-inputs maturity rule checks named subjects only; it never guesses which variables are public.

Roadmap 21.3 sets the bar: “no context, state, or bundle is required to use a validated worker count.”

Confidence

Planned, not existing. RFC 0022 §8 and roadmap phase 21 set these acceptance requirements.

One corpus, two consumers

The same value corpus runs through bundle and task-input validation: false-as-integer, overflow, whitespace, empty strings, duplicate keys, invalid constraints, required values, unknown names, hostile argv elements, Windows paths, and collection order.

Properties

Property tests for source precedence, normalization stability, and redaction of rejected values. Every precedence pair is tested, with redaction checked in errors, verbose output, and JSON.

Observed, not rendered

“Compare actual child argv, not just rendered YAML.” The unmodified hello-world fixture is retained, and plan replay is tested against a later, changed ambient profile.

Determinism is part of the contract: collection order is preserved and mapping keys canonicalized. A maturity setting “cannot disable type correctness, resource limits, or capability checks for an opted-in declaration.” And the honest limit: typed data alone does not stop a tool-specific flag bag from overriding the declared worker policy.

Accessibility and localization

Every human-readable message Netsuke emits is localized by default through its Fluent catalogues, so a rejected value, a missing input, or a type mismatch is explained in the caller's locale; input names, type names, and choices stay exactly as declared, because they are what the caller types. RFC 0022 does not discuss how input diagnostics render in Netsuke's accessible output mode. What it does say concerns what a diagnostic contains and what it must withhold:

  • A validation error shows 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.
  • Values are redacted by default. “Redaction is not secret storage”: the RFC introduces no secret type and promises no confidentiality for values passed as process arguments or persisted in plans.
  • Input documentation flows through the existing documentation and schema pipelines, “without a parallel input-help renderer”, and inspection names the effective profile, source, and contract without running a build.
  • RFC 0025 §8 tells maturity diagnostics, including typed-inputs findings, to “Reuse Fluent localization” and not to leak input values. Netsuke's existing Fluent catalogues are that surface.

An input’s description is the manifest author’s own text, so Netsuke shows it as written.