Skip to content

fix: collapse agreeing definitions in the route merge (#674) - #675

Merged
sini merged 2 commits into
mainfrom
fix/674-identical-values-merge
Sep 4, 2026
Merged

sini merged 2 commits into
mainfrom
fix/674-identical-values-merge

Conversation

@sini

@sini sini commented Sep 4, 2026

Copy link
Copy Markdown
Collaborator

Closes #674

Summary

mergeableType — the freeform type route nesting evaluates modules under — deep-merges attrsets, concatenates lists, and throws on everything else. Derivations are deliberately excluded from the attrset branch so they stay opaque, so two aspects producing the same package fell to the conflict throw even though the definitions agreed. Same for the reported enable = true shape, where deep-merging descends to a scalar leaf with two definitions.

  • Accepts all-equal definitions before the conflict throw, matching NixOS's mergeEqualOption. Checked last, so it forces values only on the path that already threw.

#574 introduced the type claiming NixOS module semantics, but mergeEqualOption accepts agreeing definitions and only throws on disagreement — so the divergence is latent from #574, and #671 made the path reachable, which is the reporter's own reading of the bisect. This is the same collapse-agreeing-definitions rule #671 applied to meta.provider, now on the route freeform type.

Testing:

  • Adds the reported case verbatim (two aspects, one shared derivation), the reported symptom shape (a scalar leaf reached by deep-merging an apps attrset defined by two aspects), and a negative control pinning that disagreeing definitions still throw.

Validation

  • nix develop -c just ci: 1118/1118, exit 0, zero failures and zero errors. nix develop -c just fmt a no-op at the final rev.
  • Reproduced first on an unfixed origin/main worktree at 36c8ba5: the issue's test verbatim fails with den: the option 'shared' has conflicting definitions from multiple aspects, so the fix is measured against a red baseline rather than a green assumption.
  • Probed the negative control rather than trusting its pass. Forcing the value without tryEval prints that same conflict error, so it fails for the guard's reason and not incidentally.
  • Confirmed the new suite is discovered by the CI harness with just ci deadbugs-issue-674 (3/3). The background just ci log truncates to 42 lines, so the suite's absence from that log is not evidence either way and was not read as such.

Known limitation

The equality check cannot reach function-valued leaves. Measured against this nixpkgs:

expression result
(x: x) == (x: x) false
let f = x: x; in f == f false
{ a = 1; f = x: x; } == { a = 1; f = x: x; } false

A lambda never compares equal, not even to itself, and one lambda anywhere in an attrset makes the whole comparison false. So two aspects delivering the same function at one option still conflict, and no ==-level rule can fix that — it needs identity carried alongside the value. Derivations are unaffected because Nix short-circuits their comparison on outPath, which is why the reported case is fully fixable here.

`mergeableType` deep-merges attrsets and concatenates lists, and threw on
everything else — including definitions that all held the same value.
Derivations are deliberately held opaque so they never reach the attrset
branch, which left two aspects producing the same package in conflict.

#574 introduced the type claiming NixOS module semantics, but NixOS's
`mergeEqualOption` accepts agreeing definitions and only throws on
disagreement. Accepts all-equal definitions before the conflict throw,
checked last so it forces values only on the path that already threw.

Lambdas never compare equal in Nix, so function-valued leaves keep the
existing conflict behaviour.
@sini
sini requested a review from vic as a code owner September 4, 2026 20:51
@github-actions github-actions Bot added the allow-ci allow all CI integration tests label Sep 4, 2026
`builtins.readFile` on the merged package built an `x86_64-linux`
derivation, which fails the macos-latest runner on platform mismatch. The
line came verbatim from the issue's repro; the oracle never needed the
build, only the forcing of the merged option.

Reads `.name` instead. Falsified against the pre-fix tree: it still fails
with `den: the option 'shared' has conflicting definitions`, and the
disagreeing-values control stays green.
@sini
sini merged commit d50f0fc into main Sep 4, 2026
15 checks passed
@sini
sini deleted the fix/674-identical-values-merge branch September 4, 2026 21:34
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

allow-ci allow all CI integration tests

Projects

None yet

Development

Successfully merging this pull request may close these issues.

BUG (regression?): Identical values from multiple aspects conflict

1 participant