New feature: the node-editor header’s pencil/trash were replaced with a
ProjectScreen-style avatar add-child menu; picking a type must create a
child of the edited node and open the new node’s editor. The
implementation plan’s createChildAndEdit did
create(child); selectNode(child.id); executeCommand(Expand) and
asserted (in a comment) that create() “optimistically adds the child to
the local store so the selection resolves immediately.” It does not — so
on first composition the new node would not be available and the editor
would render blank.
Two plan-vs-reality gaps, both caught only by holistic/code review (not by the per-task TDD, which tested the handler logic, not the runtime dispatch path):
ClientNodeManager.create() → submit() →
scope.launch { ... update() }. The local-store insert
(nodes.getOrPut) runs inside a coroutine, so it is NOT synchronously
visible after create() returns. KrillScreen’s Expand branch
gates the editor render on nodeManager.nodeAvailable(child.id), which
would be false on the composition triggered by the command flip.NodeChildren.load() returns pseudo-types. It mixes real child
KrillApp types with MenuCommand.{Update,Delete,KeepBuildingSwarm}.
ProjectScreen tolerates this because it routes through
executeCommand, which has an isMenuOption() branch; the new
createChildAndEdit does not, so a “Update”/”Delete” menu item would
have been passed to buildChildNode, building a nonsensical node.create(child) (async server submit + parent-as-source default) then
nodeManager.editing(child) — editing() → update() →
nodes.getOrPut is synchronous, so nodeAvailable(child.id) is true
before the Expand command flips and KrillScreen recomposes.
KrillScreen gates on availability only (not NodeState.EDITING), so
the later async update(NONE) from create() is harmless.nodeChildren.load(n).filterNot { it.isMenuOption() }
so the editor avatar only offers real creatable child types. Parent-
as-source needs no client code: buildChildNode yields empty
sources, so the server’s existing withParentSourceDefault wires
the parent on the create round-trip.Same class as feedback_verify_dispatch_entry_point /
2026-05-18-activity-pulse-wrong-stream: when a plan asserts a runtime
property (“create() makes the node immediately available”), verify it
against the actual dispatch path before relying on it — scope.launch
inside a “create/submit/update” chain almost always means not
synchronous. For Krill specifically: ClientNodeManager.create/submit is
async; editing()/update() is the synchronous local-store seam, and
KrillScreen editor routes gate on nodeAvailable(id). Also: any caller
that consumes NodeChildren.load() and does NOT route through
ScreenCore.executeCommand must filterNot { it.isMenuOption() } —
load() deliberately returns MenuCommand pseudo-types for the
executeCommand menu contract.