@principal-ai/subsystems-react 0.24.27 → 0.25.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 (33) hide show
  1. package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts +1 -1
  2. package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
  3. package/dist/stories/Subsystem/ComponentGraph/fixtures.js +7 -7
  4. package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
  5. package/dist/subsystem/ComponentDeclaration.d.ts +1 -1
  6. package/dist/subsystem/ComponentDeclaration.d.ts.map +1 -1
  7. package/dist/subsystem/ComponentDeclaration.js +1 -1
  8. package/dist/subsystem/ComponentDeclaration.js.map +1 -1
  9. package/dist/subsystem/ConstructsCatalog.d.ts +2 -2
  10. package/dist/subsystem/ConstructsCatalog.d.ts.map +1 -1
  11. package/dist/subsystem/ConstructsCatalog.js +62 -62
  12. package/dist/subsystem/ConstructsCatalog.js.map +1 -1
  13. package/dist/subsystem/SubsystemComponentGraph.d.ts +2 -2
  14. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
  15. package/dist/subsystem/SubsystemComponentGraph.js +27 -27
  16. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
  17. package/dist/subsystem/model.d.ts +11 -5
  18. package/dist/subsystem/model.d.ts.map +1 -1
  19. package/dist/subsystem/model.js +36 -36
  20. package/dist/subsystem/model.js.map +1 -1
  21. package/dist/subsystem/nodes.d.ts +3 -3
  22. package/dist/subsystem/nodes.d.ts.map +1 -1
  23. package/dist/subsystem/nodes.js +3 -3
  24. package/dist/subsystem/nodes.js.map +1 -1
  25. package/package.json +1 -1
  26. package/src/stories/Subsystem/ComponentGraph/ModuleBadges.stories.tsx +1 -1
  27. package/src/stories/Subsystem/ComponentGraph/fixtures.ts +8 -8
  28. package/src/subsystem/ComponentDeclaration.tsx +2 -2
  29. package/src/subsystem/ConstructsCatalog.tsx +71 -75
  30. package/src/subsystem/SubsystemComponentGraph.tsx +28 -28
  31. package/src/subsystem/model.test.ts +51 -51
  32. package/src/subsystem/model.ts +47 -41
  33. package/src/subsystem/nodes.tsx +6 -6
@@ -32,8 +32,8 @@ import {
32
32
  import type { SubsystemComponent, SubsystemComponentEdge } from './model';
33
33
 
34
34
  const comps: SubsystemComponent[] = [
35
- { id: 'reader', name: 'SessionReader', construct: 'class', file: 'SessionReader.ts', purl: 'pkg:github/principal-ai/agent-monitoring' },
36
- { id: 'transcript', name: 'transcript', construct: 'function', file: 'transcript.ts', purl: 'pkg:github/principal-ai/agent-monitoring' },
35
+ { alias: 'reader', name: 'SessionReader', construct: 'class', file: 'SessionReader.ts', purl: 'pkg:github/principal-ai/agent-monitoring' },
36
+ { alias: 'transcript', name: 'transcript', construct: 'function', file: 'transcript.ts', purl: 'pkg:github/principal-ai/agent-monitoring' },
37
37
  ];
38
38
 
39
39
  const relations = [
@@ -63,7 +63,7 @@ describe('subsystem graph model', () => {
63
63
  const withExternal: SubsystemComponent[] = [
64
64
  ...comps,
65
65
  {
66
- id: "proposed-watcher",
66
+ alias: "proposed-watcher",
67
67
  name: "watchDir",
68
68
  construct: "external",
69
69
  // Intentionally omit file/purl — agents often leave these off for externals.
@@ -240,24 +240,24 @@ describe('subsystem graph model', () => {
240
240
  test('getSubsystemRegions groups by process, skipping process-less nodes', () => {
241
241
  const regions = getSubsystemRegions({
242
242
  components: [
243
- { id: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
244
- { id: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
245
- { id: 'c', name: 'c', construct: 'function', file: 'c.ts', purl: 'pkg:github/acme/app', process: 'app/renderer' },
246
- { id: 'd', name: 'd', construct: 'function', file: 'd.ts', purl: 'pkg:github/acme/app' },
243
+ { alias: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
244
+ { alias: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
245
+ { alias: 'c', name: 'c', construct: 'function', file: 'c.ts', purl: 'pkg:github/acme/app', process: 'app/renderer' },
246
+ { alias: 'd', name: 'd', construct: 'function', file: 'd.ts', purl: 'pkg:github/acme/app' },
247
247
  ],
248
248
  });
249
249
  expect(regions.map((r) => r.key)).toEqual(['app/host', 'app/renderer']);
250
250
  expect(regions.every((r) => r.kind === 'process')).toBe(true);
251
- expect(regions[0]!.memberIds).toEqual(['a', 'b']);
251
+ expect(regions[0]!.memberAliases).toEqual(['a', 'b']);
252
252
  });
253
253
 
254
254
  test('getSubsystemModuleRegions groups by module, skipping module-less nodes', () => {
255
255
  const regions = getSubsystemModuleRegions({
256
256
  components: [
257
- { id: 'a', name: 'a', construct: 'function', file: 'transcript.ts', purl: 'pkg:github/acme/app', module: 'src/session/transcript.ts' },
258
- { id: 'b', name: 'b', construct: 'type_alias', file: 'transcript.ts', purl: 'pkg:github/acme/app', module: 'src/session/transcript.ts' },
259
- { id: 'c', name: 'c', construct: 'function', file: 'paths.ts', purl: 'pkg:github/acme/app', module: 'src/session/paths.ts' },
260
- { id: 'd', name: 'd', construct: 'function', file: 'other.ts', purl: 'pkg:github/acme/app' },
257
+ { alias: 'a', name: 'a', construct: 'function', file: 'transcript.ts', purl: 'pkg:github/acme/app', module: 'src/session/transcript.ts' },
258
+ { alias: 'b', name: 'b', construct: 'type_alias', file: 'transcript.ts', purl: 'pkg:github/acme/app', module: 'src/session/transcript.ts' },
259
+ { alias: 'c', name: 'c', construct: 'function', file: 'paths.ts', purl: 'pkg:github/acme/app', module: 'src/session/paths.ts' },
260
+ { alias: 'd', name: 'd', construct: 'function', file: 'other.ts', purl: 'pkg:github/acme/app' },
261
261
  ],
262
262
  });
263
263
  expect(regions.map((r) => r.key)).toEqual([
@@ -265,14 +265,14 @@ describe('subsystem graph model', () => {
265
265
  'src/session/paths.ts',
266
266
  ]);
267
267
  expect(regions.every((r) => r.kind === 'module')).toBe(true);
268
- expect(regions[0]!.memberIds).toEqual(['a', 'b']);
268
+ expect(regions[0]!.memberAliases).toEqual(['a', 'b']);
269
269
  });
270
270
 
271
271
  test('convertSubsystemToNodes prefers module parentId over process', () => {
272
272
  const nodes = convertSubsystemToNodes({
273
273
  components: [
274
274
  {
275
- id: 'a',
275
+ alias: 'a',
276
276
  name: 'a',
277
277
  construct: 'function',
278
278
  file: 'a.ts',
@@ -280,7 +280,7 @@ describe('subsystem graph model', () => {
280
280
  process: 'app/host',
281
281
  module: 'src/a.ts',
282
282
  },
283
- { id: 'd', name: 'd', construct: 'function', file: 'd.ts', purl: 'pkg:github/acme/app' },
283
+ { alias: 'd', name: 'd', construct: 'function', file: 'd.ts', purl: 'pkg:github/acme/app' },
284
284
  ],
285
285
  relations: [],
286
286
  });
@@ -293,8 +293,8 @@ describe('subsystem graph model', () => {
293
293
  test('convertSubsystemToNodes stamps parentId for process members only', () => {
294
294
  const nodes = convertSubsystemToNodes({
295
295
  components: [
296
- { id: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
297
- { id: 'd', name: 'd', construct: 'function', file: 'd.ts', purl: 'pkg:github/acme/app' },
296
+ { alias: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
297
+ { alias: 'd', name: 'd', construct: 'function', file: 'd.ts', purl: 'pkg:github/acme/app' },
298
298
  ],
299
299
  relations: [],
300
300
  });
@@ -307,8 +307,8 @@ describe('subsystem graph model', () => {
307
307
  test('convertSubsystemToGroups emits one parent per process', () => {
308
308
  const groups = convertSubsystemToGroups({
309
309
  components: [
310
- { id: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
311
- { id: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
310
+ { alias: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
311
+ { alias: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
312
312
  ],
313
313
  });
314
314
  expect(groups).toHaveLength(1);
@@ -320,7 +320,7 @@ describe('subsystem graph model', () => {
320
320
  const groups = convertSubsystemToGroups({
321
321
  components: [
322
322
  {
323
- id: 'a',
323
+ alias: 'a',
324
324
  name: 'a',
325
325
  construct: 'function',
326
326
  file: 'a.ts',
@@ -329,7 +329,7 @@ describe('subsystem graph model', () => {
329
329
  module: 'src/a.ts',
330
330
  },
331
331
  {
332
- id: 'b',
332
+ alias: 'b',
333
333
  name: 'b',
334
334
  construct: 'function',
335
335
  file: 'a.ts',
@@ -352,7 +352,7 @@ describe('subsystem graph model', () => {
352
352
  const { nodes, regions } = await buildSubsystemGraph({
353
353
  components: [
354
354
  {
355
- id: 'boot',
355
+ alias: 'boot',
356
356
  name: 'boot',
357
357
  construct: 'function',
358
358
  file: 'main.ts',
@@ -362,7 +362,7 @@ describe('subsystem graph model', () => {
362
362
  symbol: 'boot',
363
363
  },
364
364
  {
365
- id: 'create',
365
+ alias: 'create',
366
366
  name: 'createHost',
367
367
  construct: 'function',
368
368
  file: 'main.ts',
@@ -372,7 +372,7 @@ describe('subsystem graph model', () => {
372
372
  symbol: 'createHost',
373
373
  },
374
374
  {
375
- id: 'write',
375
+ alias: 'write',
376
376
  name: 'writeSession',
377
377
  construct: 'function',
378
378
  file: 'store.ts',
@@ -382,7 +382,7 @@ describe('subsystem graph model', () => {
382
382
  symbol: 'writeSession',
383
383
  },
384
384
  {
385
- id: 'store',
385
+ alias: 'store',
386
386
  name: 'SessionStore',
387
387
  construct: 'store',
388
388
  file: 'store.ts',
@@ -416,9 +416,9 @@ describe('subsystem graph model', () => {
416
416
  test('buildSubsystemGraph drops singleton process frames (no parentId, no group)', async () => {
417
417
  const { nodes, regions } = await buildSubsystemGraph({
418
418
  components: [
419
- { id: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
420
- { id: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
421
- { id: 'solo', name: 'solo', construct: 'function', file: 's.ts', purl: 'pkg:github/acme/app', process: 'app/lonely' },
419
+ { alias: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
420
+ { alias: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app', process: 'app/host' },
421
+ { alias: 'solo', name: 'solo', construct: 'function', file: 's.ts', purl: 'pkg:github/acme/app', process: 'app/lonely' },
422
422
  ],
423
423
  relations: [],
424
424
  walkthroughs: [{
@@ -436,7 +436,7 @@ describe('subsystem graph model', () => {
436
436
  const { nodes, regions } = await buildSubsystemGraph({
437
437
  components: [
438
438
  {
439
- id: 'rec',
439
+ alias: 'rec',
440
440
  name: 'CodexRolloutRecord',
441
441
  construct: 'type_alias',
442
442
  file: 'transcript.ts',
@@ -445,7 +445,7 @@ describe('subsystem graph model', () => {
445
445
  symbol: 'CodexRolloutRecord',
446
446
  },
447
447
  {
448
- id: 'parse',
448
+ alias: 'parse',
449
449
  name: 'parseTranscript',
450
450
  construct: 'function',
451
451
  file: 'transcript.ts',
@@ -454,7 +454,7 @@ describe('subsystem graph model', () => {
454
454
  symbol: 'parseTranscript',
455
455
  },
456
456
  {
457
- id: 'solo',
457
+ alias: 'solo',
458
458
  name: 'lonely',
459
459
  construct: 'function',
460
460
  file: 'solo.ts',
@@ -477,10 +477,10 @@ describe('subsystem graph model', () => {
477
477
  test('getSubsystemPackageRegions groups by purl repo key, skipping externals', () => {
478
478
  const regions = getSubsystemPackageRegions({
479
479
  components: [
480
- { id: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app#a.ts' },
481
- { id: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app' },
482
- { id: 'c', name: 'c', construct: 'function', file: 'c.ts', purl: 'pkg:github/other/lib' },
483
- { id: 'ext', name: 'Stripe', construct: 'external', file: '', purl: 'pkg:npm/stripe' },
480
+ { alias: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app#a.ts' },
481
+ { alias: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app' },
482
+ { alias: 'c', name: 'c', construct: 'function', file: 'c.ts', purl: 'pkg:github/other/lib' },
483
+ { alias: 'ext', name: 'Stripe', construct: 'external', file: '', purl: 'pkg:npm/stripe' },
484
484
  ],
485
485
  });
486
486
  expect(regions.map((r) => r.key).sort()).toEqual([
@@ -494,8 +494,8 @@ describe('subsystem graph model', () => {
494
494
  test('buildBoundaryLayoutGroups skips package frames for single-repo graphs', () => {
495
495
  const groups = buildBoundaryLayoutGroups({
496
496
  components: [
497
- { id: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app' },
498
- { id: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app' },
497
+ { alias: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app' },
498
+ { alias: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app' },
499
499
  ],
500
500
  });
501
501
  expect(groups.filter((g) => g.region.kind === 'package')).toHaveLength(0);
@@ -504,10 +504,10 @@ describe('subsystem graph model', () => {
504
504
  test('buildBoundaryLayoutGroups frames packages when multi-repo', () => {
505
505
  const groups = buildBoundaryLayoutGroups({
506
506
  components: [
507
- { id: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app' },
508
- { id: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app' },
509
- { id: 'c', name: 'c', construct: 'function', file: 'c.ts', purl: 'pkg:github/other/lib' },
510
- { id: 'd', name: 'd', construct: 'function', file: 'd.ts', purl: 'pkg:github/other/lib' },
507
+ { alias: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app' },
508
+ { alias: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app' },
509
+ { alias: 'c', name: 'c', construct: 'function', file: 'c.ts', purl: 'pkg:github/other/lib' },
510
+ { alias: 'd', name: 'd', construct: 'function', file: 'd.ts', purl: 'pkg:github/other/lib' },
511
511
  ],
512
512
  });
513
513
  const pkgs = groups.filter((g) => g.region.kind === 'package');
@@ -521,7 +521,7 @@ describe('subsystem graph model', () => {
521
521
  const groups = buildBoundaryLayoutGroups({
522
522
  components: [
523
523
  {
524
- id: 'a',
524
+ alias: 'a',
525
525
  name: 'a',
526
526
  construct: 'function',
527
527
  file: 'a.ts',
@@ -529,7 +529,7 @@ describe('subsystem graph model', () => {
529
529
  process: 'app/host',
530
530
  },
531
531
  {
532
- id: 'b',
532
+ alias: 'b',
533
533
  name: 'b',
534
534
  construct: 'function',
535
535
  file: 'b.ts',
@@ -537,7 +537,7 @@ describe('subsystem graph model', () => {
537
537
  process: 'app/host',
538
538
  },
539
539
  {
540
- id: 'c',
540
+ alias: 'c',
541
541
  name: 'c',
542
542
  construct: 'function',
543
543
  file: 'c.ts',
@@ -545,7 +545,7 @@ describe('subsystem graph model', () => {
545
545
  process: 'lib/worker',
546
546
  },
547
547
  {
548
- id: 'd',
548
+ alias: 'd',
549
549
  name: 'd',
550
550
  construct: 'function',
551
551
  file: 'd.ts',
@@ -562,7 +562,7 @@ describe('subsystem graph model', () => {
562
562
  const { nodes } = await buildSubsystemGraph({
563
563
  components: [
564
564
  {
565
- id: 'a1',
565
+ alias: 'a1',
566
566
  name: 'a1',
567
567
  construct: 'function',
568
568
  file: 'a.ts',
@@ -571,7 +571,7 @@ describe('subsystem graph model', () => {
571
571
  module: 'src/a.ts',
572
572
  },
573
573
  {
574
- id: 'a2',
574
+ alias: 'a2',
575
575
  name: 'a2',
576
576
  construct: 'function',
577
577
  file: 'a.ts',
@@ -580,7 +580,7 @@ describe('subsystem graph model', () => {
580
580
  module: 'src/a.ts',
581
581
  },
582
582
  {
583
- id: 'b1',
583
+ alias: 'b1',
584
584
  name: 'b1',
585
585
  construct: 'function',
586
586
  file: 'b.ts',
@@ -589,7 +589,7 @@ describe('subsystem graph model', () => {
589
589
  module: 'src/b.ts',
590
590
  },
591
591
  {
592
- id: 'b2',
592
+ alias: 'b2',
593
593
  name: 'b2',
594
594
  construct: 'function',
595
595
  file: 'b.ts',
@@ -617,8 +617,8 @@ describe('subsystem graph model', () => {
617
617
  const groups = buildBoundaryLayoutGroups(
618
618
  {
619
619
  components: [
620
- { id: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app' },
621
- { id: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app' },
620
+ { alias: 'a', name: 'a', construct: 'function', file: 'a.ts', purl: 'pkg:github/acme/app' },
621
+ { alias: 'b', name: 'b', construct: 'function', file: 'b.ts', purl: 'pkg:github/acme/app' },
622
622
  ],
623
623
  },
624
624
  { packageFrames: 'always' },
@@ -127,7 +127,13 @@ export type SubsystemEdgeView = 'relations' | 'walkthroughs';
127
127
 
128
128
  /** A component node — the named unit, construct-tagged; `file` is its location. */
129
129
  export interface SubsystemComponent {
130
- id: string;
130
+ /**
131
+ * Model-local stable alias, unique per model. Referenced by relation /
132
+ * walkthrough `from` / `to`; edges point at the alias, not the location.
133
+ * Code identity (for composed multi-model views) lives on
134
+ * `purl` + `file` + `symbol`, not here.
135
+ */
136
+ alias: string;
131
137
  name: string;
132
138
  /**
133
139
  * The node's construct — what it IS as a declaration (class, function,
@@ -268,8 +274,8 @@ export interface SubsystemRelation {
268
274
  */
269
275
  export interface SubsystemComponentEdge {
270
276
  id: string;
271
- from: string; // component id
272
- to: string; // component id or external target label
277
+ from: string; // component alias
278
+ to: string; // component alias or external target label
273
279
  mechanism: SubsystemEdgeMechanism;
274
280
  /** Concrete file/symbol refs backing the edge (the seam). */
275
281
  refs?: string[];
@@ -374,7 +380,7 @@ export function deriveGraphEdges(doc: {
374
380
  * Those snapshots are a catalog of declarations, not a graph.
375
381
  */
376
382
  export function isConstructsOnlyModel(doc: {
377
- components: readonly { id: string }[];
383
+ components: readonly { alias: string }[];
378
384
  relations?: readonly SubsystemRelation[];
379
385
  walkthroughs?: readonly SubsystemWalkthrough[];
380
386
  }): boolean {
@@ -473,7 +479,7 @@ export interface SubsystemProcessRegion {
473
479
  /** Display label for the boundary frame. */
474
480
  label: string;
475
481
  /** Component ids that are members of this region. */
476
- memberIds: string[];
482
+ memberAliases: string[];
477
483
  }
478
484
 
479
485
  /** Options for which boundary frames are kept. */
@@ -598,14 +604,14 @@ export function getSubsystemRegions(
598
604
  const p = c.process?.trim();
599
605
  if (!p) continue;
600
606
  const list = byProcess.get(p) ?? [];
601
- list.push(c.id);
607
+ list.push(c.alias);
602
608
  byProcess.set(p, list);
603
609
  }
604
- return [...byProcess.entries()].map(([key, memberIds]) => ({
610
+ return [...byProcess.entries()].map(([key, memberAliases]) => ({
605
611
  kind: 'process' as const,
606
612
  key,
607
613
  label: key,
608
- memberIds,
614
+ memberAliases,
609
615
  }));
610
616
  }
611
617
 
@@ -621,14 +627,14 @@ export function getSubsystemModuleRegions(
621
627
  const m = c.module?.trim();
622
628
  if (!m) continue;
623
629
  const list = byModule.get(m) ?? [];
624
- list.push(c.id);
630
+ list.push(c.alias);
625
631
  byModule.set(m, list);
626
632
  }
627
- return [...byModule.entries()].map(([key, memberIds]) => ({
633
+ return [...byModule.entries()].map(([key, memberAliases]) => ({
628
634
  kind: 'module' as const,
629
635
  key,
630
636
  label: key,
631
- memberIds,
637
+ memberAliases,
632
638
  }));
633
639
  }
634
640
 
@@ -645,14 +651,14 @@ export function getSubsystemPackageRegions(
645
651
  const key = componentPackageKey(c);
646
652
  if (!key) continue;
647
653
  const list = byPackage.get(key) ?? [];
648
- list.push(c.id);
654
+ list.push(c.alias);
649
655
  byPackage.set(key, list);
650
656
  }
651
- return [...byPackage.entries()].map(([key, memberIds]) => ({
657
+ return [...byPackage.entries()].map(([key, memberAliases]) => ({
652
658
  kind: 'package' as const,
653
659
  key,
654
660
  label: packageRegionLabel(key),
655
- memberIds,
661
+ memberAliases,
656
662
  }));
657
663
  }
658
664
 
@@ -662,7 +668,7 @@ export function getSubsystemPackageRegions(
662
668
  */
663
669
  export interface BoundaryLayoutGroup {
664
670
  id: string;
665
- memberIds: string[];
671
+ memberAliases: string[];
666
672
  /** When set, this group is a child of another boundary group. */
667
673
  parentId?: string;
668
674
  region: SubsystemProcessRegion;
@@ -672,7 +678,7 @@ function keepRegion(
672
678
  r: SubsystemProcessRegion,
673
679
  showSingletons: boolean,
674
680
  ): boolean {
675
- return showSingletons || r.memberIds.length >= 2;
681
+ return showSingletons || r.memberAliases.length >= 2;
676
682
  }
677
683
 
678
684
  /**
@@ -689,7 +695,7 @@ export function buildBoundaryLayoutGroups(
689
695
  ): BoundaryLayoutGroup[] {
690
696
  const showSingletons = opts.showSingletonFrames === true;
691
697
  const packageMode = opts.packageFrames ?? 'multi-repo';
692
- const byId = new Map(doc.components.map((c) => [c.id, c]));
698
+ const byAlias = new Map(doc.components.map((c) => [c.alias, c]));
693
699
 
694
700
  const allPackages = getSubsystemPackageRegions(doc);
695
701
  const packageEligible =
@@ -714,8 +720,8 @@ export function buildBoundaryLayoutGroups(
714
720
  const moduleGroups: BoundaryLayoutGroup[] = modules.map((r) => {
715
721
  const processesOfMembers = new Set<string>();
716
722
  const packagesOfMembers = new Set<string>();
717
- for (const id of r.memberIds) {
718
- const c = byId.get(id);
723
+ for (const alias of r.memberAliases) {
724
+ const c = byAlias.get(alias);
719
725
  const p = c?.process?.trim();
720
726
  if (p) processesOfMembers.add(p);
721
727
  const pkg = c ? componentPackageKey(c) : undefined;
@@ -732,7 +738,7 @@ export function buildBoundaryLayoutGroups(
732
738
  }
733
739
  return {
734
740
  id: moduleGroupNodeId(r.key),
735
- memberIds: [...r.memberIds],
741
+ memberAliases: [...r.memberAliases],
736
742
  parentId,
737
743
  region: r,
738
744
  };
@@ -742,15 +748,15 @@ export function buildBoundaryLayoutGroups(
742
748
  const processId = processGroupNodeId(r.key);
743
749
  const nestedModules = moduleGroups.filter((m) => m.parentId === processId);
744
750
  const nestedModuleKeys = new Set(nestedModules.map((m) => m.region.key));
745
- const directLeaves = r.memberIds.filter((id) => {
746
- const mod = byId.get(id)?.module?.trim();
751
+ const directLeaves = r.memberAliases.filter((alias) => {
752
+ const mod = byAlias.get(alias)?.module?.trim();
747
753
  if (!mod) return true;
748
754
  return !nestedModuleKeys.has(mod);
749
755
  });
750
756
 
751
757
  const packagesOfMembers = new Set<string>();
752
- for (const id of r.memberIds) {
753
- const c = byId.get(id);
758
+ for (const alias of r.memberAliases) {
759
+ const c = byAlias.get(alias);
754
760
  const pkg = c ? componentPackageKey(c) : undefined;
755
761
  if (pkg) packagesOfMembers.add(pkg);
756
762
  }
@@ -762,7 +768,7 @@ export function buildBoundaryLayoutGroups(
762
768
 
763
769
  return {
764
770
  id: processId,
765
- memberIds: [...nestedModules.map((m) => m.id), ...directLeaves],
771
+ memberAliases: [...nestedModules.map((m) => m.id), ...directLeaves],
766
772
  parentId,
767
773
  region: r,
768
774
  };
@@ -774,15 +780,15 @@ export function buildBoundaryLayoutGroups(
774
780
  const nestedModules = moduleGroups.filter((m) => m.parentId === packageId);
775
781
  const claimed = new Set<string>();
776
782
  for (const p of nestedProcesses) {
777
- for (const id of p.region.memberIds) claimed.add(id);
783
+ for (const alias of p.region.memberAliases) claimed.add(alias);
778
784
  }
779
785
  for (const m of nestedModules) {
780
- for (const id of m.region.memberIds) claimed.add(id);
786
+ for (const alias of m.region.memberAliases) claimed.add(alias);
781
787
  }
782
- const directLeaves = r.memberIds.filter((id) => !claimed.has(id));
788
+ const directLeaves = r.memberAliases.filter((alias) => !claimed.has(alias));
783
789
  return {
784
790
  id: packageId,
785
- memberIds: [
791
+ memberAliases: [
786
792
  ...nestedProcesses.map((p) => p.id),
787
793
  ...nestedModules.map((m) => m.id),
788
794
  ...directLeaves,
@@ -1209,7 +1215,7 @@ export function convertSubsystemToNodes(
1209
1215
  ? processGroupNodeId(processKey)
1210
1216
  : undefined;
1211
1217
  nodes.push({
1212
- id: c.id,
1218
+ id: c.alias,
1213
1219
  type: 'subsystem-component',
1214
1220
  ...(parentId ? { parentId } : {}),
1215
1221
  position: { x: PAD + col * COL_W, y: cursorY + row * ROW_H },
@@ -1245,18 +1251,18 @@ export function convertSubsystemToGroups(
1245
1251
 
1246
1252
  /**
1247
1253
  * Convert a subsystem graph document into React Flow edges. Edges whose target
1248
- * is an external label (not a component id) point at a synthetic stub so the
1254
+ * is an external label (not a component alias) point at a synthetic stub so the
1249
1255
  * relationship is visible without a member node.
1250
1256
  */
1251
1257
  export function convertSubsystemToEdges(doc: SubsystemModelDocument): SubsystemGraphEdge[] {
1252
- const compIds = new Set(doc.components.map((c) => c.id));
1258
+ const compAliases = new Set(doc.components.map((c) => c.alias));
1253
1259
  const edges: SubsystemGraphEdge[] = [];
1254
1260
 
1255
1261
  for (const e of deriveGraphEdges(doc)) {
1256
1262
  const color = MECHANISM_COLOR[e.mechanism];
1257
1263
  const style = MECHANISM_STYLE[e.mechanism];
1258
1264
  // If `to` is a real component, connect directly; otherwise point at a stub node.
1259
- const isExternal = !compIds.has(e.to);
1265
+ const isExternal = !compAliases.has(e.to);
1260
1266
  const targetId = isExternal ? `external:${e.to}` : e.to;
1261
1267
 
1262
1268
  edges.push({
@@ -1282,8 +1288,8 @@ export function subsystemGraphLayoutKey(
1282
1288
  doc: Pick<SubsystemModelDocument, 'components' | 'relations' | 'walkthroughs'>,
1283
1289
  ): string {
1284
1290
  const components = doc.components
1285
- .map(({ id, purl, name, symbol, construct, file, purpose, process, module }) =>
1286
- [id, purl, name, symbol ?? '', construct, file, purpose ?? '', process ?? '', module ?? ''].join('\0'))
1291
+ .map(({ alias, purl, name, symbol, construct, file, purpose, process, module }) =>
1292
+ [alias, purl, name, symbol ?? '', construct, file, purpose ?? '', process ?? '', module ?? ''].join('\0'))
1287
1293
  .sort()
1288
1294
  .join('\n');
1289
1295
  const edgeKey = deriveGraphEdges(doc)
@@ -1326,7 +1332,7 @@ export async function buildSubsystemGraph(
1326
1332
  const layoutGroups = buildBoundaryLayoutGroups(doc, frameOpts);
1327
1333
  const regions = layoutGroups.map((g) => g.region);
1328
1334
  const regionIds = new Set(layoutGroups.map((g) => g.id));
1329
- const byId = new Map(doc.components.map((c) => [c.id, c]));
1335
+ const byAlias = new Map(doc.components.map((c) => [c.alias, c]));
1330
1336
 
1331
1337
  // Resolve leaf parentIds against kept frames: module → process → package.
1332
1338
  // Stamp package parents here (not in convertSubsystemToNodes) so single-repo
@@ -1335,7 +1341,7 @@ export async function buildSubsystemGraph(
1335
1341
  if (n.type !== 'subsystem-component') continue;
1336
1342
  const parentId = (n as { parentId?: string }).parentId;
1337
1343
  if (parentId && regionIds.has(parentId)) continue;
1338
- const comp = (n.data as SubsystemGraphNodeData).component ?? byId.get(n.id);
1344
+ const comp = (n.data as SubsystemGraphNodeData).component ?? byAlias.get(n.id);
1339
1345
  const processKey = comp?.process?.trim();
1340
1346
  const processId = processKey ? processGroupNodeId(processKey) : undefined;
1341
1347
  if (processId && regionIds.has(processId)) {
@@ -1353,10 +1359,10 @@ export async function buildSubsystemGraph(
1353
1359
 
1354
1360
  // External edge targets that aren't real components → create stub nodes so
1355
1361
  // cross-package edges have something to land on.
1356
- const realIds = new Set(doc.components.map((c) => c.id));
1362
+ const realAliases = new Set(doc.components.map((c) => c.alias));
1357
1363
  const externalIds: string[] = [];
1358
1364
  for (const e of deriveGraphEdges(doc)) {
1359
- if (!realIds.has(e.to)) {
1365
+ if (!realAliases.has(e.to)) {
1360
1366
  const extId = `external:${e.to}`;
1361
1367
  if (!externalIds.includes(extId)) externalIds.push(extId);
1362
1368
  }
@@ -1372,7 +1378,7 @@ export async function buildSubsystemGraph(
1372
1378
  height: 60,
1373
1379
  data: {
1374
1380
  component: {
1375
- id: extId,
1381
+ alias: extId,
1376
1382
  name: label,
1377
1383
  construct: 'external',
1378
1384
  purl: 'external',
@@ -1416,7 +1422,7 @@ export async function buildSubsystemGraph(
1416
1422
  edgeLabels: showEdgeLabels === false ? { enabled: false } : { enabled: true, placement: 'CENTER' },
1417
1423
  groups: layoutGroups.map((g) => ({
1418
1424
  id: g.id,
1419
- memberIds: g.memberIds,
1425
+ memberIds: g.memberAliases,
1420
1426
  parentId: g.parentId,
1421
1427
  minWidth: g.region.kind === 'module' ? moduleMinWidthForBadge(g.region.label) : undefined,
1422
1428
  })),
@@ -69,14 +69,14 @@ function breakWords(s: string): string {
69
69
 
70
70
  export interface SubsystemGraphCallbacks {
71
71
  /** Click a component — open its file/entry point. */
72
- onSelect?: (componentId: string) => void;
72
+ onSelect?: (componentAlias: string) => void;
73
73
  /** Click the filename badge — open that component's file in the drawer
74
74
  * (same path as clicking the file link in the declaration panel). */
75
- onOpenFile?: (componentId: string) => void;
75
+ onOpenFile?: (componentAlias: string) => void;
76
76
  /** Click an edge (or its label) — select the relationship. */
77
77
  onEdgeSelect?: (edgeId: string) => void;
78
78
  /** Hover a component (null on leave) — associates it with the file tree. */
79
- onHover?: (componentId: string | null) => void;
79
+ onHover?: (componentAlias: string | null) => void;
80
80
  /** Upper bound for node width; nodes grow with content up to this, then wrap. */
81
81
  maxNodeWidth?: number;
82
82
  }
@@ -131,7 +131,7 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
131
131
  <div
132
132
  onMouseEnter={() => {
133
133
  setHover(true);
134
- SUBSYSTEM_CALLBACKS.onHover?.(c.id);
134
+ SUBSYSTEM_CALLBACKS.onHover?.(c.alias);
135
135
  }}
136
136
  onMouseLeave={() => {
137
137
  setHover(false);
@@ -139,7 +139,7 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
139
139
  }}
140
140
  onClick={(e) => {
141
141
  e.stopPropagation();
142
- SUBSYSTEM_CALLBACKS.onSelect?.(c.id);
142
+ SUBSYSTEM_CALLBACKS.onSelect?.(c.alias);
143
143
  }}
144
144
  style={{
145
145
  position: 'relative',
@@ -275,7 +275,7 @@ export function SubsystemComponentNode(props: NodeProps<Node<SubsystemGraphNodeD
275
275
  onClick={(e) => {
276
276
  // Open the file directly; don't also toggle node selection.
277
277
  e.stopPropagation();
278
- SUBSYSTEM_CALLBACKS.onOpenFile?.(c.id);
278
+ SUBSYSTEM_CALLBACKS.onOpenFile?.(c.alias);
279
279
  }}
280
280
  >
281
281
  {c.file.split('/').pop()}