fix: backport aspect-pipeline fixes from entity-gen-schema-port#589
Merged
Conversation
Nested aspects from freeform traversal (e.g., den.aspects.disk.zfs-disk-single) have __provider set by aspectContentType.merge but lack name/meta. Use __provider to derive the path key, matching how the pathSet stores these entries. (cherry picked from commit 5a3bf90)
- has-aspect.nix: accept refs with __provider (set by aspectContentType) - types.nix: annotate nested attrset children in content merger with __provider so deeply nested aspects carry provenance - Only annotate unregistered keys (skip class/pipe/structural keys) - Tests: nested present/absent, provenance distinct, deeply nested (3 levels) (cherry picked from commit aa11ce8)
aspectContentType's multi-def branch forwarded sub-keys with a shallow
`//`, so when several files each contribute a different child under the
same deeply-nested namespace, all but the last were dropped from
navigation (e.g. services/network/cilium/{cilium,hubble-ui,
cilium-bgp-resources}.nix all defining children of network.cilium).
Deep-merge instead: colliding attrsets recurse and colliding lists
concatenate (matching den's own merge semantics); scalars keep
last-def-wins, with __contentValues remaining the canonical source for
emit/forward collection. Adds a deadbugs regression test.
Full suite 860/861; the one failure (issue-583) is pre-existing
nixpkgs drift (nixpkgs now declares programs.atuin.flags, colliding with
the test's mock module) and fails identically at HEAD.
(cherry picked from commit ca4569a)
wrapChild only injected identity from __provider for content wrappers carrying __contentValues; a single-def navigated nested aspect carries __provider (its full path) but no __contentValues, so it fell through nameless and children.nix renamed it to <parent>/<anon>:<idx>. That gave the same nested aspect a different identity depending on the inclusion path — apps.gaming.steam reached via roles.gaming (host scope) vs. a per-user entity-named aspect's includes (applied by a policy at user scope) — defeating cross-scope dedup, so its nixos content (programs.steam.package) was defined twice. Derive name + meta.provider from __provider whenever a navigated child has no name. Adds a cross-scope dedup regression test. Full suite 861/862 (only the pre-existing nixpkgs issue-583). (cherry picked from commit beb2b49)
…tuin.flags The issue-583 forwarding test mocked options.programs.atuin.flags. nixpkgs gained that option in rev 64c08a7 (CI lock bumped in 4701e77), so when the denful#583 fix landed on this branch the mock redeclared an option nixpkgs already owns -> "option programs.atuin.flags is already declared" -> the test failed (it passed in the PR's original, older-nixpkgs context). Forward into a custom `forwardTarget` option instead, so the mock can't collide with nixpkgs. Behaviour and intent unchanged; full suite now 862/862. (cherry picked from commit a2cb870)
The host-aspects battery re-resolved the host aspect tree for a user's
classes (homeManager) in an isolated sub-pipeline seeded with only
{ host, user }, dropping the ancestor context the host scope actually
carries (e.g. a parent `environment` entity). A parametric host quirk
emit `{ environment, host, ... }: ...` was then stranded as a raw
function at the {host,user} projection scope, crashing any homeManager
consumer that read the pipe ("expected a set but found a function").
from-host now fires as a policy (receiving the full resolveCtx) and
threads the ambient entity-kind chain bindings into the re-resolution,
so re-fired parametric host aspects bind the same args they would at the
host scope. The same threading is applied to home-env's userForward
extraction path.
Adds deadbugs/host-aspects-chain-ctx regression test.
(cherry picked from commit eea3d6b)
02c444f to
bb6482c
Compare
`_` (alias for `provides`) lived only on aspect leaves, so `den.aspects.foo._` worked but `foo._` — where `foo` is a namespace root — threw `attribute '_' missing`. A namespace root is a container, not an aspect, so it has no provides of its own. Add a synthetic, read-only `_` to the namespace container: an aggregate aspect whose includes are every aspect declared in the namespace, so `[ ns._ ]` pulls them all in — the container-level analog of an aspect's provides bundle. Structural keys (stages/schema/classes/_module/_) are excluded, and aspect-schema's class collection skips `_`. Reported in denful#588.
theutz
approved these changes
Jun 2, 2026
…ce re-import The namespace-root `_` provides bundle is a computed, read-only option. It was being serialized into the exported `flake.denful.<ns>` and fed straight back as a definition on re-import (`den.namespace name [sources]`), colliding with the read-only option: forcing a re-imported `<ns>._` threw "The option 'den.ful.<ns>._' is read-only, but it's set multiple times". Existing namespace-provider tests passed only because they never force `._`. Drop `_` from the exported namespace and strip it in stripAliases on import — mirroring how stripAliases already drops the aspect-level `_` aliases — so the importing side recomputes its own bundle. Found in code review of denful#589 (the namespace-root `_` feature it introduced).
…tting-node A general primitive — `spawnNode` — materializes a child resolution node from any parent scope, threaded with the parent pipeline's resolved scope-tree state (parent + siblings), so the node's own assemblePipes re-derives inherited/ collected pipe values with full fleet visibility. Paired with resolve-at-emitting- node: a pipeline-parametric pipe emit resolves to concrete data at its emitting node on every crossing (local/collected/exposed), never as a function; config-dependent emits stay deferred (__configThunk). Home extraction is the first and driving consumer: it replaces three isolated sub-pipelines (host-aspects resolveImports; makeHomeEnv/hm-host resolveEntity) with spawnNode, fixing the originating bug where a host-aspects-projected ssh app saw only the local host, never the fleet peers. The mechanism is general (den-hoag `spawn` with one read-only inherited edge) and applies to any parent->child entity relationship; home is just where it is currently exercised. - assemble-pipes: resolve-at-emitting-node on the collected and exposed crossings. - spawn-node: spawnNode primitive (re-walk for one class, merge parent state, own assemblePipes, class isolation). - policy.spawn effect + register-spawn handler + drain augmentation: a deferred node spawn resolved post-walk. - route: forward source resolves via spawnNode (from = parent scope); drop the chainCtx workaround in home-env.nix. - Includes the scopeParent-walk pipe inheritance keeper. - Tests: all-peers, resolved-users, in-tree==threaded equivalency, server-host membership. Full CI 870/870. (cherry picked from commit 7875506)
3c11bb2 to
c57ac6b
Compare
The late-sibling dispatch re-fired every policy registered at the parent OR any sibling scope at every sibling (entity-kind filter only). So a policy a user registered via its own includes — opting into the host-aspects battery, a per-user `to-users` policy, etc. — fanned to every other user on the host, regardless of opt-in. Make eligibility ancestor-or-self: at each sibling, only policies registered at the parent (the host, whose subtree spans every user) plus the sibling's own fire. A user's runtime includes stay in its own subtree; host-registered provides still fan to all users (the legacy mutual-provider pattern). Fixes host-aspects projecting a host's homeManager onto users who never included the battery (a pre-existing leak, not from the spawnNode work). host-aspects.nix is unchanged — this is a dispatch fix, not a per-battery guard. Tests: host-aspects-sibling-leak (regression guard); user-host-mutual-config re-patterns the user->siblings case to host-level registration and adds test-user-include-stays-in-subtree. (cherry picked from commit 4200f37)
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Backports the standalone aspect-pipeline bug fixes that accumulated on
feat/entity-gen-schema-portso they can land onmainindependently of the gen-schema entity port (which is still in flight) and the den-diagram extraction (tracked in its own PR).Most commits carry a
(cherry picked from commit ...)trailer back to the source branch. One commit (e711eebe) is net-new — authored directly on this branch with no cherry-pick origin (see Net-new below).Generalized node-spawn + resolve-at-emitting-node (
c57ac6b8)A general primitive —
spawnNode— materializes a child resolution node from any parent scope, threaded with the parent pipeline's resolved scope-tree state (parent + siblings), so the node's ownassemblePipesre-derives inherited/collected pipe values with full fleet visibility. Paired with resolve-at-emitting-node: a pipeline-parametric pipe emit ({ <arg>, ... }: …) resolves to concrete data at its emitting node on every crossing (local / collected / exposed), never as a function; config-dependent emits stay deferred (__configThunk).The mechanism is general — den-hoag
spawn: a node with one read-only inherited edge, applicable to any parent→child entity relationship (fleet→host, environment→host, host→user, host→home). Home extraction is the first and driving consumer: it replaces the three isolated home-extraction sub-pipelines (host-aspectsresolveImports;makeHomeEnv/hm-hostresolveEntity) withspawnNode, fixing the originating bug where a host-aspects-projected ssh app saw only the local host, never the fleet peers.This supersedes the
home-envchainCtx workaround (eea3d6b1, under Fixes): that commit threaded ambient resolution-chain context into the isolated extraction as a stopgap; this change removes it —environmentand fleet-collected pipe data now arrive structurally via the threaded scope-tree state.assemble-pipes: resolve-at-emitting-node on the collected and exposed crossings; config-dependent emits stay deferred.spawn-node.nix: thespawnNodeprimitive (re-walk for one class, merge parent state, ownassemblePipes, class isolation).policy.spawneffect +register-spawnhandler + drain augmentation: a deferred node spawn resolved post-walk.route/apply.nix: forward source resolves viaspawnNode(from = parent scope).Fixes
hasAspect — nested refs via
__providerchain (5a3bf908):hasAspectresolves nested aspect refs through the provider chain.hasAspect — nested freeform refs at any depth (
aa11ce89): supports nested freeform aspect references regardless of depth, with provenance kept distinct.deep-merge nested namespace children across files (
ca4569af): colliding nested namespaces declared across multiple files now deep-merge instead of last-write-wins.navigated nested aspects get their own identity (
beb2b493): nested includes dedup correctly across scopes instead of sharing identity.home-env resolution-chain ctx (
eea3d6b1): threads the resolution-chain context into home-manager extraction so parametric host quirks survive home projection. Superseded by the node-spawn unification above (c57ac6b8), which removes the chainCtx workaround in favor ofspawnNode's threaded scope-tree state.policy dispatch — runtime-include policies scoped to their subtree (
439c3dc5): the late-sibling dispatch re-fired a sibling's own-include policies at every other sibling (entity-kind filter only), so a user opting into a battery — or any{host,user}policy a user registered — leaked to every other user on the host. Eligibility is now ancestor-or-self: a user's runtime includes stay in its subtree; host-registered provides still fan to all users. Fixes host-aspects projecting onto non-opt-in users (pre-existing leak).Net-new
_provides bundle on namespace roots (e711eebe): a namespace root now exposes_like an aspect, so[ foo._ ]bundles the namespace's root-level aspects — the container-level analog ofden.aspects.foo._. The bundle is a flat map over the container's direct keys (structural keysstages/schema/classes/_module/_excluded); it does not recurse to enumerate nested aspects. Each root-level aspect is included whole, so its nested children still arrive — via normal include resolution, not via_descending — and the aspect-leaf_(e.g.foo.app._) remains a distinct, separately-computed bundle. Previouslyfoo._threwattribute '_' missingbecause a namespace root is a container, not an aspect. Reported in discussion Issue regarding usage of namespace aspects with ._ #588. Not a backport (no upstream commit); landed here per maintainer call._on re-import (95b6277e): follow-up fix found in code review — the computed_was serialized into exporteddenfuland collided with the read-only option when a re-imported namespace forced._. Now stripped on export and import.Tests
a2cb8708): stops the issue-583 forwarding-overwrite test mock from colliding with nixpkgsprograms.atuin.flags.e711eebe): 5 tests covering the namespace-root_bundle, theden.aspects.foo._baseline, explicit-list equivalence, aspect-leafns.app._, and structural-key exclusion.c57ac6b8): 8 tests — all-peers (collected, host-aspects projection), resolved-users (exposed), config-thunk deferral, in-tree ≡ threaded equivalency, server-host membership, and a complex-forward source-fallback guard.Excluded
chore: update flake.lockcommits (bump gen-schema/den-diagram inputs not present onmain).Verification
home-extractionsuite).home-extraction8/8,host-aspects10/10,host-aspects-chain-ctx1/1 (parametric host quirk survives projection via the threaded scope-tree state, with chainCtx removed),pipe-scope16/16,pipes9/9,deadbugs23/23,has-aspect35/35.just fmtclean.