Skip to content

fix: resolve params once per target, not per edge, in the transitive dependency walk - #22

Open
neilverc wants to merge 1 commit into
mainfrom
deps/root-wins-and-hard-error-on-param-conflicts
Open

fix: resolve params once per target, not per edge, in the transitive dependency walk#22
neilverc wants to merge 1 commit into
mainfrom
deps/root-wins-and-hard-error-on-param-conflicts

Conversation

@neilverc

Copy link
Copy Markdown
Collaborator

Problem

PLAT-8321/#20 added the full transitive dependency walk; #21 fixed ctx.consumer to always be the render root. Neither implemented PLAT-8324's decided policy for a target reached via multiple edges: applyDependencies fired a target's dependent hooks once per incoming edge, unconditionally, with that edge's own unmerged params — so a target declared both directly by the render root and transitively through another dependency fired twice, with two different params, instead of the root's declaration winning.

Resolution

Per PLAT-8324: visit each (kind,name) target exactly once for hook application (not once per edge). applyDependencies now runs two passes:

  1. Walk the graph (unchanged BFS shape) collecting every (declarer, target) edge, without applying hooks.
  2. For each distinct target, resolveDependencyParams picks a single params object:
    • The render root's own direct declaration wins outright when present — no transitive path needs to agree with it.
    • Otherwise every indirect edge into the target must agree; disagreement with no direct declaration to arbitrate is a hard render-time error, not silent last-write-wins.
  3. Apply the target's dependent hooks exactly once with the resolved params.

Tests

Replaces TestDiamondDependencyFiresTargetHookOncePerIncomingEdge (asserted the old double-fire behavior) with:

  • TestDiamondDependencyWithAgreeingIndirectParamsAppliesHookOnce — two indirect paths agreeing on params fire once.
  • TestConflictingIndirectDependencyParamsIsHardError — two indirect paths disagreeing, no direct declaration → hard error.
  • TestRootDirectDependencyOverridesConflictingTransitiveParams — root declares a target directly and reaches it transitively with different params → root's params win, hook fires once, indirect params never take effect.

Existing TestMultiHopDependencyAppliesTransitiveDependentHooks" and TestDependencyCycleAppliesEachEdgeOnceWithoutInfiniteLoop` are unaffected (each target there has exactly one edge).

SPEC.md's render-time execution section documents the new params resolution policy.

…transitive dependency walk

PLAT-8324 decided the policy for a target reached by multiple edges in
veil's transitive dependency walk (PLAT-8321): the render root's own
direct declaration wins over any transitive path, and two indirect
paths disagreeing on params with no direct declaration to arbitrate is
a hard render-time error. PR #20/#21 shipped the BFS walk and
per-hop-consumer mechanics but never implemented this policy —
applyDependencies fired a target's dependent hooks once per incoming
edge, unconditionally, with that edge's own (unmerged) params.

applyDependencies now runs in two passes: first it walks the graph
collecting every (declarer, target) edge without applying hooks, then
for each distinct target it resolves a single params object via
resolveDependencyParams (root's direct edges win when present;
otherwise every indirect edge must agree, or it's a hard error) and
applies that target's dependent hooks exactly once.

Replaces TestDiamondDependencyFiresTargetHookOncePerIncomingEdge (which
asserted the old double-fire behavior) with three tests covering the
new policy: agreeing indirect params fire once, conflicting indirect
params hard-error, and a root-direct declaration overrides a
conflicting transitive one. Updates SPEC.md's render-time execution
section accordingly.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant