Literate AI

Repository documentation

ADR 0047: Admit native CLI Components through verifier-owned cases

Context

The portable application contract intentionally accepts one JSON argument and emits one JSON result. A native command-line program instead owns ordinary argument vectors, exit status, standard streams, file inputs and output artifacts. Reclassifying such a program as portable-application would misstate its public interface, while classifying it as a generic Component currently makes project validation reject its authored native-cli-application kind before lifecycle work can begin.

Draft PR #484 demonstrates a useful direction: a distinct Component and entrypoint kind plus verifier-owned cases. It is not current authority. The draft describes its argument model as POSIX despite supported Windows targets, admits executable paths from ambient environment variables, reads those executables outside accepted dependency custody, and validates only named output files rather than the complete filesystem delta. It also lacks a synthetic end-to-end Component proving locks, selected native Flavors, packaged executable custody and receipt invalidation.

Decision

Add a distinct kind without changing the portable ABI

Add native-cli-application to ComponentKind and native-cli to entrypoint kinds. The kind requires at least one native CLI entrypoint and never inherits the JSON argv, test-mode or smoke-mode protocol of portable-application.

ADR 0026 still governs multiple entrypoints and deployment units. A Component may have multiple native CLI entrypoints, and each receives an independently named acceptance binding. Mixing native CLI, portable CLI, persistent service or web entrypoints is allowed only when the existing kind-set rules resolve the combination unambiguously; otherwise validation refuses it with a diagnostic naming the conflicting kinds. This ADR does not change kind priority to make one entrypoint silently dominate another.

The Component lock, package plan and receipt bind every native CLI entrypoint's name, deployment unit, package-relative executable path, selected native build/platform Flavors and command projection. Absolute host paths and executable environment variables are not authored Component authority.

Use a portable, exact verifier-owned case contract

Each native CLI deployment unit requires a versioned acceptance document in the existing verifier-owned acceptance directory. Its identity binds the Component and entrypoint identities, accepted specification-set identity, target tuple, fixed environment policy, resource bounds and a canonical nonempty case list.

A case declares:

Paths are normalized portable relative paths. Case identifiers, paths and expectation sets are unique and canonical. Placeholders cannot select arbitrary host paths. Binary fixtures and outputs are supported as identity-bound bytes; text predicates require valid UTF-8 explicitly. Undeclared created, removed or changed filesystem entries, links, devices, sockets and path escapes fail acceptance.

The acceptance schema is published in the installed schema catalog. Its loader rejects unknown fields, empty case sets, conflicting expectations, prefix/path collisions, unbounded data and a specification or entrypoint identity that is no longer current.

Execute the packaged product in lifecycle custody

Normal packaged execution records that native CLI invocation is deferred to independent acceptance. The independent verifier resolves the selected entrypoint only through the accepted package plan/result and rechecks its executable identity and package-tree custody before and after every case. It never substitutes a source-tree executable or an ambient executable named by an environment variable.

Each case runs in a fresh directory below the lifecycle object root with a minimal, fixed target-appropriate environment, locale, timezone and umask. Only explicitly declared non-secret environment values may be added. The process receives an argv list directly without shell parsing. Tree-wide termination, deadlines and bounded capture apply on every supported platform. The verifier snapshots the complete case tree before and after execution and compares the result with the declared projection before recording evidence.

Acceptance runs on the selected compatible lifecycle worker that owns the packaged artifact and target toolchain. Controller-local execution is valid only when the controller is that selected worker. Returned evidence binds worker observation, target, package plan/result, executable, acceptance contract, per-case argv and input/output identities, exit status and stream identities. The finalized project receipt binds that evidence, so changed cases, specifications, Flavors, executable bytes, target or worker execution invalidate it.

Host tools needed to build the native CLI remain governed by resolved lifecycle tool bindings. If a case needs an additional executable as part of the product contract, it must be an accepted runtime dependency in package custody; ambient PATH or arbitrary environment-variable executable injection is refused.

Qualification and rollout

Implementation proceeds under the explicit maintainer acceptance recorded on 2026-09-25. Reconcile PR #484 by reimplementing reviewed slices against current authority rather than merging it wholesale. Qualification must prove:

Land in three reviewable slices: kind/schema/lock authority; local packaged acceptance with the synthetic fixture; then worker and complete supported-platform qualification. No slice may claim native CLI support from parser-only or mocked execution evidence.

Consequences

Native command-line applications become honest first-class Components without weakening the portable JSON application contract. Verifier-owned cases can express real CLI behavior while locks and receipts remain bound to the exact packaged executable and native target.

The contract is intentionally stricter than the retained draft: ambient helper executables and undeclared filesystem changes are refused, and complete qualification requires supported-platform and worker evidence. Interactive terminal sessions, long-running services and shell-language command strings remain outside this Component kind and require their existing or future dedicated contracts.

Read the Markdown sourceConference homePublished 1.3.0 · Patch 1.3.1 in progress · Development 1.4