Skip to content

feat(studio): collapsible child cards — core-owned card fold (G1 R-B) - #396

Draft
Yona-Appletree wants to merge 2 commits into
claude/jovial-knuth-b627fcfrom
claude/collapsible-child-cards-r-b
Draft

feat(studio): collapsible child cards — core-owned card fold (G1 R-B)#396
Yona-Appletree wants to merge 2 commits into
claude/jovial-knuth-b627fcfrom
claude/collapsible-child-cards-r-b

Conversation

@Yona-Appletree

@Yona-Appletree Yona-Appletree commented Aug 8, 2026

Copy link
Copy Markdown
Collaborator

Stacked on #380 (claude/jovial-knuth-b627fc). Implements the R-B ruling from the module-authoring G1 gate (2026-08-07): collapsing child cards, scoped out of #380 but wanted "soon" now that pattern projects ship rigs (strip + matrix + outputs + effect module) and the child column runs long.

What changed

The fold moves into core. It was a web-local use_signal seeded from NodeControllerState.collapsed — a bit nothing in production ever wrote. So the fold died with the component instance (amnesia across re-renders and remounts), was unreachable by e2e, and the sim-lens subscription gate that read it carried a comment saying it "becomes real when the UI state audit moves live collapse state into core". This is that move.

  • NodeCardUiState gains collapsed (default false) and NodeUiOp gains SetCollapsed, keyed by node address path — the same convention as every drawer bit, so child cards inherit it through into_node_view for free.
  • NodeControllerState.collapsed and UiNodeView.collapsed retire: one source of truth, carried on card_ui. ui_nodes() now overlays the card-UI store exactly like editor_view() already did.
  • No new affordance. The rail is the existing Collapse node button from the root card's header (PaneCollapse / StudioPane) — every card already rendered through NodePane, so they all get it by construction.

The subscription consequence, verified. node_subscribes_products' sim arm now reads the real fold state — ancestors included. Collapsing a card folds its whole child column away, so a hidden card must not keep streaming just because its own bit is clear. Folded cards stop streaming previews, which is the desirable behaviour the ruling asked us to confirm. Explicit Subscribed/Unsubscribed intent still wins over the fold, and the device arm (focused-only) is untouched.

Selection is untouched. Collapsing never focuses, unfocuses, or otherwise moves the one-live-surface selection — asserted directly.

Tests

  • node_card_ui_state: default is expanded; SetCollapsed round-trips; the card fold is its own bit (expanding the card leaves drawers where the user left them); every op names its node.
  • collapsing_a_child_card_rides_the_seam_and_never_touches_the_selection: fold rides the action seam, is keyed per address, survives a tree resync, leaves selection alone.
  • collapsed_cards_stop_streaming_under_the_sim_lens: subscriptions drop on fold, return on expand, drop again when an ancestor folds, and explicit subscribe intent still wins.

Stories

child_grouping_folded — the grouped pattern column with everything but fire folded. Verified rendering locally: folded cards keep the full identity row (chevron, name, export chip, kind label, ⓘ), and both the sage exports header and the rig header still place each folded card, so the ships/stays-home split survives the fold. PanelWalk learned SetCollapsed, so the rail is genuinely clickable in the walkable stories rather than a static prop.

Baselines are not committed here — they are CI-canonical (validate-stories auto-commits; macOS rendering differs from the pinned CI environment).

Validation

just check testgreen, exit 0, 7270 tests passed, no clippy/fmt diffs. All four new tests confirmed executing by name.

One caveat worth recording: the first gate run reported success while its output ended in error: Recipe fmt-check failed, and a later run failed with what looked like dependency breakage (extern location for futures_core does not exist) that was actually build-cache corruption left by an out-of-disk build — the dev machine's volume filled up (3.6 TiB used, unrelated to this repo; worktree caches total only 11 GB). Cleared by wiping this workspace's cache shard and rebuilding builtins. The green run above is from that clean cache.

🤖 Generated with Claude Code

Yona-Appletree and others added 2 commits August 7, 2026 23:34
… SetCollapsed (G1 R-B)

The whole-card collapse was a web-local use_signal seeded from a
controller bit nothing ever wrote: amnesia across re-renders, invisible
to e2e, and the sim-lens subscription gate that read it was
documented as 'becomes real when the UI state audit moves live
collapse state into core'. This is that move.

- NodeCardUiState gains collapsed (default false) and NodeUiOp gains
  SetCollapsed, keyed by node address like every drawer bit.
- node_subscribes_products' sim arm reads the card-UI store, ancestors
  included: collapsing a card folds its whole child column away, so a
  hidden card must not keep streaming just because its own bit is
  clear. Explicit subscribe intent still wins; the device arm
  (focused-only) is untouched.
- NodeControllerState.collapsed and UiNodeView.collapsed retire —
  the DTO carries the fold in card_ui, and ui_nodes() now overlays
  the store exactly like editor_view().
- e2e: fold rides the action seam, survives a tree resync, never
  touches the selection; sim-lens subscriptions drop on fold (self
  and ancestor) and return on expand.

Co-Authored-By: Claude Fable 5 <[email protected]>
…tory

NodePane's collapse toggle now dispatches NodeUiOp::SetCollapsed
through the action seam instead of flipping a view-local signal — the
same wiring as every drawer, so the fold persists per node address,
child cards inherit it through into_node_view, and folding a card
stops its sim previews streaming.

PanelWalk learns the SetCollapsed op so the rail stays live in the
walkable stories (root card and children alike), and the grouped
pattern column gains a folded story: everything but fire collapsed to
its header, judging whether a mostly-folded column still reads as the
same grammar.

Co-Authored-By: Claude Fable 5 <[email protected]>
@Yona-Appletree
Yona-Appletree force-pushed the claude/collapsible-child-cards-r-b branch from 5084a70 to c321e25 Compare August 8, 2026 07:43
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