Skip to content

fix: preserve source entity binding in forward fallback - #627

Merged
sini merged 2 commits into
denful:mainfrom
ZetabS:fix/forward-fallback-source-binding
Jul 2, 2026
Merged

fix: preserve source entity binding in forward fallback#627
sini merged 2 commits into
denful:mainfrom
ZetabS:fix/forward-fallback-source-binding

Conversation

@ZetabS

@ZetabS ZetabS commented Jul 2, 2026

Copy link
Copy Markdown
Contributor

Problem

Refs #620.

The documented platform-specific hmPlatforms example can fail when a
user-scoped complex forward includes a fromClass whose class key never appears
in the user's aspect tree and is not explicitly registered in den.classes.

For example, a user may include hmPlatforms and define only hmLinux content.
The generated forward for another class, such as hmDarwin, then has no
collected source modules when there is no hmDarwin key in that user's aspect
tree and hmDarwin is not registered as a class.

In that case getCollectedSource returns [], so resolveSourceFallback
rewalks the source aspect from the parent scope to keep fleet visibility. That
fallback used bindings = { }, dropping the source scope's entity binding. For
a user-scoped forward, losing the user binding can make the spawn collapse
into a self-parent edge:

den: spawnNode spawn root equals its parent scope 'host=igloo,system=x86_64-linux'
— a self-parent edge collapses policyBoundAncestor to null and yields zero fleet peers.
The seed ctx likely lost its child binding (e.g. `user`).

Fix

Restore the source scope's entity binding during complex forward fallback
rewalk.

The binding reconstruction mirrors the existing
mkDrained drained-spawn path:

sctx = scopeContexts.${scopeId} or { };
ownKind = scopeEntityKind.${scopeId} or null;
ownRecord = if ownKind == null then null else sctx.${ownKind} or null;

bindings = {
${ownKind} = ownRecord;
};

The fallback now follows the same pattern: look up the source scope's entity
kind, read that entity from scopeContexts, and pass it as the spawn binding.

This keeps the existing parent-scope spawn behavior for fleet visibility, while
preserving bindings like user when the sourceScopeId is a child entity
scope.

Tests

Added deadbugs.hm-platform-forward covering:

  • the docs hmPlatforms shape: active hmLinux forwards, inactive hmDarwin
    stays inert
  • an undeclared/unused source class forwarded into homeManager, which should
    be inert rather than crash

Both new regression tests fail on the test-only commit with spawnNode spawn root equals its parent scope and pass with this fix.

Validated locally:

  • just ci deadbugs.hm-platform-forward.test-doc-example-forwards-linux-only
  • just ci deadbugs.hm-platform-forward.test-unknown-source-class-to-home-manager-is-inert
  • just fmt
  • just ci: 1036/1036 successful

AI assistance

I used AI assistance while preparing this contribution:

  • ChatGPT 5.5 Thinking helped me understand the complex forward fallback path,
    analyze the dropped source scope entity binding, and notice the existing
    mkDrained binding reconstruction pattern.
  • Codex GPT-5.5 medium helped with contribution workflow, test strategy,
    fail/pass comparison, test naming/comments, and English wording.

I also used AI to translate and polish English because I am not fluent in
written English. I reviewed the final text, understand the code changes and PR
description, removed repetition, kept the diff minimal and focused, and ran the
final tests locally.

I Gyeongmin Song, explicitly acknowledge the following:

  • My contribution is AI generated and I mention which models/mcp-servers/tools were used.
  • I recognize AI generated contribution as my own and am myself legally accountable for it.
  • My contribution aligns with Den LICENSE and does not violate any other projects licenses (e.g. GPL)
  • I will truthfully answer and provide any requested details on how contribution was generated.

ZetabS added 2 commits July 2, 2026 20:24
Add a deadbug suite for the platform-specific `hmPlatforms` pattern from the
custom-classes guide and discussion denful#620.

The suite covers two fallback cases:
- a generated inactive platform source class (`hmDarwin`) while `hmLinux`
  forwards into `homeManager`
- an undeclared, unused source class forwarded into an evaluated
  `homeManager` target

Both cases should be inert instead of tripping the self-parent spawn guard.
Complex forward fallback rewalks from the parent scope so it keeps fleet
visibility. When the source scope is a child entity scope, that rewalk still
needs the source scope's own entity binding.

Previously `resolveSourceFallback` spawned with `bindings = { }`, so a
user-scoped forward with no collected source modules could lose its `user`
binding and collapse into the self-parent spawn guard.

Restore that binding from `scopeEntityKind` and `scopeContexts`, mirroring the
existing drained-spawn reconstruction path.
@ZetabS
ZetabS requested review from sini and vic as code owners July 2, 2026 12:00
@ZetabS

ZetabS commented Jul 2, 2026

Copy link
Copy Markdown
Contributor Author

Sorry, I forgot to add the ai-assisted tag.

Comment thread nix/lib/aspects/fx/edges/spawn-context.nix

@sini sini left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This looks solid enough to me.

@sini

sini commented Jul 2, 2026

Copy link
Copy Markdown
Collaborator

I'll merge it once CI passes. Thank you for your contribution! :)

@sini sini added allow-ci allow all CI integration tests ai-assisted labels Jul 2, 2026
@sini
sini enabled auto-merge (squash) July 2, 2026 16:50
@sini
sini merged commit 1614f6f into denful:main Jul 2, 2026
43 of 59 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

ai-assisted allow-ci allow all CI integration tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants