@principal-ai/subsystems-react 0.42.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 +30 -110
  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 +38 -132
  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
@@ -48,13 +48,35 @@ const comps: SubsystemComponent[] = [
48
48
  { alias: 'transcript', name: 'transcript', construct: 'function', file: 'transcript.ts', purl: 'pkg:github/principal-ai/agent-monitoring' },
49
49
  ];
50
50
 
51
- const relations = [
52
- { id: 'e1', from: 'transcript', to: 'reader', relationType: 'method' as const },
53
- // 'host' is NOT a component — this is the cross-package external case.
54
- { id: 'e2', from: 'reader', to: 'host', relationType: 'method' as const, refs: ['bun/index.ts'] },
51
+ // 'host' is NOT a component — this is the cross-package external case.
52
+ const walkthroughs: SubsystemWalkthrough[] = [
53
+ {
54
+ id: 'wt',
55
+ title: 'flow',
56
+ steps: [
57
+ {
58
+ from: 'transcript',
59
+ to: 'reader',
60
+ mechanism: 'calls',
61
+ file: 'transcript.ts',
62
+ line: 1,
63
+ purl: 'pkg:github/principal-ai/agent-monitoring#transcript.ts',
64
+ symbol: 'transcript',
65
+ },
66
+ {
67
+ from: 'reader',
68
+ to: 'host',
69
+ mechanism: 'calls',
70
+ file: 'SessionReader.ts',
71
+ line: 1,
72
+ purl: 'pkg:github/principal-ai/agent-monitoring#SessionReader.ts',
73
+ symbol: 'SessionReader',
74
+ },
75
+ ],
76
+ },
55
77
  ];
56
78
 
57
- const doc = { components: comps, relations };
79
+ const doc = { components: comps, walkthroughs };
58
80
 
59
81
  describe('subsystem graph model', () => {
60
82
  test('converts all components to flat component nodes', () => {
@@ -85,7 +107,6 @@ describe('subsystem graph model', () => {
85
107
  ];
86
108
  const { nodes } = await buildSubsystemGraph({
87
109
  components: withExternal,
88
- relations: [],
89
110
  walkthroughs: [{
90
111
  id: 'w1',
91
112
  title: 'feed',
@@ -294,7 +315,6 @@ describe('subsystem graph model', () => {
294
315
  },
295
316
  { alias: 'd', name: 'd', construct: 'function', file: 'd.ts', purl: 'pkg:github/acme/app' },
296
317
  ],
297
- relations: [],
298
318
  });
299
319
  expect((nodes.find((n) => n.id === 'a') as { parentId?: string }).parentId).toBe(
300
320
  moduleGroupNodeId('src/a.ts'),
@@ -308,7 +328,6 @@ describe('subsystem graph model', () => {
308
328
  { alias: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
309
329
  { alias: 'd', name: 'd', construct: 'function', file: 'd.ts', purl: 'pkg:github/acme/app' },
310
330
  ],
311
- relations: [],
312
331
  });
313
332
  expect((nodes.find((n) => n.id === 'a') as { parentId?: string }).parentId).toBe(
314
333
  processGroupNodeId('app/host'),
@@ -404,7 +423,6 @@ describe('subsystem graph model', () => {
404
423
  symbol: 'SessionStore',
405
424
  },
406
425
  ],
407
- relations: [],
408
426
  });
409
427
  expect(regions.map((r) => r.key).sort()).toEqual(
410
428
  ['app/host', 'src/main.ts', 'src/store.ts'].sort(),
@@ -432,7 +450,6 @@ describe('subsystem graph model', () => {
432
450
  { alias: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
433
451
  { alias: 'solo', name: 'solo', construct: 'function', file: 's.ts', purl: 'pkg:github/acme/app', process: 'app/lonely' },
434
452
  ],
435
- relations: [],
436
453
  walkthroughs: [{
437
454
  id: 'w1',
438
455
  title: 'call',
@@ -566,7 +583,6 @@ describe('subsystem graph model', () => {
566
583
  symbol: 'lonely',
567
584
  },
568
585
  ],
569
- relations: [],
570
586
  });
571
587
  expect(regions.map((r) => r.key)).toEqual(['src/session/transcript.ts']);
572
588
  expect(regions[0]!.kind).toBe('module');
@@ -701,7 +717,6 @@ describe('subsystem graph model', () => {
701
717
  module: 'src/b.ts',
702
718
  },
703
719
  ],
704
- relations: [],
705
720
  });
706
721
  const pkgA = nodes.find((n) => n.id === packageGroupNodeId('pkg:github/acme/app'));
707
722
  const proc = nodes.find((n) => n.id === processGroupNodeId('app/host'));
@@ -791,7 +806,7 @@ describe('subsystem graph model', () => {
791
806
  }
792
807
  : c,
793
808
  ),
794
- relations,
809
+ walkthroughs,
795
810
  };
796
811
  expect(subsystemGraphLayoutKey(base)).toBe(subsystemGraphLayoutKey(withRef));
797
812
  });
@@ -799,7 +814,7 @@ describe('subsystem graph model', () => {
799
814
 
800
815
  describe('isConstructsOnlyModel', () => {
801
816
  test('true when components exist and there are no edges', () => {
802
- expect(isConstructsOnlyModel({ components: comps, relations: [], walkthroughs: [] })).toBe(true);
817
+ expect(isConstructsOnlyModel({ components: comps, walkthroughs: [] })).toBe(true);
803
818
  expect(isConstructsOnlyModel({ components: comps })).toBe(true);
804
819
  });
805
820
 
@@ -812,13 +827,12 @@ describe('isConstructsOnlyModel', () => {
812
827
  ).toBe(false);
813
828
  });
814
829
 
815
- test('false when empty, or when relations or walkthrough hops exist', () => {
816
- expect(isConstructsOnlyModel({ components: [], relations: [] })).toBe(false);
817
- expect(isConstructsOnlyModel({ components: comps, relations })).toBe(false);
830
+ test('false when empty, or when walkthrough hops exist', () => {
831
+ expect(isConstructsOnlyModel({ components: [] })).toBe(false);
832
+ expect(isConstructsOnlyModel({ components: comps, walkthroughs })).toBe(false);
818
833
  expect(
819
834
  isConstructsOnlyModel({
820
835
  components: comps,
821
- relations: [],
822
836
  walkthroughs: [
823
837
  {
824
838
  id: 'wt',
@@ -993,7 +1007,6 @@ describe('reorderTargetIndex', () => {
993
1007
  describe('graphify-native edges', () => {
994
1008
  const doc = {
995
1009
  components: comps,
996
- relations: [],
997
1010
  };
998
1011
  const graphify = [
999
1012
  { id: 'g1', from: 'reader', to: 'dst', relation: 'imports' },
@@ -1006,12 +1019,12 @@ describe('graphify-native edges', () => {
1006
1019
  expect(e.mechanism).toBe('imports');
1007
1020
  });
1008
1021
 
1009
- test('subsystem relations keep no graphify provenance', () => {
1022
+ test('walkthrough edges keep no graphify provenance', () => {
1010
1023
  const [e] = deriveGraphEdges({
1011
- relations: [{ id: 'r', from: 'reader', to: 'dst', relationType: 'method' }],
1024
+ walkthroughs: [walkthroughs[0]!],
1012
1025
  });
1013
1026
  expect(e.provenance).toBeUndefined();
1014
- expect(e.mechanism).toBe('method');
1027
+ expect(e.mechanism).toBe('calls');
1015
1028
  });
1016
1029
 
1017
1030
  test('graphify edges color from the separate palette', () => {
@@ -1019,7 +1032,7 @@ describe('graphify-native edges', () => {
1019
1032
  GRAPHIFY_RELATION_COLOR.imports,
1020
1033
  );
1021
1034
  expect(edgeColor({ mechanism: 'imports', provenance: 'graphify' })).not.toBe(
1022
- MECHANISM_COLOR.method,
1035
+ MECHANISM_COLOR.calls,
1023
1036
  );
1024
1037
  });
1025
1038
 
@@ -1030,7 +1043,7 @@ describe('graphify-native edges', () => {
1030
1043
  });
1031
1044
 
1032
1045
  test('subsystem mechanism color is unaffected by the graphify palette', () => {
1033
- expect(edgeColor({ mechanism: 'method' })).toBe(MECHANISM_COLOR.method);
1046
+ expect(edgeColor({ mechanism: 'calls' })).toBe(MECHANISM_COLOR.calls);
1034
1047
  });
1035
1048
 
1036
1049
  test('graphify edges all share the graphify stroke style', () => {
@@ -108,17 +108,6 @@ export interface SubsystemDeclToken {
108
108
  color?: string;
109
109
  }
110
110
 
111
- /**
112
- * Topology relation type — structural / module / type claims.
113
- * Belongs on `relations[]`, not on walkthrough hops.
114
- */
115
- export type SubsystemRelationType =
116
- | 'extends'
117
- | 'inherits'
118
- | 'implements'
119
- | 'mixes_in'
120
- | 'method';
121
-
122
111
  /**
123
112
  * Walkthrough hop mechanism — runtime seams with a `file:line` site.
124
113
  */
@@ -132,24 +121,21 @@ export type SubsystemWalkthroughMechanism =
132
121
  | 'watches'
133
122
  | 'registers-into';
134
123
 
135
- /** Union for derived graph-edge styling (relationType or hop mechanism). */
136
- export type SubsystemEdgeMechanism =
137
- | SubsystemRelationType
138
- | SubsystemWalkthroughMechanism;
124
+ /** Union for derived graph-edge styling — the walkthrough hop mechanism. */
125
+ export type SubsystemEdgeMechanism = SubsystemWalkthroughMechanism;
139
126
 
140
127
  /**
141
- * Which edge vocabulary the canvas draws. The two vocabularies are disjoint;
142
- * a view shows only edges (and their labels) from the selected source.
143
- * - `relations`: only topology relation edges (`extends`, `implements`, …)
128
+ * Which edge source the canvas draws. The two sources are disjoint; a view
129
+ * shows only edges (and their labels) from the selected source.
130
+ * - `graphify`: only graphify-native static edges (`imports`, `contains`, …)
144
131
  * - `walkthroughs`: only walkthrough hop edges (`calls`, `feeds`, …)
145
132
  */
146
- export type SubsystemEdgeView = 'relations' | 'walkthroughs';
133
+ export type SubsystemEdgeView = 'graphify' | 'walkthroughs';
147
134
 
148
135
  /**
149
136
  * Where a display edge came from.
150
137
  *
151
- * - `subsystem` (default): a verb from the authored vocabularies
152
- * (`SubsystemRelationType` / `SubsystemWalkthroughMechanism`), colored from
138
+ * - `subsystem` (default): a walkthrough hop mechanism verb, colored from
153
139
  * `MECHANISM_COLOR`.
154
140
  * - `graphify`: a raw relation read off graphify's static symbol graph
155
141
  * (`imports`, `contains`, `re_exports`, …). These are DERIVED, never authored,
@@ -162,10 +148,8 @@ export type SubsystemEdgeProvenance = 'subsystem' | 'graphify';
162
148
  * A graphify-native topology edge — a raw graphify relation that has no
163
149
  * subsystem mechanism equivalent.
164
150
  *
165
- * Kept structurally separate from `SubsystemRelation`: those are authored into
166
- * a portable model and validated against a closed vocabulary, whereas these are
167
- * derived from a graphify run and carry graphify's own (open) verb set. They are
168
- * a display input only — never written back into a `SubsystemModelDocument`.
151
+ * Derived from a graphify run and carry graphify's own (open) verb set. They
152
+ * are a display input only — never written back into a `SubsystemModelDocument`.
169
153
  */
170
154
  export interface SubsystemGraphifyRelation {
171
155
  id: string;
@@ -192,8 +176,8 @@ export interface SubsystemGraphifyRelation {
192
176
  /** A component node — the named unit, construct-tagged; `file` is its location. */
193
177
  export interface SubsystemComponent {
194
178
  /**
195
- * Model-local stable alias, unique per model. Referenced by relation /
196
- * walkthrough `from` / `to`; edges point at the alias, not the location.
179
+ * Model-local stable alias, unique per model. Referenced by walkthrough
180
+ * `from` / `to`; edges point at the alias, not the location.
197
181
  * Code identity (for composed multi-model views) lives on
198
182
  * `purl` + `file` + `symbol`, not here.
199
183
  */
@@ -330,19 +314,9 @@ export interface SubsystemComponent {
330
314
  declarationRef?: SubsystemDeclarationRef;
331
315
  }
332
316
 
333
- /** A topology relation between components (structural / module / type). */
334
- export interface SubsystemRelation {
335
- id: string;
336
- from: string;
337
- to: string;
338
- relationType: SubsystemRelationType;
339
- /** Concrete file/symbol refs backing the relation. */
340
- refs?: string[];
341
- }
342
-
343
317
  /**
344
- * Derived / display graph edge used by renderers. Built from `relations`
345
- * and/or walkthrough hops — not authored as its own document field.
318
+ * Derived / display graph edge used by renderers. Built from walkthrough hops
319
+ * (and graphify-native relations) — not authored as its own document field.
346
320
  */
347
321
  export interface SubsystemComponentEdge {
348
322
  id: string;
@@ -352,8 +326,8 @@ export interface SubsystemComponentEdge {
352
326
  * The edge verb. For `provenance: 'subsystem'` (the default) this is a
353
327
  * `SubsystemEdgeMechanism`; for `provenance: 'graphify'` it is the raw
354
328
  * graphify relation. Typed as `string` because the display edge is a derived
355
- * structure and graphify's verb set is open — the authored vocabularies
356
- * (`SubsystemRelationType` / `SubsystemWalkthroughMechanism`) stay closed.
329
+ * structure and graphify's verb set is open — the walkthrough vocabulary
330
+ * (`SubsystemWalkthroughMechanism`) stays closed.
357
331
  */
358
332
  mechanism: string;
359
333
  /** Origin of the edge; absent means `'subsystem'`. */
@@ -414,13 +388,11 @@ export interface SubsystemWalkthrough {
414
388
 
415
389
  export interface SubsystemModelDocument {
416
390
  components: SubsystemComponent[];
417
- /** Topology relations (structural / module / type). May be empty. */
418
- relations: SubsystemRelation[];
419
391
  /** Ordered runtime walkthroughs (one per named behavior). */
420
392
  walkthroughs?: SubsystemWalkthrough[];
421
393
  }
422
394
 
423
- /** Stable id for a derived graph edge from a relation or walkthrough hop. */
395
+ /** Stable id for a derived graph edge from a walkthrough hop. */
424
396
  export function derivedGraphEdgeId(
425
397
  from: string,
426
398
  to: string,
@@ -429,10 +401,6 @@ export function derivedGraphEdgeId(
429
401
  return `${from}--${mechanism}-->${to}`;
430
402
  }
431
403
 
432
- /**
433
- * Build display edges for the graph canvas from topology relations and
434
- * walkthrough hops (deduped by from/to/mechanism).
435
- */
436
404
  /** React Flow / canvas edge id for a walkthrough hop. */
437
405
  export function walkthroughStepGraphEdgeId(
438
406
  step: Pick<SubsystemWalkthroughStep, 'from' | 'to' | 'mechanism'>,
@@ -472,23 +440,10 @@ export function reorderTargetIndex(boundary: number, from: number): number {
472
440
  }
473
441
 
474
442
  export function deriveGraphEdges(doc: {
475
- relations?: readonly SubsystemRelation[];
476
443
  walkthroughs?: readonly SubsystemWalkthrough[];
477
444
  graphifyRelations?: readonly SubsystemGraphifyRelation[];
478
445
  }): SubsystemComponentEdge[] {
479
446
  const byId = new Map<string, SubsystemComponentEdge>();
480
- for (const r of doc.relations ?? []) {
481
- const id = r.id || derivedGraphEdgeId(r.from, r.to, r.relationType);
482
- if (!byId.has(id)) {
483
- byId.set(id, {
484
- id,
485
- from: r.from,
486
- to: r.to,
487
- mechanism: r.relationType,
488
- refs: r.refs,
489
- });
490
- }
491
- }
492
447
  for (const w of doc.walkthroughs ?? []) {
493
448
  for (const step of w.steps) {
494
449
  const id = derivedGraphEdgeId(step.from, step.to, step.mechanism);
@@ -522,18 +477,16 @@ export function deriveGraphEdges(doc: {
522
477
  }
523
478
 
524
479
  /**
525
- * True when the model has components but no topology or walkthrough edges.
480
+ * True when the model has components but no walkthrough or graphify edges.
526
481
  * Those snapshots are a catalog of declarations, not a graph.
527
482
  */
528
483
  export function isConstructsOnlyModel(doc: {
529
484
  components: readonly { alias: string }[];
530
- relations?: readonly SubsystemRelation[];
531
485
  walkthroughs?: readonly SubsystemWalkthrough[];
532
486
  graphifyRelations?: readonly SubsystemGraphifyRelation[];
533
487
  }): boolean {
534
488
  if (doc.components.length === 0) return false;
535
489
  return deriveGraphEdges({
536
- relations: doc.relations,
537
490
  walkthroughs: doc.walkthroughs,
538
491
  graphifyRelations: doc.graphifyRelations,
539
492
  }).length === 0;
@@ -1131,19 +1084,6 @@ export interface SubsystemGraphEdgeData extends Record<string, unknown> {
1131
1084
 
1132
1085
  export type SubsystemGraphEdge = Edge<SubsystemGraphEdgeData>;
1133
1086
 
1134
- /**
1135
- * Runtime vocabulary of relation types — mirrors `SubsystemRelationType`.
1136
- * Used to split derived display edges into their relation vs walkthrough
1137
- * source (the two unions are disjoint).
1138
- */
1139
- export const SUBSYSTEM_RELATION_TYPES = [
1140
- 'extends',
1141
- 'inherits',
1142
- 'implements',
1143
- 'mixes_in',
1144
- 'method',
1145
- ] as const satisfies readonly SubsystemRelationType[];
1146
-
1147
1087
  /** Runtime vocabulary of walkthrough hop mechanisms — mirrors `SubsystemWalkthroughMechanism`. */
1148
1088
  export const SUBSYSTEM_WALKTHROUGH_MECHANISMS = [
1149
1089
  'calls',
@@ -1156,18 +1096,10 @@ export const SUBSYSTEM_WALKTHROUGH_MECHANISMS = [
1156
1096
  'registers-into',
1157
1097
  ] as const satisfies readonly SubsystemWalkthroughMechanism[];
1158
1098
 
1159
- const RELATION_TYPE_SET: ReadonlySet<string> = new Set(SUBSYSTEM_RELATION_TYPES);
1160
1099
  const WALKTHROUGH_MECHANISM_SET: ReadonlySet<string> = new Set(
1161
1100
  SUBSYSTEM_WALKTHROUGH_MECHANISMS,
1162
1101
  );
1163
1102
 
1164
- /** True when a mechanism belongs to the topology relation vocabulary. */
1165
- export function isRelationMechanism(
1166
- mechanism: string,
1167
- ): mechanism is SubsystemRelationType {
1168
- return RELATION_TYPE_SET.has(mechanism);
1169
- }
1170
-
1171
1103
  /** True when a mechanism belongs to the walkthrough hop vocabulary. */
1172
1104
  export function isWalkthroughMechanism(
1173
1105
  mechanism: string,
@@ -1177,12 +1109,7 @@ export function isWalkthroughMechanism(
1177
1109
 
1178
1110
  export const MECHANISM_COLOR: Record<SubsystemEdgeMechanism, string> = {
1179
1111
  calls: '#22c55e', // green
1180
- extends: '#b48ead', // purple
1181
- inherits: '#9b6fd0', // purple
1182
- implements: '#c586c0', // magenta
1183
- mixes_in: '#d474a8', // pink-magenta
1184
1112
  uses: '#e3b341', // gold
1185
- method: '#c586c0', // magenta
1186
1113
  feeds: '#4ec9b0', // teal — data-flow into a processor
1187
1114
  produces: '#a78bfa', // violet — emits an output type
1188
1115
  writes: '#e8853a', // orange — mutates retained state
@@ -1193,12 +1120,7 @@ export const MECHANISM_COLOR: Record<SubsystemEdgeMechanism, string> = {
1193
1120
 
1194
1121
  export const MECHANISM_STYLE: Record<SubsystemEdgeMechanism, 'solid' | 'dashed' | 'dotted'> = {
1195
1122
  calls: 'solid',
1196
- extends: 'dashed',
1197
- inherits: 'dashed',
1198
- implements: 'dashed',
1199
- mixes_in: 'dashed',
1200
1123
  uses: 'solid',
1201
- method: 'solid',
1202
1124
  feeds: 'solid',
1203
1125
  produces: 'solid',
1204
1126
  writes: 'solid',
@@ -1363,12 +1285,7 @@ export function boundaryFill(color: string, alpha = '1f'): string {
1363
1285
  * directly verifiable" styling of edge labels. */
1364
1286
  export const MECHANISM_DESCRIPTIONS: [SubsystemEdgeMechanism, string, boolean][] = [
1365
1287
  ['calls', 'function/method call (call graph edge)', true],
1366
- ['extends', 'class inheritance', true],
1367
- ['inherits', 'class inheritance', true],
1368
- ['implements', 'implements interface / protocol', true],
1369
- ['mixes_in', 'applies mixin', true],
1370
1288
  ['uses', 'general dependency (import, call, or reference)', false],
1371
- ['method', 'structural: has method / member', true],
1372
1289
  ['feeds', 'data flow: output feeds into input', false],
1373
1290
  ['produces', 'data flow: produces / outputs', false],
1374
1291
  ['writes', 'state access: mutates retained state', true],
@@ -1733,7 +1650,7 @@ export function convertSubsystemToEdges(
1733
1650
 
1734
1651
  /** Stable key for layout-affecting graph fields (ignores declarationRef, etc.). */
1735
1652
  export function subsystemGraphLayoutKey(
1736
- doc: Pick<SubsystemModelDocument, 'components' | 'relations' | 'walkthroughs'> & {
1653
+ doc: Pick<SubsystemModelDocument, 'components' | 'walkthroughs'> & {
1737
1654
  graphifyRelations?: readonly SubsystemGraphifyRelation[];
1738
1655
  },
1739
1656
  ): string {
@@ -18,17 +18,12 @@ const doc: SubsystemModelDocument = {
18
18
  { alias: 'x', name: 'Gist API', construct: 'external', file: '', purl: 'external:api.github.com/gists' },
19
19
  { alias: 'agent', name: 'Maintenance agent', construct: 'custom_entity', file: '', purl: 'external' },
20
20
  ],
21
- relations: [
22
- { id: 'r1', from: 'a', to: 'b', relationType: 'method' }, // intra p1
23
- { id: 'r2', from: 'a', to: 'c', relationType: 'method' },
24
- { id: 'r3', from: 'b', to: 'c', relationType: 'method' },
25
- { id: 'r4', from: 'a', to: 'x', relationType: 'method' },
26
- ],
27
21
  walkthroughs: [
28
22
  {
29
23
  id: 'w1',
30
24
  title: 'flow',
31
25
  steps: [
26
+ { from: 'a', to: 'b', mechanism: 'calls', file: 'src/a.ts', line: 1, purl: `${REPO}#src/a.ts`, symbol: 'Alpha' },
32
27
  { from: 'b', to: 'c', mechanism: 'calls', file: 'src/b.ts', line: 1, purl: `${REPO}#src/b.ts`, symbol: 'Beta' },
33
28
  { from: 'a', to: 'c', mechanism: 'calls', file: 'src/a.ts', line: 2, purl: `${REPO}#src/a.ts`, symbol: 'Alpha' },
34
29
  { from: 'a', to: 'agent', mechanism: 'calls', file: 'src/a.ts', line: 3, purl: `${REPO}#src/a.ts`, symbol: 'Alpha' },
@@ -77,18 +72,16 @@ describe('container view', () => {
77
72
  expect(model.groups[0]!.memberIds.sort()).toEqual(['container:p1', 'container:p2']);
78
73
  });
79
74
 
80
- test('edges roll up and intra-container edges drop', () => {
75
+ test('flow edges roll up to containers; only flow edges exist', () => {
81
76
  const byId = Object.fromEntries(model.edges.map((e) => [e.id, e]));
82
77
  // a->b is intra-p1 — dropped.
83
78
  expect(model.edges.some((e) => e.source === e.target)).toBe(false);
84
- const rel = byId['relationship:container:p1\u0000container:p2']!;
85
- expect(rel.kind).toBe('relationship');
86
- expect(rel.count).toBe(2); // r2 + r3
87
79
  const flow = byId['flow:container:p1\u0000container:p2']!;
80
+ expect(flow.kind).toBe('flow');
88
81
  expect(flow.count).toBe(2); // a->c + b->c
89
- expect(byId['relationship:container:p1\u0000external:external:api.github.com/gists']).toBeDefined();
90
82
  expect(byId['flow:container:p1\u0000actor:agent']).toBeDefined();
91
- expect(model.edges).toHaveLength(4);
83
+ expect(model.edges.every((e) => e.kind === 'flow')).toBe(true);
84
+ expect(model.edges).toHaveLength(2);
92
85
  });
93
86
  });
94
87
 
@@ -2,8 +2,8 @@
2
2
  * toC4 — project a subsystem-model (or a composition of them) onto C4 levels.
3
3
  *
4
4
  * The stored document is flat: `components[]` carry `process` (runtime unit),
5
- * `purl` (repo/package), `construct`, `role`, `module`, plus `relations[]`
6
- * (static) and `walkthroughs[]` (dynamic). C4 wants a hierarchy above the
5
+ * `purl` (repo/package), `construct`, `role`, `module`, plus `walkthroughs[]`
6
+ * (dynamic). C4 wants a hierarchy above the
7
7
  * component — system → container → component — with externals and actors
8
8
  * outside it. This module derives that hierarchy without inventing data:
9
9
  *
@@ -59,12 +59,12 @@ export interface C4Group {
59
59
  memberIds: string[];
60
60
  }
61
61
 
62
- /** A rolled-up collaboration. `relationship` = static, `flow` = walkthrough. */
62
+ /** A rolled-up collaboration — a walkthrough flow between owners. */
63
63
  export interface C4Edge {
64
64
  id: string;
65
65
  source: string;
66
66
  target: string;
67
- kind: 'relationship' | 'flow';
67
+ kind: 'flow';
68
68
  label: string;
69
69
  mechanisms: string[];
70
70
  count: number;
@@ -234,7 +234,6 @@ export function toC4(doc: SubsystemModelDocument, options: ToC4Options = {}): C4
234
234
  }
235
235
 
236
236
  // --- Edges: roll each endpoint up to its owner at this view -------------
237
- const relEdges = new Map<string, C4Edge>();
238
237
  const flowEdges = new Map<string, C4Edge>();
239
238
 
240
239
  const addEdge = (
@@ -255,16 +254,13 @@ export function toC4(doc: SubsystemModelDocument, options: ToC4Options = {}): C4
255
254
  if (!edge.mechanisms.includes(mechanism)) edge.mechanisms.push(mechanism);
256
255
  };
257
256
 
258
- for (const r of doc.relations ?? []) {
259
- addEdge(relEdges, ownerOf.get(r.from), ownerOf.get(r.to), 'relationship', r.relationType);
260
- }
261
257
  for (const w of doc.walkthroughs ?? []) {
262
258
  for (const s of w.steps ?? []) {
263
259
  addEdge(flowEdges, ownerOf.get(s.from), ownerOf.get(s.to), 'flow', s.mechanism);
264
260
  }
265
261
  }
266
262
 
267
- const edges = [...relEdges.values(), ...flowEdges.values()];
263
+ const edges = [...flowEdges.values()];
268
264
  for (const e of edges) e.label = [...e.mechanisms].sort().join(' + ');
269
265
 
270
266
  return {