@principal-ai/subsystems-react 0.41.0 → 0.43.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. package/dist/index.d.ts +2 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +1 -1
  4. package/dist/index.js.map +1 -1
  5. package/dist/stories/Subsystem/C4Graph/c4Fixture.js +1 -1
  6. package/dist/stories/Subsystem/C4Graph/c4Fixture.js.map +1 -1
  7. package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts +6 -9
  8. package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
  9. package/dist/stories/Subsystem/ComponentGraph/fixtures.js +15 -50
  10. package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
  11. package/dist/subsystem/C4Graph.d.ts +2 -3
  12. package/dist/subsystem/C4Graph.d.ts.map +1 -1
  13. package/dist/subsystem/C4Graph.js +2 -3
  14. package/dist/subsystem/C4Graph.js.map +1 -1
  15. package/dist/subsystem/IssueList.d.ts +5 -5
  16. package/dist/subsystem/IssueList.d.ts.map +1 -1
  17. package/dist/subsystem/IssueList.js +9 -18
  18. package/dist/subsystem/IssueList.js.map +1 -1
  19. package/dist/subsystem/SubsystemComponentGraph.d.ts +9 -12
  20. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
  21. package/dist/subsystem/SubsystemComponentGraph.js +42 -112
  22. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
  23. package/dist/subsystem/model.d.ts +18 -51
  24. package/dist/subsystem/model.d.ts.map +1 -1
  25. package/dist/subsystem/model.js +2 -51
  26. package/dist/subsystem/model.js.map +1 -1
  27. package/dist/subsystem/toC4.d.ts +4 -4
  28. package/dist/subsystem/toC4.d.ts.map +1 -1
  29. package/dist/subsystem/toC4.js +3 -7
  30. package/dist/subsystem/toC4.js.map +1 -1
  31. package/package.json +3 -3
  32. package/src/index.ts +0 -4
  33. package/src/stories/Subsystem/AgentsPanel.stories.tsx +3 -5
  34. package/src/stories/Subsystem/C4Graph/C4Container.stories.tsx +1 -4
  35. package/src/stories/Subsystem/C4Graph/c4Fixture.ts +1 -1
  36. package/src/stories/Subsystem/ComponentGraph/Appearance.stories.tsx +2 -4
  37. package/src/stories/Subsystem/ComponentGraph/Basics.stories.tsx +8 -8
  38. package/src/stories/Subsystem/ComponentGraph/Captures.stories.tsx +19 -19
  39. package/src/stories/Subsystem/ComponentGraph/Constructs.stories.tsx +0 -1
  40. package/src/stories/Subsystem/ComponentGraph/CustomEntities.stories.tsx +3 -3
  41. package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +0 -2
  42. package/src/stories/Subsystem/ComponentGraph/Diagnostics.stories.tsx +2 -6
  43. package/src/stories/Subsystem/ComponentGraph/EdgeViews.stories.tsx +21 -21
  44. package/src/stories/Subsystem/ComponentGraph/Flows.stories.tsx +0 -3
  45. package/src/stories/Subsystem/ComponentGraph/FrameworkStereotype.stories.tsx +30 -32
  46. package/src/stories/Subsystem/ComponentGraph/GraphTitle.stories.tsx +4 -4
  47. package/src/stories/Subsystem/ComponentGraph/IssueOverlay.stories.tsx +1 -26
  48. package/src/stories/Subsystem/ComponentGraph/Issues.stories.tsx +3 -25
  49. package/src/stories/Subsystem/ComponentGraph/ModuleBadges.stories.tsx +7 -8
  50. package/src/stories/Subsystem/ComponentGraph/Modules.stories.tsx +9 -11
  51. package/src/stories/Subsystem/ComponentGraph/Packages.stories.tsx +3 -6
  52. package/src/stories/Subsystem/ComponentGraph/Processes.stories.tsx +3 -3
  53. package/src/stories/Subsystem/ComponentGraph/Proposed.stories.tsx +8 -6
  54. package/src/stories/Subsystem/ComponentGraph/ProposedWalkthroughs.stories.tsx +0 -1
  55. package/src/stories/Subsystem/ComponentGraph/Reorder.stories.tsx +0 -1
  56. package/src/stories/Subsystem/ComponentGraph/Scenarios.stories.tsx +26 -28
  57. package/src/stories/Subsystem/ComponentGraph/WalkthroughAutoplay.stories.tsx +0 -1
  58. package/src/stories/Subsystem/ComponentGraph/WorkspacePackageFrames.stories.tsx +2 -3
  59. package/src/stories/Subsystem/ComponentGraph/fixtures.ts +18 -58
  60. package/src/stories/Subsystem/EgoGraph/EgoGraph.stories.tsx +0 -2
  61. package/src/subsystem/C4Graph.tsx +2 -3
  62. package/src/subsystem/IssueList.tsx +12 -22
  63. package/src/subsystem/SubsystemComponentGraph.tsx +55 -136
  64. package/src/subsystem/model.test.ts +37 -24
  65. package/src/subsystem/model.ts +18 -101
  66. package/src/subsystem/toC4.test.ts +5 -12
  67. package/src/subsystem/toC4.ts +5 -9
@@ -4,7 +4,7 @@ import type { Meta, StoryObj } from '@storybook/react';
4
4
  import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
5
5
  import { SubsystemComponentGraph } from '../../../subsystem/SubsystemComponentGraph';
6
6
  import { SubsystemIssueList, type SubsystemIssue } from '../../../subsystem/IssueList';
7
- import { components, graphSpecFromEdges } from './fixtures';
7
+ import { components, graphSpecFromHops } from './fixtures';
8
8
 
9
9
  const meta = {
10
10
  title: 'Subsystem/ComponentGraph/Issues',
@@ -47,13 +47,6 @@ const ISSUES: SubsystemIssue[] = [
47
47
  message: 'Named parameter types are not present in the graphify cache.',
48
48
  target: { kind: 'component', id: 'entry', label: 'checkoutApi' },
49
49
  },
50
- {
51
- id: 'i4',
52
- severity: 'error',
53
- kind: 'topology_broken_endpoint',
54
- message: 'Relationship endpoint "store" does not resolve to a component.',
55
- target: { kind: 'relation', id: 'r1', label: 'checkoutApi → cartStore', detail: 'writes' },
56
- },
57
50
  {
58
51
  id: 'i5',
59
52
  severity: 'info',
@@ -132,21 +125,7 @@ const ISSUES: SubsystemIssue[] = [
132
125
  message: 'Graphify cache missing for pkg:github/you/your-app — build it to verify constructs and anchors',
133
126
  target: { kind: 'repo', id: 'pkg:github/you/your-app', label: 'pkg:github/you/your-app' },
134
127
  },
135
- {
136
- id: 'i14',
137
- severity: 'info',
138
- kind: 'topology_import_unconfirmed',
139
- message: 'No import edge from checkoutApi to cartStore found in Graphify.',
140
- target: { kind: 'relation', id: 'r1', label: 'checkoutApi → cartStore', detail: 'references' },
141
- },
142
- {
143
- id: 'i15',
144
- severity: 'info',
145
- kind: 'topology_relation_unconfirmed',
146
- message: 'Graphify found no inheritance evidence for FileSessionStore → SessionStore.',
147
- target: { kind: 'relation', id: 'r2', label: 'FileSessionStore → SessionStore', detail: 'extends' },
148
- },
149
- ];
128
+ ];
150
129
 
151
130
  const graphComponents = components([
152
131
  ['entry', 'checkoutApi', 'function', 'src/checkout/api.ts', 'pkg:github/you/your-app', 'Handles cart requests.', 'checkoutApi'],
@@ -154,7 +133,7 @@ const graphComponents = components([
154
133
  ['stripe', 'Stripe', 'external', '', 'external', undefined, undefined],
155
134
  ]);
156
135
 
157
- const graphEdges = graphSpecFromEdges([
136
+ const graphEdges = graphSpecFromHops([
158
137
  ['entry', 'store', 'writes'],
159
138
  ['entry', 'stripe', 'calls'],
160
139
  ]);
@@ -187,7 +166,6 @@ export const GraphWithIssues: Story = {
187
166
  <div style={{ width: '100%', height: '100vh' }}>
188
167
  <SubsystemComponentGraph
189
168
  components={graphComponents}
190
- relations={graphEdges.relations}
191
169
  walkthroughs={graphEdges.walkthroughs}
192
170
  title="Checkout"
193
171
  description="A small e-commerce checkout subsystem."
@@ -9,7 +9,7 @@ import type {
9
9
  SubsystemGraphNode,
10
10
  SubsystemProcessRegion,
11
11
  } from '../../../subsystem/model';
12
- import { graphSpecFromEdges } from './fixtures';
12
+ import { graphSpecFromHops } from './fixtures';
13
13
 
14
14
  const meta = {
15
15
  title: 'Subsystem/ComponentGraph/ModuleBadges',
@@ -102,12 +102,12 @@ const deepComponents: SubsystemComponent[] = [
102
102
  },
103
103
  ];
104
104
 
105
- const deepEdges = graphSpecFromEdges([
106
- ['node-comp', 'group-comp', 'method'],
107
- ['edge-comp', 'group-comp', 'method'],
108
- ['model-view', 'layers', 'method'],
109
- ['use-drawings', 'model-view', 'method'],
110
- ['model-view', 'node-comp', 'method'],
105
+ const deepEdges = graphSpecFromHops([
106
+ ['node-comp', 'group-comp', 'calls'],
107
+ ['edge-comp', 'group-comp', 'calls'],
108
+ ['model-view', 'layers', 'calls'],
109
+ ['use-drawings', 'model-view', 'calls'],
110
+ ['model-view', 'node-comp', 'calls'],
111
111
  ]);
112
112
 
113
113
  /** Collapsed module badges show `first/…/last`. Click one to expand the full
@@ -122,7 +122,6 @@ export const DeepModulePaths: Story = {
122
122
  'click the badge to expand the full path (the badge widens past the frame edge — the frame itself stays put), click again to collapse it back.'
123
123
  }
124
124
  components={deepComponents}
125
- relations={deepEdges.relations}
126
125
  walkthroughs={deepEdges.walkthroughs}
127
126
  />
128
127
  </div>
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
4
4
  import { SubsystemComponentGraph } from '../../../subsystem/SubsystemComponentGraph';
5
5
  import type { SubsystemComponent } from '../../../subsystem/model';
6
- import { graphSpecFromEdges } from './fixtures';
6
+ import { graphSpecFromHops } from './fixtures';
7
7
 
8
8
  const meta = {
9
9
  title: 'Subsystem/ComponentGraph/Modules',
@@ -120,10 +120,10 @@ const moduleComponents: SubsystemComponent[] = [
120
120
  },
121
121
  ];
122
122
 
123
- const moduleEdges = graphSpecFromEdges([
124
- ['parse', 'record', 'method'],
125
- ['is-rollout', 'record', 'method'],
126
- ['reader', 'normalize', 'method'],
123
+ const moduleEdges = graphSpecFromHops([
124
+ ['parse', 'record', 'uses'],
125
+ ['is-rollout', 'record', 'uses'],
126
+ ['reader', 'normalize', 'calls'],
127
127
  ['normalize', 'parse', 'calls'],
128
128
  ['normalize', 'tool-name', 'calls'],
129
129
  ['normalize', 'file-path', 'calls'],
@@ -137,7 +137,6 @@ export const ModuleBoundaries: Story = {
137
137
  title="Module boundaries"
138
138
  description="Concrete exports keep their real construct. Shared `module` (the source path) draws the file as a dashed frame — not `construct: module`. Singleton modules (registerAgent) stay unframed."
139
139
  components={moduleComponents}
140
- relations={moduleEdges.relations}
141
140
  walkthroughs={moduleEdges.walkthroughs}
142
141
  />
143
142
  </div>
@@ -260,10 +259,10 @@ const nestedComponents: SubsystemComponent[] = [
260
259
  },
261
260
  ];
262
261
 
263
- const nestedEdges = graphSpecFromEdges([
264
- ['boot', 'write-session', 'method'],
265
- ['trail-view', 'bridge', 'method'],
266
- ['trail-view', 'use-trail', 'method'],
262
+ const nestedEdges = graphSpecFromHops([
263
+ ['boot', 'write-session', 'calls'],
264
+ ['trail-view', 'bridge', 'calls'],
265
+ ['trail-view', 'use-trail', 'uses'],
267
266
  ['boot', 'create-host', 'calls'],
268
267
  ['create-host', 'write-session', 'calls'],
269
268
  ['write-session', 'session-store', 'writes'],
@@ -279,7 +278,6 @@ export const ProcessAndModuleNesting: Story = {
279
278
  title="Process × module nesting"
280
279
  description="process frames wrap module frames wrap exports. Leaves parent to module:; module groups parent to process: when every member shares that process."
281
280
  components={nestedComponents}
282
- relations={nestedEdges.relations}
283
281
  walkthroughs={nestedEdges.walkthroughs}
284
282
  />
285
283
  </div>
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
4
4
  import { SubsystemComponentGraph } from '../../../subsystem/SubsystemComponentGraph';
5
5
  import type { SubsystemComponent } from '../../../subsystem/model';
6
- import { graphSpecFromEdges } from './fixtures';
6
+ import { graphSpecFromHops } from './fixtures';
7
7
 
8
8
  const meta = {
9
9
  title: 'Subsystem/ComponentGraph/Packages',
@@ -80,7 +80,7 @@ const multiRepoComponents: SubsystemComponent[] = [
80
80
  },
81
81
  ];
82
82
 
83
- const multiRepoEdges = graphSpecFromEdges([
83
+ const multiRepoEdges = graphSpecFromHops([
84
84
  ['dispatch', 'session', 'calls'],
85
85
  ['dispatch', 'parse', 'calls'],
86
86
  ['parse', 'normalize', 'calls'],
@@ -90,7 +90,6 @@ export const MultiRepoPackageFrames: Story = {
90
90
  name: 'Multi-repo package frames',
91
91
  args: {
92
92
  components: multiRepoComponents,
93
- relations: multiRepoEdges.relations,
94
93
  walkthroughs: multiRepoEdges.walkthroughs,
95
94
  },
96
95
  render: (args) => (
@@ -107,7 +106,6 @@ export const SingleRepoNoPackageFrames: Story = {
107
106
  name: 'Single-repo (no package frames)',
108
107
  args: {
109
108
  components: singleRepo,
110
- relations: multiRepoEdges.relations,
111
109
  walkthroughs: multiRepoEdges.walkthroughs,
112
110
  },
113
111
  render: (args) => (
@@ -220,7 +218,7 @@ const nestedComponents: SubsystemComponent[] = [
220
218
  },
221
219
  ];
222
220
 
223
- const nestedEdges = graphSpecFromEdges([
221
+ const nestedEdges = graphSpecFromHops([
224
222
  ['boot', 'dispatch', 'calls'],
225
223
  ['dispatch', 'session', 'calls'],
226
224
  ['dispatch', 'parse', 'calls'],
@@ -231,7 +229,6 @@ export const PackageProcessModuleNesting: Story = {
231
229
  name: 'Package → process → module nesting',
232
230
  args: {
233
231
  components: nestedComponents,
234
- relations: nestedEdges.relations,
235
232
  walkthroughs: nestedEdges.walkthroughs,
236
233
  },
237
234
  render: (args) => (
@@ -2,7 +2,7 @@ import '@xyflow/react/dist/style.css';
2
2
  import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
4
4
  import { SubsystemComponentGraph } from '../../../subsystem/SubsystemComponentGraph';
5
- import { components, graphSpecFromEdges, relations } from './fixtures';
5
+ import { components, graphSpecFromHops } from './fixtures';
6
6
 
7
7
  const meta = {
8
8
  title: 'Subsystem/ComponentGraph/Processes',
@@ -54,7 +54,7 @@ const processComponents = [
54
54
  },
55
55
  ];
56
56
 
57
- const processEdges = graphSpecFromEdges([
57
+ const processEdges = graphSpecFromHops([
58
58
  ['main', 'store', 'writes'],
59
59
  ['main', 'bridge', 'calls'],
60
60
  ['bridge', 'view', 'feeds'],
@@ -69,7 +69,7 @@ export const ProcessBoundaries: Story = {
69
69
  title="Process boundaries"
70
70
  description="Host and renderer each render in their own ELK-aware boundary frame. The worker process holds a single node and still gets a frame (singleton frames default on). The telemetry service has no `process` and sits outside every boundary."
71
71
  components={processComponents}
72
- relations={processEdges.relations} walkthroughs={processEdges.walkthroughs}
72
+ walkthroughs={processEdges.walkthroughs}
73
73
  />
74
74
  </div>
75
75
  ),
@@ -7,9 +7,9 @@ import { SubsystemComponentGraph } from '../../../subsystem/SubsystemComponentGr
7
7
  import { SubsystemComponentNode } from '../../../subsystem/nodes';
8
8
  import type {
9
9
  SubsystemComponent,
10
- SubsystemComponentEdge,
11
10
  SubsystemGraphNode,
12
11
  } from '../../../subsystem/model';
12
+ import { walkthroughFromHops } from './fixtures';
13
13
 
14
14
  const meta = {
15
15
  title: 'Subsystem/ComponentGraph/Proposed',
@@ -225,10 +225,12 @@ const migrationComponents: SubsystemComponent[] = [
225
225
  },
226
226
  ];
227
227
 
228
- const migrationEdges: SubsystemComponentEdge[] = [
229
- { id: 'e0', from: 'audit', to: 'maintain', mechanism: 'calls' },
230
- { id: 'e1', from: 'maintain', to: 'lifecycle', mechanism: 'uses' },
231
- { id: 'e2', from: 'lifecycle', to: 'cli', mechanism: 'calls' },
228
+ const migrationWalkthroughs = [
229
+ walkthroughFromHops('migration-hops', 'Migration hops', [
230
+ ['audit', 'maintain', 'calls', 'src/bun/verify-subsystem-component.ts', 1],
231
+ ['maintain', 'lifecycle', 'uses', 'src/bun/maintain-model.ts', 1],
232
+ ['lifecycle', 'cli', 'calls', 'src/bun/opencode-v2.ts', 1],
233
+ ]),
232
234
  ];
233
235
 
234
236
  function MigrationDemo() {
@@ -237,7 +239,7 @@ function MigrationDemo() {
237
239
  <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
238
240
  <SubsystemComponentGraph
239
241
  components={migrationComponents}
240
- relations={migrationEdges.relations} walkthroughs={migrationEdges.walkthroughs}
242
+ walkthroughs={migrationWalkthroughs}
241
243
  onSelect={(id) => setSelected(id)}
242
244
  />
243
245
  <div style={{ marginTop: 8, padding: '0 12px', fontFamily: 'monospace', fontSize: 12, color: '#aaa' }}>
@@ -151,7 +151,6 @@ function ProposedWalkthroughsDemo() {
151
151
  <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
152
152
  <SubsystemComponentGraph
153
153
  components={components}
154
- relations={[]}
155
154
  walkthroughs={walkthroughs}
156
155
  title="proposed-work marker"
157
156
  description="Walkthroughs that touch a **proposed** component get their title tinted darkgoldenrod in the flows panel; expand a row and each hop onto a proposed component is tinted too (index and title). Nodes for proposed components already draw dashed darkgoldenrod on the canvas."
@@ -137,7 +137,6 @@ function ReorderGraphDemo() {
137
137
  <div style={{ flex: 1, minHeight: 0 }}>
138
138
  <SubsystemComponentGraph
139
139
  components={components}
140
- relations={[]}
141
140
  walkthroughs={walkthroughs}
142
141
  onReorderWalkthroughs={onReorder}
143
142
  title="drawing-files flows"
@@ -9,7 +9,7 @@ import type {
9
9
  SubsystemModelDocument,
10
10
  } from '../../../subsystem/model';
11
11
  import type { GraphifyComponentDetail } from '../../../graphify';
12
- import { components, graphSpecFromEdges, relations } from './fixtures';
12
+ import { components, graphSpecFromHops } from './fixtures';
13
13
 
14
14
  const meta = {
15
15
  title: 'Subsystem/ComponentGraph/Scenarios',
@@ -92,11 +92,11 @@ const mermaidComponents: SubsystemComponent[] = [
92
92
  },
93
93
  ];
94
94
 
95
- const mermaidEdges = graphSpecFromEdges([
95
+ const mermaidEdges = graphSpecFromHops([
96
96
  ['input', 'slide', 'feeds'],
97
97
  ['slide', 'chunk', 'produces'],
98
98
  ['chunk', 'lazy', 'feeds'],
99
- ['lazy', 'diagram', 'wraps'],
99
+ ['lazy', 'diagram', 'uses'],
100
100
  ['diagram', 'helpers', 'uses'],
101
101
  ]);
102
102
 
@@ -106,7 +106,7 @@ function MermaidDemo() {
106
106
  <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
107
107
  <SubsystemComponentGraph
108
108
  components={mermaidComponents}
109
- relations={mermaidEdges.relations} walkthroughs={mermaidEdges.walkthroughs}
109
+ walkthroughs={mermaidEdges.walkthroughs}
110
110
  onSelect={(id) => setSelected(id)}
111
111
  />
112
112
  <div style={{ marginTop: 8, fontFamily: 'monospace', fontSize: 12, color: '#aaa' }}>
@@ -137,10 +137,10 @@ const multiRepoComponents = components([
137
137
  ['rewire', 'extract.py', 'function', 'graphify/extract.py', graphifyPurl, 'corpus pass folding unique-label stubs onto definitions', '_rewire_unique_stub_nodes'],
138
138
  ]);
139
139
 
140
- const multiRepoEdges = graphSpecFromEdges([
141
- ['detail', 'reftypes', 'method'],
142
- ['resolver', 'reftypes', 'method'],
143
- ['resolver', 'engine', 'method'],
140
+ const multiRepoEdges = graphSpecFromHops([
141
+ ['detail', 'reftypes', 'uses'],
142
+ ['resolver', 'reftypes', 'uses'],
143
+ ['resolver', 'engine', 'calls'],
144
144
  ['rewire', 'resolver', 'calls'],
145
145
  ]);
146
146
 
@@ -151,7 +151,7 @@ function MultiRepoDemo() {
151
151
  title="Type-ref resolution across repos"
152
152
  description="Two repos → two file trees. Each tree is scoped to its repo's files and headed by the owner avatar + repo name. Cross-repo edges land on external stubs."
153
153
  components={multiRepoComponents}
154
- relations={multiRepoEdges.relations} walkthroughs={multiRepoEdges.walkthroughs}
154
+ walkthroughs={multiRepoEdges.walkthroughs}
155
155
  />
156
156
  </div>
157
157
  );
@@ -354,7 +354,7 @@ const accessSurfaceComponents: SubsystemComponent[] = [
354
354
  },
355
355
  ];
356
356
 
357
- const accessSurfaceEdges = graphSpecFromEdges([
357
+ const accessSurfaceEdges = graphSpecFromHops([
358
358
  ['agents', 'http-entry', 'calls'],
359
359
  ['http-entry', 'create', 'calls'],
360
360
  ['http-entry', 'update', 'calls'],
@@ -384,7 +384,7 @@ function AccessSurfacesDemo() {
384
384
  title="Access surfaces, roles, and process boundaries"
385
385
  description="Two process regions (host, renderer) + boundary entries; agents and the external service float outside every boundary. Hover for the role badge; click the store to drill into its state-only detail."
386
386
  components={accessSurfaceComponents}
387
- relations={accessSurfaceEdges.relations} walkthroughs={accessSurfaceEdges.walkthroughs}
387
+ walkthroughs={accessSurfaceEdges.walkthroughs}
388
388
  onSelect={(id) => setSelected(id)}
389
389
  onEdgeSelect={(e) => setSelectedEdge(e)}
390
390
  />
@@ -534,7 +534,7 @@ const storeFlavorComponents: SubsystemComponent[] = [
534
534
  },
535
535
  ];
536
536
 
537
- const storeFlavorEdges = graphSpecFromEdges([
537
+ const storeFlavorEdges = graphSpecFromHops([
538
538
  ['f1-create', 'f1-store', 'writes'],
539
539
  ['f1-get', 'f1-store', 'reads'],
540
540
  ['f2-cache', 'f2-store', 'writes'],
@@ -549,7 +549,7 @@ function StoreFlavorsDemo() {
549
549
  title="Store flavors"
550
550
  description="construct:= the node's verifiable anchor. Class-managed stores are TWO nodes: the manager class (declaration, methods) + the state (store block). Click any node to see its anchor-honest drill-down."
551
551
  components={storeFlavorComponents}
552
- relations={storeFlavorEdges.relations} walkthroughs={storeFlavorEdges.walkthroughs}
552
+ walkthroughs={storeFlavorEdges.walkthroughs}
553
553
  onSelect={(id) => setSelected(id)}
554
554
  />
555
555
  <div style={{ marginTop: 8, fontFamily: 'monospace', fontSize: 12, color: '#aaa' }}>
@@ -637,7 +637,7 @@ const sharedStoreComponents: SubsystemComponent[] = [
637
637
  },
638
638
  ];
639
639
 
640
- const sharedStoreEdges = graphSpecFromEdges([
640
+ const sharedStoreEdges = graphSpecFromHops([
641
641
  ['ss-load', 'ss-store', 'reads'],
642
642
  ['ss-save', 'ss-store', 'writes'],
643
643
  ['ss-watch', 'ss-store', 'watches'],
@@ -652,7 +652,7 @@ function SharedStoreDemo() {
652
652
  title="Shared store across processes"
653
653
  description="A store with no process sits outside every boundary; accessors from both processes reach across to it."
654
654
  components={sharedStoreComponents}
655
- relations={sharedStoreEdges.relations} walkthroughs={sharedStoreEdges.walkthroughs}
655
+ walkthroughs={sharedStoreEdges.walkthroughs}
656
656
  onEdgeSelect={(e) => setSelectedEdge(e)}
657
657
  />
658
658
  <div style={{ marginTop: 8, fontFamily: 'monospace', fontSize: 12, color: '#aaa' }}>
@@ -919,7 +919,7 @@ const storeSpectrumComponents: SubsystemComponent[] = [
919
919
  },
920
920
  ];
921
921
 
922
- const storeSpectrumEdges = graphSpecFromEdges([
922
+ const storeSpectrumEdges = graphSpecFromHops([
923
923
  ['load', 'db', 'reads'],
924
924
  ['create', 'graph-store', 'writes'],
925
925
  ['cache', 'cache-state', 'writes'],
@@ -938,7 +938,7 @@ function StoreStateSpectrumDemo() {
938
938
  title="State → Store spectrum, all modeled as store"
939
939
  description="Every holder here claims construct: 'store' (state-block anatomy). They differ only in what they hold and how long it lives. Which ones do you call a store? Where is the cut?"
940
940
  components={storeSpectrumComponents}
941
- relations={storeSpectrumEdges.relations} walkthroughs={storeSpectrumEdges.walkthroughs}
941
+ walkthroughs={storeSpectrumEdges.walkthroughs}
942
942
  onSelect={(id) => setSelected(id)}
943
943
  />
944
944
  <div style={{ marginTop: 8, fontFamily: 'monospace', fontSize: 12, color: '#aaa' }}>
@@ -1114,12 +1114,12 @@ const typeSpectrumComponents: SubsystemComponent[] = [
1114
1114
  },
1115
1115
  ];
1116
1116
 
1117
- const typeSpectrumEdges = graphSpecFromEdges([
1117
+ const typeSpectrumEdges = graphSpecFromHops([
1118
1118
  ['listener', 'subscriber', 'uses'],
1119
- ['subscriber', 'messages', 'method'],
1119
+ ['subscriber', 'messages', 'uses'],
1120
1120
  ['state', 'listener', 'feeds'],
1121
- ['deep', 'record', 'method'],
1122
- ['rows', 'record', 'method'],
1121
+ ['deep', 'record', 'uses'],
1122
+ ['rows', 'record', 'uses'],
1123
1123
  ]);
1124
1124
 
1125
1125
  function TypeFamilySpectrumDemo() {
@@ -1130,7 +1130,7 @@ function TypeFamilySpectrumDemo() {
1130
1130
  title="Type-family — what a type can hold"
1131
1131
  description="Structured buckets: callable signature (generics + params), interface properties, enum members, union, plain alias reference. `rhs` = verbatim escape hatch for shapes that don't fit (mapped/conditional/template-literal). Click each to see the declaration."
1132
1132
  components={typeSpectrumComponents}
1133
- relations={typeSpectrumEdges.relations} walkthroughs={typeSpectrumEdges.walkthroughs}
1133
+ walkthroughs={typeSpectrumEdges.walkthroughs}
1134
1134
  onSelect={(id) => setSelected(id)}
1135
1135
  />
1136
1136
  <div style={{ marginTop: 8, fontFamily: 'monospace', fontSize: 12, color: '#aaa' }}>
@@ -1209,7 +1209,7 @@ const queueComponents: SubsystemComponent[] = [
1209
1209
  },
1210
1210
  ];
1211
1211
 
1212
- const queueEdges = graphSpecFromEdges([
1212
+ const queueEdges = graphSpecFromHops([
1213
1213
  ['q-producer', 'q-queue', 'writes'],
1214
1214
  ['q-worker', 'q-queue', 'reads'],
1215
1215
  ['q-worker', 'q-results', 'writes'],
@@ -1223,7 +1223,7 @@ function QueueAsStoreDemo() {
1223
1223
  title="Queue as store"
1224
1224
  description="Ordered retained state. Stores as pipeline stages: producer writes the queue, the worker reads it and writes results."
1225
1225
  components={queueComponents}
1226
- relations={queueEdges.relations} walkthroughs={queueEdges.walkthroughs}
1226
+ walkthroughs={queueEdges.walkthroughs}
1227
1227
  onEdgeSelect={(e) => setSelectedEdge(e)}
1228
1228
  />
1229
1229
  <div style={{ marginTop: 8, fontFamily: 'monospace', fontSize: 12, color: '#aaa' }}>
@@ -1343,7 +1343,6 @@ const dataVizDoc: SubsystemModelDocument = {
1343
1343
  },
1344
1344
  },
1345
1345
  ],
1346
- relations: [],
1347
1346
  walkthroughs: [
1348
1347
  {
1349
1348
  id: 'data-viz-hops',
@@ -1369,8 +1368,8 @@ function DataVsVisualizationDemo() {
1369
1368
  setText(next);
1370
1369
  try {
1371
1370
  const parsed = JSON.parse(next) as SubsystemModelDocument;
1372
- if (!Array.isArray(parsed.components) || !Array.isArray(parsed.relations)) {
1373
- throw new Error('document needs `components` and `relations` arrays');
1371
+ if (!Array.isArray(parsed.components)) {
1372
+ throw new Error('document needs a `components` array');
1374
1373
  }
1375
1374
  setDoc(parsed);
1376
1375
  setError(null);
@@ -1431,7 +1430,6 @@ function DataVsVisualizationDemo() {
1431
1430
  title="the visualization"
1432
1431
  description="Same document, rendered: construct:→ node anatomy, role → topology glyph, process → boundary region, mechanism → edge color/style."
1433
1432
  components={doc.components}
1434
- relations={doc.relations}
1435
1433
  walkthroughs={doc.walkthroughs}
1436
1434
  />
1437
1435
  </div>
@@ -197,7 +197,6 @@ export const AutoplayFocus: Story = {
197
197
  <SubsystemComponentGraph
198
198
  {...args}
199
199
  components={components}
200
- relations={[]}
201
200
  walkthroughs={walkthroughs}
202
201
  hideSidebar
203
202
  hideDrawer
@@ -3,7 +3,7 @@ import type { Meta, StoryObj } from '@storybook/react';
3
3
  import { ThemeProvider, defaultEditorTheme } from '@principal-ade/industry-theme';
4
4
  import { SubsystemComponentGraph } from '../../../subsystem/SubsystemComponentGraph';
5
5
  import type { SubsystemComponent } from '../../../subsystem/model';
6
- import { graphSpecFromEdges } from './fixtures';
6
+ import { graphSpecFromHops } from './fixtures';
7
7
 
8
8
  const meta = {
9
9
  title: 'Subsystem/ComponentGraph/WorkspacePackages',
@@ -114,7 +114,7 @@ const components: SubsystemComponent[] = [
114
114
  },
115
115
  ];
116
116
 
117
- const spec = graphSpecFromEdges([
117
+ const spec = graphSpecFromHops([
118
118
  ['read-file', 'get-model', 'calls'],
119
119
  ['model-view', 'component-graph', 'uses'],
120
120
  ['component-graph', 'file-drawer', 'calls'],
@@ -125,7 +125,6 @@ export const PackageOwnedProcess: Story = {
125
125
  name: 'Package-owned process · library outside',
126
126
  args: {
127
127
  components,
128
- relations: spec.relations,
129
128
  walkthroughs: spec.walkthroughs,
130
129
  graphTitle: 'Opening a file — app package owns the process',
131
130
  },
@@ -1,12 +1,9 @@
1
1
  import type {
2
2
  SubsystemComponent,
3
3
  SubsystemComponentEdge,
4
- SubsystemRelation,
5
- SubsystemRelationType,
6
4
  SubsystemWalkthrough,
7
5
  SubsystemWalkthroughMechanism,
8
6
  } from '../../../subsystem/model';
9
- import { derivedGraphEdgeId } from '../../../subsystem/model';
10
7
  import type { GraphifyComponentDetail } from '../../../graphify';
11
8
 
12
9
  // ---------------------------------------------------------------------------
@@ -27,26 +24,6 @@ export function components(
27
24
  }));
28
25
  }
29
26
 
30
- const RELATION_TYPES = new Set<string>([
31
- 'extends',
32
- 'inherits',
33
- 'implements',
34
- 'mixes_in',
35
- 'method',
36
- ]);
37
-
38
- export function relations(
39
- spec: Array<[from: string, to: string, relationType: SubsystemRelationType, refs?: string[]]>,
40
- ): SubsystemRelation[] {
41
- return spec.map(([from, to, relationType, refs], i) => ({
42
- id: `r${i}`,
43
- from,
44
- to,
45
- relationType,
46
- refs,
47
- }));
48
- }
49
-
50
27
  /** Story helper: one walkthrough whose hops derive Set B display edges. */
51
28
  export function walkthroughFromHops(
52
29
  id: string,
@@ -68,48 +45,32 @@ export function walkthroughFromHops(
68
45
  };
69
46
  }
70
47
 
71
- /** @deprecated story helper — prefer `relations` + `walkthroughFromHops`. */
48
+ /** @deprecated story helper — prefer `graphSpecFromHops` + `walkthroughFromHops`. */
72
49
  export function edges(
73
- spec: Array<[from: string, to: string, mechanism: SubsystemComponentEdge['mechanism'], refs?: string[]]>,
50
+ spec: Array<[from: string, to: string, mechanism: SubsystemComponentEdge['mechanism']]>,
74
51
  ): SubsystemComponentEdge[] {
75
- return spec.map(([from, to, mechanism, refs], i) => ({
52
+ return spec.map(([from, to, mechanism], i) => ({
76
53
  id: `e${i}`,
77
54
  from,
78
55
  to,
79
56
  mechanism,
80
- refs,
81
57
  }));
82
58
  }
83
59
 
84
- /** Split mixed mechanism lists into relations + optional walkthrough for stories. */
85
- export function graphSpecFromEdges(
86
- spec: Array<[from: string, to: string, mechanism: SubsystemComponentEdge['mechanism'], refs?: string[]]>,
87
- ): { relations: SubsystemRelation[]; walkthroughs?: SubsystemWalkthrough[] } {
88
- const rels: SubsystemRelation[] = [];
89
- const hops: SubsystemWalkthrough['steps'] = [];
90
- for (const [from, to, mechanism, refs] of spec) {
91
- if (RELATION_TYPES.has(mechanism)) {
92
- rels.push({
93
- id: derivedGraphEdgeId(from, to, mechanism as SubsystemRelationType),
94
- from,
95
- to,
96
- relationType: mechanism as SubsystemRelationType,
97
- refs,
98
- });
99
- } else {
100
- hops.push({
101
- from,
102
- to,
103
- mechanism: mechanism as SubsystemWalkthroughMechanism,
104
- file: 'story-placeholder.ts',
105
- line: 1,
106
- purl: 'pkg:github/storybook/fixture#story-placeholder.ts',
107
- symbol: from,
108
- });
109
- }
110
- }
60
+ /** Turn every spec entry into a walkthrough hop — the sole authored edge form. */
61
+ export function graphSpecFromHops(
62
+ spec: Array<[from: string, to: string, mechanism: SubsystemComponentEdge['mechanism']]>,
63
+ ): { walkthroughs?: SubsystemWalkthrough[] } {
64
+ const hops: SubsystemWalkthrough['steps'] = spec.map(([from, to, mechanism]) => ({
65
+ from,
66
+ to,
67
+ mechanism: mechanism as SubsystemWalkthroughMechanism,
68
+ file: 'story-placeholder.ts',
69
+ line: 1,
70
+ purl: 'pkg:github/storybook/fixture#story-placeholder.ts',
71
+ symbol: from,
72
+ }));
111
73
  return {
112
- relations: rels,
113
74
  walkthroughs:
114
75
  hops.length > 0
115
76
  ? [{ id: 'story-hops', title: 'Story hops', steps: hops }]
@@ -192,17 +153,16 @@ export const investigateOnlyComponents: SubsystemComponent[] = [
192
153
  },
193
154
  ];
194
155
 
195
- const investigateSpec = graphSpecFromEdges([
156
+ const investigateSpec = graphSpecFromHops([
196
157
  ['v1', 'input', 'produces'],
197
158
  ['v2', 'input', 'produces'],
198
159
  ['input', 'acc', 'feeds'],
199
160
  ['acc', 'out', 'produces'],
200
161
  ]);
201
162
 
202
- export const investigateOnlyRelations = investigateSpec.relations;
203
163
  export const investigateOnlyWalkthroughs = investigateSpec.walkthroughs;
204
164
 
205
- /** @deprecated use investigateOnlyRelations + investigateOnlyWalkthroughs */
165
+ /** @deprecated use investigateOnlyWalkthroughs */
206
166
  export const investigateOnlyEdges: SubsystemComponentEdge[] = edges([
207
167
  ['v1', 'input', 'produces'],
208
168
  ['v2', 'input', 'produces'],
@@ -275,7 +275,6 @@ function EgoDemo({ category, orderByLine }: { category: Category; orderByLine: b
275
275
  <div style={{ width: '100%', height: '100vh', display: 'flex', flexDirection: 'column' }}>
276
276
  <SubsystemComponentGraph
277
277
  components={graph.components}
278
- relations={[]}
279
278
  graphifyRelations={graph.graphifyRelations}
280
279
  orderByLine={orderByLine}
281
280
  showSingletonFrames
@@ -345,7 +344,6 @@ export const AuthoredConstructs: Story = {
345
344
  <div style={{ width: '100%', height: '100vh' }}>
346
345
  <SubsystemComponentGraph
347
346
  components={authoredComponents}
348
- relations={[]}
349
347
  title="Authored-only constructs"
350
348
  description="store / external / custom_entity — reused cast from stored subsystem models (no graphify node, no edges)."
351
349
  />