Symptom

Every node editor was a single vertical scroll where type-specific fields, metadata, and the shared source-wiring widget were stacked together. The TargetingWiringEditor was appended mid/bottom of ~24 editors with no explanation of what sources or verbs (EXECUTE/RESET) mean, so users could not grok what a node does, how it observes sources, or what it does when fired.

Root cause

Not a bug — a design gap. Wiring UI was duplicated inline across every editor (each with its own meta.copy(sources = it) callbacks), so there was no single place to add explanation or restructure layout.

Fix

NodeEditorContainer now hosts a consistent 3-tab layout (Overview / Settings / Sources) with the header and Save/Cancel kept outside the tabs. The Overview/Settings split is done in one place, not per-editor: the router’s type when was extracted into a single NodeFace(node, viewMode) composable, and the Overview tab renders each type’s existing ROW face (its at-a-glance live state) while the Settings tab renders the existing EDIT/VIEW editor form (metadata editing). Reusing the per-type representations that already existed avoided bespoke field re-categorization across 24 editors and the regression risk that carried. The Sources tab is fully centralized in a new SourcesTab composable: it renders a fixed sources/verbs preamble plus per-type prose pulled from the existing KrillFeature JSON fields (llmPurpose, llmBehavior, description — no cross-repo schema change), then the shared TargetingWiringEditor for TargetingNodeMetaData nodes (or a “does-not-observe-sources” notice otherwise). Wiring writes go through one new generic helper, NodeMetaData.withWiring(...), mirroring the existing withParentSourceDefault per-subtype copy dispatch. The inline TargetingWiringEditor call was deleted from all 24 editors; a tab-less NodeEditorContainer(viewMode, content) overload was kept for node-less setup flows (Add Server / Connect Peer).

Prevention

Centralizing the wiring write path created a latent clobber risk: an editor’s local-meta LaunchedEffect persistence vs. the Sources tab’s edited() write. This is structurally prevented because the tab container composes only the selected tab, so the Overview editor and the Sources tab never coexist in composition — switching tabs re-seeds each editor’s remember from live state. Regression guards: NodeMetaDataWithWiringTest (round-trips all three wiring fields per family, preserves unrelated fields) and SourcesExplanationTest (pure buildSourcesExplanation mapping incl. the non-targeting branch). withWiring and withParentSourceDefault must stay in lockstep — both carry an explicit comment saying any new TargetingNodeMetaData subtype must be added to both when arms or its wiring silently becomes uneditable.