[feat] roadmap 13 batch H - flow v2: per-object graphs (H1/H5/H2/H3) - #36
Merged
Conversation
- flowGraphs store (keyed 'scene' | objectUuid) is the source of truth; the legacy flowNodes/flowEdges stores become the ACTIVE graph's editor view, kept in sync by a mirror in flowStore.js (leaf module - history.js imports flowRuntime, so the mirror cannot live behind a module that imports history) - flowGraphs.js: createObjectGraph/deleteObjectGraph (replicated graphcreate/ graphdelete + 'flowgraph' history kind - undo of delete restores the whole document and re-replicates it), requestDeleteObjectGraph confirmation flow, serializeGraphs with orphan pruning AT SERIALIZATION ONLY (undoing an object delete finds its flow intact) - nodesHandler: every applier takes the target graph (missing graphId = scene, legacy compat); full-state 'nodes' message carries a graphs map + legacy scene fields; graphHash/nodesync/sendNodes cover every graph - flowRuntime + physics collectParams iterate ALL graphs; an effect/physics/ sound/onclick node inside an object graph with no Object Selector wired implicitly targets the graph's OWNER object (explicit wiring still wins); C2 live angvel/motor re-apply now subscribes flowGraphs - editor: scope follows the viewport selection (scene when nothing selected); scope chip + delete-flow button; no-flow objects show an empty state with one-click Create flow (palette adds also auto-create); peer cursors filter to the graph being viewed; all editor broadcasts tag graphId - sessions/autosave persist the full graphs map (legacy nodes/edges kept for old builds; old snapshots load as the scene graph); stash clears all graphs - cross-graph readers fixed: pathCapture, sceneAssets, Explorer live-script, button module trigger lookup, Inspector physics rows - e2e: flow-object-graphs (17 checks incl. two-peer replication + undo restore, all pass); regression: button, flow-nodes-core, flow-physics-nodes all pass; path-node's patrol-drift failure reproduces on MAIN (pre-existing machine flake, not H1); build green; svelte-check 501/77 held Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Flow Input / Flow Output interface nodes DECLARE an object flow's public sockets (name + type; number/boolean/vector3/color). Inside the graph a Flow Input is a value source (fed by the scene, else its fallback) and a Flow Output surfaces its wired value. - Object Flow node embeds a flow into the SCENE graph with the declared sockets: wired scene values inject into the flow's Flow Inputs each tick (same tick); Flow Output values are harvested at tick end and read by the scene on the NEXT tick (one frame of latency, by design). Multi-output values travel as a __handles map unwrapped by sourceHandle in input()/ resolveInputs. - Two entry points: the node palette/pane menu (Object Flow group, picker lists only objects WITH flows) and the object context menu "Add flow to Scene graph" (embed-once per graph v1; drag-from-object-list deferred). - Determinism invariants: renaming/retyping/deleting interface nodes prunes stale embed edges on EVERY peer (pure function of graph state - watcher on the interface signature + the applyNodesSnapshot post-pass, the customNodes precedent); deleting a flow removes its embedded nodes + edges on both the local and applier paths. - Typed sockets: Socket.svelte gains forceType (data-declared types); isValidFlowConnection resolves flowinput vtype / embed handles from the referenced graph; flow outputs accept any value type. - e2e: flow-object-embed (7 checks, all pass - incl. the scene->flow->scene value round-trip and prune-on-rename); flow-object-graphs regression all pass; build green; svelte-check 501/77 held. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- H2 api.registerNodeDefs: modules ship CODE-EDITABLE node definitions. Each def lands in the replicated customNodeDefs store (NodeDesigner-editable, palette Custom section, late-joiner handshake sync for free) with the id mod-<moduleId>-<key>; seeding is ABSENT-ONLY so a user-edited def is never clobbered by a module reload. hello module ships the worked example (Bobble node, mod-hello-bobble). - H3 Key Press node (Triggers group): fires a REPLICATED trigger pulse on a local keydown of its captured event.code (button-module pattern - local input never streams; peers compute the same pulse from the shared synced timestamp); held keys re-stamp before the pulse expires so the output stays 1 while held (bounded ~3/s). Press-a-key capture UI on the node; text fields are already filtered by inputRuntime. - TDZ cycle avoided: flowRuntime reaches inputRuntime via a PRIMED dynamic import - a static edge closes history -> flowRuntime -> inputRuntime -> shortcuts -> history (broke the SSR prerender with "Cannot access kindHandlers before initialization"); comment documents the loop. - e2e: flow-input-moddefs (6 checks, all pass - def seeded/editable/drives its H1 graph owner; keypress press/hold/release); build green; svelte-check 499/77 (under the 501/77 baseline). Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…flow - scope-follows-selection now reads the selectedObjects SET (deselectObject clears only the set; selectedObject keeps the last object on purpose for the inspector/outline - the editor stayed stuck on the object flow) - new Scene chip button in the flow editor: switches back to the Scene flow AND deselects the current object (deselectObject) - e2e: flow-object-graphs extended to 20 checks (real deselect path + the chip button, all pass); build green; svelte-check 499/77 held Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…snapshot - root cause (user-reported): the autosave scene round-trips through GLTF and GLTFLoader assigns NEW uuids on parse - the restored graph stayed keyed by the ORIGINAL uuid, so the object came back flowless (annotations share the same uuid keying and orphaned too). Verified with a failing e2e first. - fix: exportScene stamps every object's uuid into userData.__uuid (GLTF extras round-trip it; markers stripped after export), and restoreSnapshot re-assigns the original uuids before adding children / re-broadcasting - peers receive the same uuids the graphs are keyed by. - e2e: autosave-object-flows (6 checks: save -> reload -> restore offer -> restore -> box keeps its uuid, its flow has its node, no empty state); flow-object-graphs regression all pass; build green; svelte-check 499/77. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…ick opens the flow - the embed card now renders ONE labeled row per declared socket (inputs left, outputs right, names truncated) with each handle anchored to its row ON the card edge - previous absolute offsets anchored to the padded content box and drifted from the labels; the card stretches with the interface - double-clicking the card selects the owner object, which opens its flow in the editor (the H1 scope-follows-selection rule); hint line on the card - e2e: flow-object-embed extended to 8 checks (real dblclick, all pass); build green; svelte-check 499/77 held; layout verified by screenshot Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
…et, wired readouts - FlowIONode + KeyPressNode fields now stack in ONE column (NodeWrapper's slot is a flex ROW - three sibling labels squeezed side-by-side and the name field showed three letters) - Flow Input: changing the declared type RESETS the fallback to a typed default and the fallback editor matches the type (number input / checkbox / color swatch / x-y-z fields) - Flow Output input socket (and Object Flow output sockets) are the neutral gray 'any' type - they accept any VALUE; only effect wires are blocked (effects are animations, not values - the docs example now says to output the driving Time value, not the Pulse) - wired params show the LIVE incoming value instead of their slider (Animation + Custom node cards): a flowValues lookup - the runtime already publishes value-node outputs ~6/s for card readouts, so this renders for free with no new evaluation - e2e: flow-object-embed 10 checks + flow-input-moddefs 7 checks (typed fallback reset, wired readout replaces the slider) all pass; build green; svelte-check 499/77 held; layouts verified by screenshots Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- a NAMED value input holds at most ONE wire: connecting a new one detaches the previous edge (replicated edgedelete) - the runtime only reads the first edge anyway, so extra wires were dead weight. Blender/UE semantics. - multi fan-IN survives where it is meaningful: the effect channel (many animations into one Object Selector) and event inputs (many triggers into one Counter); fan-OUT from any output stays unlimited (unchanged, by design) - flowSockets gains resolvedInputType (static table + the H5 data-declared cases) and replaceableInputEdges (pure, used by the editor edge-create) - the Object Flow picker never offers the flow it lives in: self-embed cannot hang (per-tick cycle guards + one-frame output latency bound everything) but a self-feedback card is pure confusion; embedding OTHER flows inside an object flow stays allowed (composition chains work) - e2e: flow-object-embed extended to 14 checks (replace/multi-fan-in rules + picker exclusion, all pass); build green; svelte-check 499/77 held Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
AlexZ005
added a commit
that referenced
this pull request
Jul 22, 2026
…itions, modules repo - CLAUDE.md map rewritten for flow v2: flowGraphs source-of-truth + editor-view mirror (and WHY it lives in the leaf store), flowGraphs.js/objectFlow.js libs, graph-routed nodesHandler, implicit-owner runtime rule, embed inject/harvest, resolvedInputType/replaceableInputEdges socket rules - new gotchas: the history.js TDZ-cycle family (flowRuntime imports must not reach history/shortcuts statically), NodeWrapper flex-ROW slot + padded-box handle anchoring, autosave GLTF restore re-uuid (userData.__uuid stamp), deselect clears only the selectedObjects SET - Module SDK section: registerNodeDefs + pointerRay (drag recipe) + the theprototype-app/modules repo (flow-toolkit, untangle) - status 2026-07-22: PR #36 + #37 merged, 190 untangle shipped as-built, svelte-check baseline now 499/77, remaining H4 + open PRs #33/#34 - e2e skill: npx vite dev (npm eats --port flags), __stores additions (flowGraphs/flowGraphsCtl/objectFlow/pointerRayNow/applyModuleMessage), repo-external module verification recipe (manager zip install) - peer-feature skill: per-object graph replication rules (graphId, allNodes, prune invariants, flowgraph history kind, single-connection inputs) + SDK additions + untangle as the worked game example Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Roadmap #13 — Batch H: flow v2, true per-object graphs (H1, H5, H2, H3)
The flow architecture rework. One commit per phase; H4 (car as nodes) follows in-lane — it builds on these + batch C's car and is deliberately not in this PR.
H1 — true per-object graph documents
flowGraphsstore (keyedscene| objectUuid) is the source of truth; the legacyflowNodes/flowEdgesbecome the ACTIVE graph's editor view (mirror lives in the leafflowStore.js—history.jsimportsflowRuntime, so the mirror can't sit behind a history-importing module).graphcreate/graphdelete+ aflowgraphhistory kind (undo of delete restores the whole document and re-replicates it). Graph-tagged edit messages (graphIdon nodecreate/move/data/delete/edge*, absent = scene → legacy compat). Full-statenodesmessage carries agraphsmap; hash/drift-heal cover every graph.H5 — object flows as scene-graph nodes
Flow Input/Flow Outputinterface nodes DECLARE a flow's public sockets (name + type). AnObject Flownode embeds the flow in the Scene graph: scene values inject into Flow Inputs (same tick), Flow Output values surface back (next tick — one frame latency by design).H2 —
api.registerNodeDefsmod-<moduleId>-<key>); absent-only seeding so user edits survive reloads. hello module ships the worked example (Bobble).H3 — Key Press trigger node
history → flowRuntime → inputRuntime → shortcuts → historyTDZ cycle that broke the SSR prerender.Verification
flow-object-graphs(17 checks incl. two-peer replication, undo-restores-content),flow-object-embed(7 checks incl. the scene→flow→scene value round-trip, prune-on-rename),flow-input-moddefs(6 checks).button,flow-nodes-core,flow-physics-nodes(two-peer).path-node's patrol-drift failure reproduces on main (pre-existing machine-saturation flake, verified).npm run buildgreen; svelte-check 499/77 (under the 501/77 baseline).Companions landed outside this PR:
theprototype.app-modulesrepo with the installableflow-toolkitexample (registerNodeDefs demo), and public-docs pages (Object Flows guide, Key Press node, registerNodeDefs in modules.md).🤖 Generated with Claude Code