The graph is built from nodes and edges with structure driven by persisted relationships.
Current taxonomy has exactly 5 species.
| Level | Species | Role | React Flow node type |
|---|---|---|---|
| 1 | flow |
Ordered sequence container | flow |
| 0 | view |
Reusable page/screen | view |
| — | data-model |
Data entity/table | dataModel |
| — | api-endpoint |
API endpoint | apiEndpoint |
| — | acceptance |
A testable promise: What (title), How (metadata.gherkin, one Given/When/Then), Why (metadata.values, Bain elements), with per-platform status in metadata.platformStatuses. Id prefix AC-. |
acceptance |
Config source: lib/config/species.ts
- The canvas is the Journey map (vision.md § Core Product, spec/maps.md): it renders
flowandviewspecies as React Flow nodes over the full compose closure from the root — views chain the traversal onward, flows render as collapsed, expandable cards. data-modelandapi-endpointremain persisted graph species, editable from panels and import/export. They render as standalone cards on the System map (/project/[id]/maps/system); on the Journey map they surface inline on View cards via embedded API actions and in the detail panel's Connections section.- Cross-layer edges (
calls,displays,queries) draw whenever both endpoints are visible.
Source: components/maps/JourneyMap.tsx, lib/utils/journey-graph.ts, components/graph/nodes/ViewNode.tsx
Project library is available at /project/[id]/library with two browsing modes:
- Gallery: card grid with title, prefixed ID, species/status badges, platforms, and flow playlist preview.
- Directory: sortable table for
id,title,species,status, andused inflow count.
Filtering:
- Species selection is owned by the sidebar (
?species=deep links:allwhen absent, or one offlow,view,data-model,api-endpoint,acceptance); the in-page bar carries search and the display-mode toggle only. - Search matches node title and description text.
Library source:
- app/project/[id]/library/page.tsx
- components/library/LibraryFilterBar.tsx
- components/library/NodeCard.tsx
- components/library/NodeTable.tsx
- Acceptances (
/project/[id]/acceptances) — the parity matrix: acceptances grouped by the view/flow they cover, one status column per platform, filterable by platform/status/value/anchor/parity-gap. Editing (Gherkin, values, per-platform status) happens in the node detail panel.
- Pyramid (
/project/[id]/pyramid) — the value-element gauge grid: four Bain tier sections (functional → emotional → life-changing → social impact), each rendering its value elements as cards showing the element's icon + label + per-platform delivery gauge + acceptance count. A platform chip row filters the gauges to a single platform (or all), and each element card links to the Acceptances matrix pre-filtered on that value (?value=<id>).
project.root_node_idis the explicit canvas anchor when present.- If
root_node_idis not set, the canvas infers roots from nodes with no incomingcomposesedge.
Source: components/maps/JourneyMap.tsx, lib/utils/journey-graph.ts, lib/data/types.ts
- Persisted parent/child links are
composesedges. - Child ordering is read from
node.metadata.playlist.entries. - When playlist entries do not reference all compose-edge children, missing children are appended after playlist-derived ordering.
Source: components/maps/JourneyMap.tsx, lib/utils/journey-graph.ts
The canvas walks the compose closure from the anchor (project.root_node_id, or inferred roots when absent): views are always rendered and chain the walk onward; flows are rendered as collapsed cards whose interiors (playlists) render only when expanded. Flows start collapsed, except that the first top-level flow (a flow reached without passing through another flow) auto-expands on initial load. Top-level expansion is accordion-style: opening one top-level flow collapses any other top-level flow already open.
Expanded flow children follow a strict alternating drill layout:
- Root children are rendered horizontally below the root.
- Level 2 children are rendered vertically below each level 1 node.
- Level 3 children are rendered horizontally below each level 2 node.
- The pattern continues alternating by depth.
- Vertical drill segments always use top/bottom handles for both
flowandviewnodes.
Layout is computed by elkjs (Eclipse Layout Kernel, layered algorithm). The page builds a flat list of nodes and compose edges with position: {x:0, y:0}, then an async useEffect calls computeElkLayout() which runs the ELK layered algorithm and returns positioned nodes.
Layout source: lib/utils/elk-layout.ts
Source: components/maps/JourneyMap.tsx, lib/utils/journey-graph.ts
viewandflownodes are reusable and can appear multiple times across playlists.- Reuse is many-to-many: a single node can be referenced by many flow playlists, and one flow playlist can reference many nodes.
- The source of truth for sequence semantics is the playlist (
metadata.playlist.entries), whilecomposesedges provide structural connectivity. - The detail panel's where-used UI derives reverse references by scanning all flow playlists.
Source: components/panels/NodeDetailPanel.tsx, lib/utils/where-used.ts, lib/data/types.ts
flow nodes store ordered playlist data in node.metadata.playlist.entries.
| Entry Type | Required Fields | Notes |
|---|---|---|
view |
view_id |
Reference to an existing view node |
flow |
flow_id |
Reference to an existing flow node (cycle-checked before persist) |
condition |
label, if_true, if_false |
Two branch lists, each a recursive PlaylistEntry[] |
junction |
label, cases[] |
Each case has label + entries: PlaylistEntry[] |
Editing source: components/panels/PlaylistEditor.tsx, components/panels/PlaylistEntryRow.tsx
Type source: lib/data/types.ts
Statuses are configured in:
Rollup behavior:
acceptanceis the primary carrier of stored per-platform status values (metadata.platformStatuses).viewalso storesmetadata.platformStatuses, but this is now a deprecated fallback: it is authoritative only while no acceptance covers the view (seecoversunder Edge Types).flowstatus is computed for display by recursively walking playlist entries and aggregating descendant view platform statuses, including nested sub-flows and branch entries.data-modelandapi-endpointuse single lifecycle status.
Sources:
- lib/utils/platform-status.ts
- components/panels/NodeDetailPanel.tsx
- components/maps/JourneyMap.tsx, lib/utils/journey-graph.ts
Platforms are configured in:
Views can target one or more platforms; per-platform notes/statuses are stored in node metadata.
Source:
| Edge Type | Use |
|---|---|
composes |
Composition hierarchy and ordered flow sequences |
calls |
View/flow to API relationship, or API endpoint to API endpoint (first-party endpoint fanning out to internal/external APIs) |
displays |
View to data-model relationship |
queries |
API to data-model relationship |
covers |
Acceptance to view/flow relationship — anchors a testable promise to the surface(s) it covers |
Config source: lib/config/edge-types.ts
Rendering mapping source: lib/utils/journey-graph.ts, lib/utils/system-graph.ts
calls edges between a view and API endpoint are projected into View card UI:
- API -> View: inbound/read affordance (
cloud-downloadicon) - View -> API: outbound/write affordance (
cloud-uploadicon)
calls edges between two API endpoints (a first-party endpoint fanning out to
internal/external APIs) have no view endpoint to project onto, so they surface only
on the System map (/project/[id]/maps/system), drawn between the two endpoint cards.
Source: components/maps/JourneyMap.tsx, lib/utils/journey-graph.ts, components/graph/nodes/ViewNode.tsx
covers — acceptance → view | flow. Zero covers edges = product-level
acceptance (legal, not an orphan). Stored per-platform status lives on
acceptances; a covered view's per-platform status is computed from its
covering acceptances, falling back to the view's stored platformStatuses
when uncovered (spec §3.4).
Source: packages/schema/src/acceptance.ts (projections; not yet consumed by
the app's status rollups — see the Surfaces plan).
Node registration is in:
Current custom registrations:
flow->FlowNodeview->ViewNodedataModel->DataModelNodeapiEndpoint->ApiEndpointNode
dataModel and apiEndpoint remain registered node types for compatibility, but the current project page renderer does not add those species into visibleNodes.
acceptance nodes and covers edges have no custom Canvas registration yet — they render with generic node/edge components, and the map-kind defaults exclude them (see the dated note under the Taxonomy Update Checklist).
Edge registration is also in components/graph/Canvas.tsx.
- Update config array in
lib/config/*. - Update graph builders and rendering filters in lib/utils/journey-graph.ts and lib/utils/system-graph.ts.
- Update Canvas registrations in components/graph/Canvas.tsx.
- Update forms/panels that branch by species.
- Update seed data in seed/pebbles.json.
- Update this document.
2026-07-19 — acceptance/covers: steps 3–4 deferred to the Surfaces plan
(docs/superpowers/specs/2026-07-19-acceptance-value-model-design.md §9);
maps exclude acceptances by default. Step 1 is done (lib/config/species.ts,
lib/config/edge-types.ts); step 2 needed no
change — journey and system map kinds' species defaults in
packages/schema/src/maps.ts exclude
acceptance, so lib/utils/journey-graph.ts and lib/utils/system-graph.ts
have no acceptance-specific branches. lib/utils/graph-build.ts already maps
acceptance to React Flow node type "acceptance" as a forward-fix
placeholder for when Canvas registration lands. Step 5 is done (seed).