ADR-120
descend the shared service execution driver out of nika-runtime
accepted · 2026-08-23 · L3 L4
0ADR-120: descend the shared service execution driver out of `nika-runtime` #
Context #
W06 adds one shared driver that joins descriptor-rooted, owned-byte admission
from nika-execution to the production composition already owned by
nika-runtime. The local CLI and future service adapter must use this same
driver so nested workflows, permits, closure digests, redaction, and effect
construction cannot drift between interfaces.
Keeping that driver inside nika-runtime raised the crate from 14,931 to
16,024 production lines. scripts/ci/check-crate-size.sh enforces a hard
15,000-line budget. Hiding source from the counter, weakening the ceiling,
compressing the implementation, or dropping tests would preserve the wrong
boundary rather than fix it. ADR-108 and the later nika-secret descent
establish the workspace precedent: when a coherent concern fills a crate,
move the concern into the lowest honest Diamond layer.
Decision #
Create the WIP L3 crate `nika-service-execution` and move the W06 driver and its focused tests there as one unit.
The new crate depends on the two L3 peers whose contracts it joins:
nika-executionowns immutable snapshot admission and the sealedExecutionContextpairing;nika-runtimeowns generic DAG execution and production composition.
nika-runtime no longer depends on nika-execution or the driver's
tempfile test dependency. It exposes one narrow additional seam,
compose::service_runtime, beside production_runtime; the driver remains
the only owner of its service-safe surface and metadata-only result types.
nika-cli imports the moved driver from nika-service-execution. No HTTP,
listener, authentication, job-store, SDK, or nika-serve code enters this
split.
The moved public types intentionally leave the nika_runtime namespace and
live under nika_service_execution. Both crates are internal, unpublished
workspace crates, and every current consumer is updated in the same commit.
The regenerated nika-runtime/public-api.txt records the removal and the one
new composer seam explicitly.
Consequences #
Positive #
nika-runtimereturns below the hard production-LOC ceiling without an exemption or behavioral deletion.- Local CLI/ARM and future remote execution keep one production driver and one child-composition implementation.
- Snapshot admission remains absent from the generic runtime crate.
- The service result remains redacted and metadata-only; the move adds no transport surface.
Negative #
- The workspace gains a seventieth crate and one same-layer L3 dependency.
- The internal Rust namespace changes for the eight moved driver types.
service_runtimebecomes public so a sibling crate can consume the narrow production seam; its public baseline must remain floored.
Neutral #
nika-service-executionlands WIP and is not published independently.Cargo.lock, the Diamond layer registry, and the CLI dependency graph gain the new member.- The split changes ownership, not execution semantics.
Required evidence #
scripts/ci/check-crate-size.shpasses with every crate below 15,000 production lines.scripts/ci/check-layering.shaccepts all 70 workspace crates.cargo check --workspace --lockedpasses with the regenerated lockfile.- Focused library tests pass for
nika-cli,nika-runtime,nika-execution, andnika-service-execution. - Workspace all-target clippy passes with warnings denied, and workspace formatting is clean.
nika-runtime/public-api.txtis regenerated deterministically and containscompose::service_runtimebut none of the moved driver types.- The CLI extinction fixture reads the new crate and continues to reject a second execution/composition path.
Alternatives considered #
Shrink or exempt `nika-runtime` #
Rejected. The 1,024-line excess is a real architectural boundary signal. An
exemption, test deletion, cfg trick, or compressed implementation would make
the maintainability gate dishonest.
Put the driver in `nika-execution` #
Rejected. The driver consumes nika-runtime composition; making
nika-execution depend on runtime would invert the admission boundary and
create a cycle with the pre-existing runtime-to-execution edge.
Put the driver in an L4 interface crate #
Rejected. CLI, ARM, and Serve must share it. An interface-owned driver would make one interface canonical and force the others to depend sideways on an adapter rather than on a shared L3 implementation.
read at v0.114.0 · the decision record ships with the engine