Skip to content

[feat] roadmap 13 batch H - flow v2: per-object graphs (H1/H5/H2/H3) - #36

Merged
AlexZ005 merged 9 commits into
mainfrom
feature/roadmap-13-flow-v2
Jul 21, 2026
Merged

[feat] roadmap 13 batch H - flow v2: per-object graphs (H1/H5/H2/H3)#36
AlexZ005 merged 9 commits into
mainfrom
feature/roadmap-13-flow-v2

Conversation

@AlexZ005

Copy link
Copy Markdown
Collaborator

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

  • flowGraphs store (keyed scene | objectUuid) is the source of truth; the legacy flowNodes/flowEdges become the ACTIVE graph's editor view (mirror lives in the leaf flowStore.jshistory.js imports flowRuntime, so the mirror can't sit behind a history-importing module).
  • Replicated lifecycle: graphcreate/graphdelete + a flowgraph history kind (undo of delete restores the whole document and re-replicates it). Graph-tagged edit messages (graphId on nodecreate/move/data/delete/edge*, absent = scene → legacy compat). Full-state nodes message carries a graphs map; hash/drift-heal cover every graph.
  • Runtime + physics iterate ALL graphs; inside an object flow, effect/physics/sound/onclick nodes target the OWNER implicitly (no Object Selector needed; explicit wiring wins).
  • Editor scope follows the viewport selection; scope chip + 🗑 delete-with-confirmation; empty state with one-click "Create flow". Peer cursors filter per graph.
  • Sessions/autosave persist the graphs map (old snapshots load as the scene graph); orphan graphs prune at serialization only, so undoing an object delete finds its flow intact.

H5 — object flows as scene-graph nodes

  • Flow Input / Flow Output interface nodes DECLARE a flow's public sockets (name + type). An Object Flow node 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).
  • Add paths: palette picker (lists only objects WITH flows) + object context menu "Add flow to Scene graph". Embed-once per graph v1.
  • Determinism: interface renames/deletes prune stale embed edges on every peer (interface-signature watcher + snapshot post-pass, the customNodes precedent); deleting a flow removes its embeds on local + applier paths.

H2 — api.registerNodeDefs

  • Modules ship code-editable node defs (NodeDesigner-editable, replicated like user defs, id mod-<moduleId>-<key>); absent-only seeding so user edits survive reloads. hello module ships the worked example (Bobble).

H3 — Key Press trigger node

  • Local keydown → replicated trigger pulse (button-module pattern; peers compute the same pulse from the shared timestamp); held keys re-stamp so the output stays high; press-a-key capture UI. Reached inputRuntime via a primed dynamic import — the static edge closed a history → flowRuntime → inputRuntime → shortcuts → history TDZ cycle that broke the SSR prerender.

Verification

  • New suites (all pass): 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).
  • Regressions green: 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 build green; svelte-check 499/77 (under the 501/77 baseline).

Companions landed outside this PR: theprototype.app-modules repo with the installable flow-toolkit example (registerNodeDefs demo), and public-docs pages (Object Flows guide, Key Press node, registerNodeDefs in modules.md).

🤖 Generated with Claude Code

AlexZ005 and others added 9 commits July 21, 2026 23:38
- 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
AlexZ005 merged commit 4d91493 into main Jul 21, 2026
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>
@AlexZ005
AlexZ005 deleted the feature/roadmap-13-flow-v2 branch August 5, 2026 20:24
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