fix(central-ui): Flow Debugger spawn edges — real arrowhead + bolt badge; spawner picked by trigger attribute

The trigger-cascade (Spawn) edge used a lightning-bolt marker IN PLACE of the arrowhead. On the upward
Tags→Site hop the bolt sat on top of the riser and read as a broken arrow (rig screenshot 2026-09-08).
Every connector now ends in an ordinary arrowhead; the bolt is a badge (circle + bolt) centred on the
connector's vertical run (FlowGraphView.SpawnBadgePoint, kept in step with OrthogonalPath/AsyncPath).

Same screenshot, second defect: the cascade spawned by "RecipeDownloadFlag = false" was drawn from the
LAST write of the burst (RecipeProcessedFlag) because all four writes share a millisecond and the
spawner rule was time-only. FlowGraphBuilder now prefers the parent's last AttributeWrite of the attribute
the child's triggerAttribute names (at or before the child, else the last such write); the time/position
rules remain the fallback when the child names no attribute or the parent never wrote it.

Verified live on the rebuilt rig (DelmiaSim01 / DelmiaReceiverSimulator): both spawn edges show an
arrowhead + badge, the second one originates from the RecipeDownloadFlag write. bUnit + builder tests
updated/added; Playwright FlowDebuggerTests 4/4.

Claude-Session: https://claude.ai/code/session_01NcMG6xXdsJbmD2wYAgkK29
This commit is contained in:
Joseph Doherty
2026-09-08 09:04:39 -04:00
parent cb8c6eb813
commit 859922ba17
7 changed files with 174 additions and 18 deletions
@@ -17,10 +17,6 @@
<marker id="fg-arrow-accent" viewBox="0 0 10 10" refX="9" refY="5" markerWidth="8" markerHeight="8" orient="auto-start-reverse">
<path d="M 0 0 L 10 5 L 0 10 z" class="fg-arrowhead accent" />
</marker>
@* Trigger spawn (plan D9): a small bolt in place of the arrowhead — this write fired a value-change script. *@
<marker id="fg-arrow-spawn" viewBox="0 0 12 14" refX="10" refY="7" markerWidth="12" markerHeight="14" orient="auto-start-reverse">
<path d="M 5 1 l -4 6 h 5 l -3 6 l 7 -8 h -5 l 3 -4 z" class="fg-arrowhead spawn" />
</marker>
</defs>
@* Lanes *@
@@ -57,6 +53,17 @@
@* Names the branch where it lands: the run carried on without waiting for this. *@
@SvgText(tag.X, tag.Y, "fg-edge-tag", "async", dataTest: "flow-edge-tag")
}
@if (SpawnBadgeFor(e) is { } badge)
{
@* Trigger cascade (plan D9): a bolt badge on the connector itself — this write fired a value-change
script. The edge keeps an ordinary arrowhead; the bolt used to BE the arrowhead, and on the
upward Tags→Site hop it read as a broken arrow (rig, 2026-09-08). *@
<g class="fg-spawn-badge" transform="translate(@badge.X @badge.Y)" data-test="flow-edge-spawn">
<title>Trigger cascade: this write fired the script</title>
<circle r="8" class="fg-spawn-badge-bg" />
<path d="M -1 -6 l -4 6 h 5 l -3 6 l 7 -8 h -5 l 3 -4 z" class="fg-spawn-bolt" />
</g>
}
}
@* Nodes *@