Skip to content

Instantly share code, notes, and snippets.

@sini
Created June 13, 2026 05:40
Show Gist options
  • Select an option

  • Save sini/16f1941e6b8683419840aa70ca3a4137 to your computer and use it in GitHub Desktop.

Select an option

Save sini/16f1941e6b8683419840aa70ca3a4137 to your computer and use it in GitHub Desktop.
den delivery-edge unification — net behavior changes (1 latent-bug fix + 1 additive API; reference host byte-identical)

den delivery-edge unification — net behavior changes

Branch feat/entity-gen-schema-port, commits e71860b7..d3c74242 (the delivery-half rewrite: nine ad-hoc delivery mechanisms collapsed into one (S, T, P, M) edge algebra). This document enumerates every net runtime behavior change versus the pre-rewrite baseline, with justification. Spec + plan + deviation ledger live in den-architecture/{specs,plans}/2026-06-12-delivery-edge-unification-*.

TL;DR

This is a behavior-preserving refactor with exactly one intended runtime change and one additive API. The headline proof: the reference host's full system closure is byte-identical — same derivation hash — built against den at the pre-rewrite baseline and at HEAD. Not "flake-source-only delta"; the same store path. Every existing configuration evaluates to exactly what it did before, with one exception (a latent-bug fix that no shipping config was hitting).

  • 1 intended behavior change: a latent cross-host config-resolution bug, fixed.
  • 1 additive API: the deliver primitive (new surface; nothing removed).
  • 0 regressions, 0 semantic flips to any existing configuration.

Verification: den CI 974/974; the delivery-contract fixture suite 14/14 byte-stable; reference-host toplevel byte-identical across the entire port.


Change 1 — cross-host config resolution over a pipe-consuming peer (the one real fix)

Status: intended fix of a latent bug. The only change in evaluated output for any input that exercises it.

Before

When one host's configuration depended on another host's fully-evaluated config — the cross-host config-thunk path, used by fleet pipe.collect / collectAll that feed a peer's config — the peer host's config was built over raw scope contexts: the context slice before pipe values were assembled into it.

If that peer's aspect tree contained a pipe-consuming module — one that destructures a pipe/quirk value out of its context, e.g. { feat, ... }: { ... } where feat is a value produced by the pipe layer — the peer config was built with that value missing, and evaluation threw:

error: attribute 'feat' missing

This diverged from the peer's real instantiate output (which builds over assembled contexts and resolves feat correctly). It was a genuine latent bug: no shipping configuration combined "cross-host config-thunk" with "pipe-consuming peer aspect", so it never fired in practice — but it was a real correctness gap, surfaced by the census that opened this effort.

After

The cross-host peer-config build now runs over an assembled context (an assemblePipes pass with cross-host config resolution disabled). Pipeline-parametric pipe values are injected, so a pipe-consuming peer aspect resolves correctly and the peer config builds instead of throwing.

Why this approach

The naive fix — "just build peer configs over the already-assembled contexts" — is a dependency cycle: assembling contexts triggers cross-host config resolution, which is the very thing being built. The fix breaks the cycle by running a context-assembly pass with cross-host resolution turned off: that pass is cycle-free yet still resolves all pipeline-parametric pipe values (the feat case), which is what the peer aspect needs.

Documented residual

A peer aspect that defers on a value which is genuinely config-dependent — i.e. its value can only come from another peer's fully-built config, the exact thing mid-resolution — stays deferred. That is an unbreakable mutual-config recursion, not something any single pass can resolve; it is documented in code and covered by an asymmetric-topology test that isolates the fixed bug from the genuine recursion.

Evidence

A regression witness (bprime-basedrain-crosshost) was added: a defect case (cross-host thunk reading a pipe-derived config field) plus a control. Reverting the fix makes the defect case throw feat missing while the control still passes — confirming the test has teeth and the fix is load-bearing.


Change 2 — deliver primitive (additive API, no behavior change)

Status: new public surface. Nothing removed; no existing behavior altered.

A user-facing delivery primitive was added: deliver { from; to; at ? []; mode ? "merge"; } — the direct expression of one delivery edge. from is either a class name (collect that class's content) or { module = …; } (inject new content); mode ∈ merge | nest | verbatim.

route and provides are now thin permanent shims over deliver. Their signatures are unchanged and they desugar to identical delivery descriptors (proven by descriptor equality + the edge-trace oracle), so every existing route/provides/policy.provide call behaves exactly as before. The shims carry a # TODO marker for a future deprecation warning, but no warning is emitted today — they may live on permanently as sugar.

Surface notes:

  • mode = "verbatim" is explicit (replaces the old reinstantiate = true route flag at the new surface; the route shim still accepts reinstantiate and maps it to verbatim, so existing reinstantiating routes are unaffected).
  • appendToParent and collectSubtree are deliberately not on the deliver surface — they remain route-shim-internal mechanisms. Consequently the nix-config microvm-guest delivery (which needs both: parent-scope targeting + subtree-source widening) stays on the route shim permanently — there is no migration. (An earlier spec draft claimed a guests-to-deliver migration; that claim was found incorrect during implementation and struck — deliver's to names a class, not a scope, so it cannot express the parent-scope redirect the guest edge requires.)

What did NOT change

Everything else. Concretely:

  • The reference host's full system closure is byte-identical — same derivation hash — built against den at the pre-rewrite baseline vs. HEAD. This subsumes, by closure membership, that agenix identity paths, the microvm guest config (+ its passthrough gating), impermanence, home-manager delivery, and every other delivery-fed surface are unchanged.
  • route / provides / policy.provide semantics — identical (shims desugar to the same descriptors).
  • reinstantiate and appendToParent route behavior — identical (still reachable via the route shim).
  • Isolation semantics (isolated entities are their own delivery roots) — identical; now expressed as "no default-fold edge into the parent" (edge-absence) rather than a mid-walk filter, but the resulting content is the same.
  • Pipe / quirk data-flow — out of scope and untouched (only an internal stage-interpreter de-duplication, behavior-preserving).
  • The instantiation-root selection for entity outputs — identical results; the old name-matching heuristic (findHostScopeId, matching scopes by name infix) was replaced by an exact link recorded at scope creation, but it resolves to the same scope for every input, including the multi-system same-name case.

Internal-only structural changes (no observable behavior)

For completeness — these changed how delivery works, with no change to what it produces:

  • Nine delivery mechanisms (default class-fold, simple route, verbatim route, complex forward, provides, host-aspects spawn projection, instantiates, + two pipe flows that stayed out of scope) → one (S, T, P, M) edge algebra: a single mechanism-free mode switch, one constructor per mechanism, a toposort for ordering, and an explicit root-context projection replacing three hand-threaded phase-fold re-entries.
  • The findHostScopeId name-infix heuristic was dissolved into an exact scope-creation link.
  • Write-only pipeline state, four duplicated subtree-walk implementations, three hand-rolled dedup loops, and a provenance-forked stage interpreter were unified.
  • A read-only edge-trace oracle was added that renders delivery decisions as a normalized edge list and shares the same constructors production uses — so the oracle and the runtime cannot drift. A 14-fixture suite pins this as the delivery contract.

Line count rose ~+550 deliberately: the algebra is now explicit, typed, and oracle-verified where the old mechanisms were implicit and untested; all the duplication was removed.


Verification summary

Gate Result
den CI (full battery) 974/974
delivery-contract fixtures 14/14 byte-stable
deliver API suite 11/11
reference-host toplevel (baseline vs HEAD) byte-identical (same store hash)
B′ fix witness has teeth (fails on revert, passes with fix)

The single intended behavior change is a fix; everything else is provably unchanged.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment