@principal-ai/subsystems-react 0.53.6 → 0.54.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 (104) hide show
  1. package/dist/index.d.ts +8 -2
  2. package/dist/index.d.ts.map +1 -1
  3. package/dist/index.js +3 -0
  4. package/dist/index.js.map +1 -1
  5. package/dist/pierre/PierreTrailCodeView.d.ts.map +1 -1
  6. package/dist/pierre/PierreTrailCodeView.js +7 -5
  7. package/dist/pierre/PierreTrailCodeView.js.map +1 -1
  8. package/dist/stories/Subsystem/ComponentGraph/fixtures.d.ts.map +1 -1
  9. package/dist/stories/Subsystem/ComponentGraph/fixtures.js +1 -2
  10. package/dist/stories/Subsystem/ComponentGraph/fixtures.js.map +1 -1
  11. package/dist/subsystem/C4Graph.d.ts +144 -9
  12. package/dist/subsystem/C4Graph.d.ts.map +1 -1
  13. package/dist/subsystem/C4Graph.js +924 -148
  14. package/dist/subsystem/C4Graph.js.map +1 -1
  15. package/dist/subsystem/C4NodeCard.d.ts +22 -1
  16. package/dist/subsystem/C4NodeCard.d.ts.map +1 -1
  17. package/dist/subsystem/C4NodeCard.js +10 -2
  18. package/dist/subsystem/C4NodeCard.js.map +1 -1
  19. package/dist/subsystem/ComponentDeclaration.d.ts +5 -4
  20. package/dist/subsystem/ComponentDeclaration.d.ts.map +1 -1
  21. package/dist/subsystem/ComponentDeclaration.js +12 -4
  22. package/dist/subsystem/ComponentDeclaration.js.map +1 -1
  23. package/dist/subsystem/ConstructsCatalog.d.ts.map +1 -1
  24. package/dist/subsystem/ConstructsCatalog.js +10 -7
  25. package/dist/subsystem/ConstructsCatalog.js.map +1 -1
  26. package/dist/subsystem/GraphSidebar.d.ts +71 -0
  27. package/dist/subsystem/GraphSidebar.d.ts.map +1 -0
  28. package/dist/subsystem/GraphSidebar.js +145 -0
  29. package/dist/subsystem/GraphSidebar.js.map +1 -0
  30. package/dist/subsystem/GraphWithSidebar.d.ts +24 -0
  31. package/dist/subsystem/GraphWithSidebar.d.ts.map +1 -0
  32. package/dist/subsystem/GraphWithSidebar.js +25 -0
  33. package/dist/subsystem/GraphWithSidebar.js.map +1 -0
  34. package/dist/subsystem/SubsystemComponentGraph.d.ts +33 -0
  35. package/dist/subsystem/SubsystemComponentGraph.d.ts.map +1 -1
  36. package/dist/subsystem/SubsystemComponentGraph.js +364 -411
  37. package/dist/subsystem/SubsystemComponentGraph.js.map +1 -1
  38. package/dist/subsystem/TrailStepsNav.d.ts +48 -0
  39. package/dist/subsystem/TrailStepsNav.d.ts.map +1 -0
  40. package/dist/subsystem/TrailStepsNav.js +189 -0
  41. package/dist/subsystem/TrailStepsNav.js.map +1 -0
  42. package/dist/subsystem/TrailsPanel.d.ts.map +1 -1
  43. package/dist/subsystem/TrailsPanel.js +3 -4
  44. package/dist/subsystem/TrailsPanel.js.map +1 -1
  45. package/dist/subsystem/__fixtures__/c4Drilldown.d.ts +151 -0
  46. package/dist/subsystem/__fixtures__/c4Drilldown.d.ts.map +1 -0
  47. package/dist/subsystem/__fixtures__/c4Drilldown.js +783 -0
  48. package/dist/subsystem/__fixtures__/c4Drilldown.js.map +1 -0
  49. package/dist/subsystem/c4.d.ts +39 -8
  50. package/dist/subsystem/c4.d.ts.map +1 -1
  51. package/dist/subsystem/c4.js.map +1 -1
  52. package/dist/subsystem/declarationRef.d.ts +26 -18
  53. package/dist/subsystem/declarationRef.d.ts.map +1 -1
  54. package/dist/subsystem/declarationRef.js +9 -4
  55. package/dist/subsystem/declarationRef.js.map +1 -1
  56. package/dist/subsystem/model.d.ts +19 -1
  57. package/dist/subsystem/model.d.ts.map +1 -1
  58. package/dist/subsystem/model.js +12 -2
  59. package/dist/subsystem/model.js.map +1 -1
  60. package/dist/subsystem/nodes.d.ts.map +1 -1
  61. package/dist/subsystem/nodes.js +24 -4
  62. package/dist/subsystem/nodes.js.map +1 -1
  63. package/dist/subsystem/trailBrief.d.ts.map +1 -1
  64. package/dist/subsystem/trailBrief.js +6 -1
  65. package/dist/subsystem/trailBrief.js.map +1 -1
  66. package/package.json +3 -3
  67. package/src/index.ts +9 -2
  68. package/src/pierre/PierreTrailCodeView.tsx +9 -13
  69. package/src/stories/Subsystem/C4Graph/C4ToComponentDrill.stories.tsx +496 -0
  70. package/src/stories/Subsystem/ComponentGraph/AnimatedProgression.stories.tsx +7 -7
  71. package/src/stories/Subsystem/ComponentGraph/Constructs.stories.tsx +19 -25
  72. package/src/stories/Subsystem/ComponentGraph/DetailPanel.stories.tsx +8 -10
  73. package/src/stories/Subsystem/ComponentGraph/Issues.stories.tsx +19 -19
  74. package/src/stories/Subsystem/ComponentGraph/ProposedTrails.stories.tsx +6 -6
  75. package/src/stories/Subsystem/ComponentGraph/TrailAutoplay.stories.tsx +7 -7
  76. package/src/stories/Subsystem/ComponentGraph/Trails.stories.tsx +27 -30
  77. package/src/stories/Subsystem/ComponentGraph/fixtures.ts +1 -2
  78. package/src/stories/Subsystem/ConstructsCatalog.stories.tsx +10 -13
  79. package/src/stories/Subsystem/EgoGraph/EgoGraph.stories.tsx +3 -4
  80. package/src/stories/Subsystem/Sidebar/GraphSidebar.stories.tsx +206 -0
  81. package/src/subsystem/C4Graph.groups.test.ts +96 -2
  82. package/src/subsystem/C4Graph.tsx +1377 -165
  83. package/src/subsystem/C4NodeCard.test.ts +1 -2
  84. package/src/subsystem/C4NodeCard.tsx +24 -2
  85. package/src/subsystem/ComponentDeclaration.tsx +18 -8
  86. package/src/subsystem/ConstructsCatalog.tsx +23 -8
  87. package/src/subsystem/GraphSidebar.test.tsx +152 -0
  88. package/src/subsystem/GraphSidebar.tsx +300 -0
  89. package/src/subsystem/GraphWithSidebar.test.tsx +78 -0
  90. package/src/subsystem/GraphWithSidebar.tsx +60 -0
  91. package/src/subsystem/SubsystemComponentGraph.tsx +284 -370
  92. package/src/subsystem/TrailStepsNav.test.tsx +179 -0
  93. package/src/subsystem/TrailStepsNav.tsx +372 -0
  94. package/src/subsystem/TrailsPanel.tsx +3 -4
  95. package/src/subsystem/__fixtures__/c4Drilldown.test.ts +338 -0
  96. package/src/subsystem/__fixtures__/c4Drilldown.ts +847 -0
  97. package/src/subsystem/c4.ts +41 -8
  98. package/src/subsystem/declarationRef.ts +28 -19
  99. package/src/subsystem/model.test.ts +32 -12
  100. package/src/subsystem/model.ts +30 -1
  101. package/src/subsystem/nodes.group.test.tsx +82 -0
  102. package/src/subsystem/nodes.tsx +30 -2
  103. package/src/subsystem/trailBrief.test.ts +2 -2
  104. package/src/subsystem/trailBrief.ts +6 -1
@@ -0,0 +1,338 @@
1
+ import { describe, expect, test } from 'bun:test';
2
+ import {
3
+ CLI,
4
+ DISK,
5
+ DRILLDOWN_C4_MODEL,
6
+ DRILLDOWN_COMPONENTS,
7
+ DRILLDOWN_DOC,
8
+ DRILLDOWN_EDGES,
9
+ DRILLDOWN_MODEL_SOURCE,
10
+ DRILLDOWN_MEMBERS,
11
+ DRILLDOWN_TRAILS,
12
+ GITHUB,
13
+ HOST,
14
+ RENDERER,
15
+ SITE,
16
+ deriveCodeProcessKeys,
17
+ deriveElementMembers,
18
+ deriveLitC4Edges,
19
+ deriveTrailPlayerElements,
20
+ drilldownMembersOf,
21
+ rollupEdgeTrailRefs,
22
+ } from './c4Drilldown';
23
+
24
+ /**
25
+ * The fixture is the test case for the C4 → component-graph drill-down,
26
+ * grounded in the real, verified model sg-1788913589907-ieokymqg9. Two kinds
27
+ * of truth are asserted:
28
+ *
29
+ * fidelity — the document half is a faithful copy of the real model, so the
30
+ * test case is genuinely "pulled from a reviewed subsystem model";
31
+ *
32
+ * consistency — the assumed-accepted C4 layer's claims (process keys,
33
+ * memberships, edge provenance) are exactly what the document supports,
34
+ * which is also the spec for the eventual `buildC4Model` rollup.
35
+ */
36
+
37
+ const membersOf = (id: string) => drilldownMembersOf(id);
38
+
39
+ /** Flat list of the steps a C4 edge's refs point at, in document order. */
40
+ function stepsOf(edge: (typeof DRILLDOWN_EDGES)[number]) {
41
+ const byId = new Map(DRILLDOWN_TRAILS.map((t) => [t.id, t]));
42
+ return (edge.trailRefs ?? []).flatMap((ref) => {
43
+ const trail = byId.get(ref.trailId);
44
+ if (!trail) return [];
45
+ return ref.stepIndices.map((i) => trail.steps[i]).filter(Boolean);
46
+ });
47
+ }
48
+
49
+ function sortRefs(refs: ReturnType<typeof rollupEdgeTrailRefs>) {
50
+ return [...refs]
51
+ .sort((a, b) => a.trailId.localeCompare(b.trailId))
52
+ .map((r) => ({ ...r, stepIndices: [...r.stepIndices].sort((x, y) => x - y) }));
53
+ }
54
+
55
+ // ---------------------------------------------------------------------------
56
+ // Fidelity to the real reviewed model.
57
+ // ---------------------------------------------------------------------------
58
+
59
+ describe('c4Drilldown fixture — faithful copy of sg-1788913589907-ieokymqg9', () => {
60
+ test('doc metadata matches the real model', () => {
61
+ expect(DRILLDOWN_DOC.title).toBe(DRILLDOWN_MODEL_SOURCE.title);
62
+ expect(DRILLDOWN_MODEL_SOURCE.repo).toBe('pkg:github/principal-ai/subsystem-modeling');
63
+ expect(DRILLDOWN_COMPONENTS).toHaveLength(19);
64
+ expect(DRILLDOWN_TRAILS).toHaveLength(3);
65
+ });
66
+
67
+ test('component alias → process map is exactly the real model\'s', () => {
68
+ const expected: Array<[string, string | undefined]> = [
69
+ ['site-gist-page', 'site/web'],
70
+ ['gist-load', 'site/web'],
71
+ ['to-portable', 'site/web'],
72
+ ['is-portable', 'subsystems-core/types'],
73
+ ['portable-doc', 'subsystems-core/types'],
74
+ ['host-binding', 'subsystems-core/types'],
75
+ ['github-gists-api', 'external/github'],
76
+ ['react-graph', 'subsystems-react/ui'],
77
+ ['cli-create', 'principal-studio-cli'],
78
+ ['studio-create', 'subsystems-studio/host'],
79
+ ['studio-update', 'subsystems-studio/host'],
80
+ ['studio-get', 'subsystems-studio/host'],
81
+ ['model-disk-store', 'local/disk'],
82
+ ['index-entry', 'subsystems-studio/host'],
83
+ ['studio-list', 'subsystems-studio/renderer'],
84
+ ['studio-model-view', 'subsystems-studio/renderer'],
85
+ ['open-tab', 'subsystems-studio/host'],
86
+ ['gist-share-ui', 'proposed/studio-gist'],
87
+ ['gist-ref', 'proposed/studio-gist'],
88
+ ];
89
+ expect(
90
+ DRILLDOWN_COMPONENTS.map((c) => [c.alias, c.process] as [string, string | undefined]),
91
+ ).toEqual(expected);
92
+ });
93
+
94
+ test('trail ids, titles, and step counts match the real model', () => {
95
+ expect(
96
+ DRILLDOWN_TRAILS.map((t) => [t.id, t.title, t.steps.length]),
97
+ ).toEqual([
98
+ ['tl-site-view-gist', 'Someone opens the shared gist on the site', 4],
99
+ ['tl-persist-local-store', 'Persist a local model to ~/.principal/subsystem-models', 4],
100
+ ['tl-proposed-share-gist', 'Proposed: share local model as gist (create or update)', 5],
101
+ ]);
102
+ });
103
+
104
+ test('model-level verification state is carried through', () => {
105
+ expect(DRILLDOWN_MODEL_SOURCE.verifiedCount).toBe(15);
106
+ });
107
+ });
108
+
109
+ // ---------------------------------------------------------------------------
110
+ // Consistency of the assumed-accepted C4 layer.
111
+ // ---------------------------------------------------------------------------
112
+
113
+ describe('c4Drilldown fixture — C4 layer agrees with the document', () => {
114
+ test('every trail step endpoints a component alias that exists', () => {
115
+ const aliases = new Set(DRILLDOWN_COMPONENTS.map((c) => c.alias));
116
+ for (const trail of DRILLDOWN_TRAILS) {
117
+ for (const step of trail.steps) {
118
+ expect(aliases.has(step.from), `${trail.id} step .from`).toBe(true);
119
+ expect(aliases.has(step.to), `${trail.id} step .to`).toBe(true);
120
+ }
121
+ }
122
+ });
123
+
124
+ test('every member of every C4 element is a real component alias', () => {
125
+ const aliases = new Set(DRILLDOWN_COMPONENTS.map((c) => c.alias));
126
+ for (const element of DRILLDOWN_C4_MODEL.nodes) {
127
+ const members = element.kind === 'container'
128
+ ? (DRILLDOWN_MEMBERS[element.id] ?? [])
129
+ : (element.members ?? []);
130
+ for (const alias of members) {
131
+ expect(aliases.has(alias), `${element.id} member ${alias}`).toBe(true);
132
+ }
133
+ }
134
+ });
135
+
136
+ test('container memberships derive exactly from their process claims', () => {
137
+ const byElement = deriveElementMembers(DRILLDOWN_C4_MODEL.nodes, DRILLDOWN_COMPONENTS);
138
+ expect([...byElement.entries()].sort((a, b) => a[0].localeCompare(b[0]))).toEqual([
139
+ ['component:cli-command', ['cli-create']],
140
+ ['component:host-store', ['studio-create', 'studio-update', 'studio-get', 'index-entry']],
141
+ ['component:host-tabs', ['open-tab']],
142
+ ['component:renderer-views', ['studio-list', 'studio-model-view']],
143
+ ['component:site-gist', ['site-gist-page', 'gist-load', 'to-portable']],
144
+ [CLI, ['cli-create']],
145
+ [SITE, ['site-gist-page', 'gist-load', 'to-portable']],
146
+ [HOST, ['studio-create', 'studio-update', 'studio-get', 'index-entry', 'open-tab']],
147
+ [RENDERER, ['studio-list', 'studio-model-view']],
148
+ [DISK, ['model-disk-store']],
149
+ [GITHUB, ['github-gists-api']],
150
+ ]);
151
+ });
152
+
153
+ test('top-level memberships split all claimed components disjointly', () => {
154
+ const seen = new Set<string>();
155
+ for (const id of [HOST, RENDERER, CLI, SITE, GITHUB, DISK]) {
156
+ for (const alias of drilldownMembersOf(id) ?? []) {
157
+ expect(seen.has(alias), `${id} reuses ${alias}`).toBe(false);
158
+ seen.add(alias);
159
+ }
160
+ }
161
+ // 19 components total; the 6 library/proposed ones are intentionally
162
+ // claimed by nothing (asserted next), so 13 sit inside the C4 boxes.
163
+ expect(seen.size).toBe(13);
164
+ const unclaimed = DRILLDOWN_COMPONENTS.map((c) => c.alias).filter((a) => !seen.has(a));
165
+ expect(unclaimed.sort()).toEqual([
166
+ 'gist-ref',
167
+ 'gist-share-ui',
168
+ 'host-binding',
169
+ 'is-portable',
170
+ 'portable-doc',
171
+ 'react-graph',
172
+ ]);
173
+ });
174
+
175
+ test('no container claims a library or proposed process — the anti-"library" rule', () => {
176
+ const claimed = new Set(
177
+ DRILLDOWN_C4_MODEL.nodes
178
+ .filter((n) => n.kind === 'container')
179
+ .map((n) => (n as { process?: string }).process)
180
+ .filter((p): p is string => !!p),
181
+ );
182
+ for (const excluded of ['subsystems-core/types', 'subsystems-react/ui', 'proposed/studio-gist']) {
183
+ expect(claimed.has(excluded), `claims ${excluded}`).toBe(false);
184
+ }
185
+ // …and every claim it DOES make exists in the document.
186
+ for (const process of claimed) {
187
+ expect(
188
+ DRILLDOWN_COMPONENTS.some((c) => c.process === process),
189
+ `claim ${process}`,
190
+ ).toBe(true);
191
+ }
192
+ });
193
+
194
+ test('the rollup recomputes exactly the hand-authored trailRefs', () => {
195
+ for (const edge of DRILLDOWN_EDGES) {
196
+ const computed = rollupEdgeTrailRefs({
197
+ source: edge.source,
198
+ target: edge.target,
199
+ trails: DRILLDOWN_TRAILS,
200
+ membersOf,
201
+ });
202
+ expect(sortRefs(computed), `${edge.id} rollup`).toEqual(
203
+ sortRefs(edge.trailRefs ?? []),
204
+ );
205
+ }
206
+ });
207
+
208
+ test('count is the total referenced steps; mechanisms their distinct verbs', () => {
209
+ for (const edge of DRILLDOWN_EDGES) {
210
+ if ((edge.trailRefs ?? []).length === 0) continue;
211
+ const steps = stepsOf(edge);
212
+ expect(edge.count, `${edge.id} count`).toBe(steps.length);
213
+ const verbs = [...new Set(steps.map((s) => s.mechanism))];
214
+ expect(edge.mechanisms, `${edge.id} mechanisms`).toEqual(verbs);
215
+ }
216
+ });
217
+
218
+ test('the proposed trail contributes no accepted edges (its components are not claimed)', () => {
219
+ // The proposed gist-share walkthrough touches host/site/github, but its
220
+ // unclaimed proposed components keep it out of the accepted fold.
221
+ for (const edge of DRILLDOWN_EDGES) {
222
+ for (const ref of edge.trailRefs ?? []) {
223
+ expect(ref.trailId).not.toBe('tl-proposed-share-gist');
224
+ }
225
+ }
226
+ });
227
+
228
+ test('the renderer has no incident edges — only the proposed trail would attach lines to it', () => {
229
+ const incident = DRILLDOWN_EDGES.filter(
230
+ (e) => e.source === RENDERER || e.target === RENDERER,
231
+ );
232
+ expect(incident).toEqual([]);
233
+ });
234
+
235
+ test('trail players: the containers/externals/parts each walkthrough actually touches', () => {
236
+ const playersOf = (id: string) =>
237
+ deriveTrailPlayerElements(
238
+ DRILLDOWN_TRAILS.find((t) => t.id === id)!.steps,
239
+ DRILLDOWN_MEMBERS,
240
+ );
241
+ const sortIds = (s: Set<string>) => [...s].sort();
242
+
243
+ // Site viewer: site page/loader parts + the GitHub API. The site CONTAINER
244
+ // is a player because its process claim owns the aliases — the visible
245
+ // card lights up at container view, and stays lit when opened (its parts
246
+ // are players too).
247
+ expect(sortIds(playersOf('tl-site-view-gist'))).toEqual(
248
+ sortIds(new Set([SITE, 'component:site-gist', GITHUB])),
249
+ );
250
+ // CLI create → host store → disk + tab open: both the CLI and host
251
+ // containers AND the authored parts nested under them play.
252
+ expect(sortIds(playersOf('tl-persist-local-store'))).toEqual(
253
+ sortIds(
254
+ new Set([
255
+ CLI,
256
+ 'component:cli-command',
257
+ HOST,
258
+ 'component:host-store',
259
+ 'component:host-tabs',
260
+ DISK,
261
+ ]),
262
+ ),
263
+ );
264
+ // Proposed share: host + site parts + GitHub; no container for the
265
+ // unclaimed proposed aliases (gist-share-ui, gist-ref) — they light
266
+ // nothing, exactly like the anti-library stance.
267
+ expect(sortIds(playersOf('tl-proposed-share-gist'))).toEqual(
268
+ sortIds(new Set([HOST, 'component:host-store', SITE, 'component:site-gist', GITHUB])),
269
+ );
270
+ // A step between two unclaimed aliases (library/proposed components)
271
+ // contributes no players at all — the dim set is the whole model.
272
+ expect(
273
+ playersOf('tl-site-view-gist').size,
274
+ ).toBeGreaterThan(0);
275
+ expect(
276
+ deriveTrailPlayerElements(
277
+ [{ from: 'react-graph', to: 'gist-ref' }],
278
+ DRILLDOWN_MEMBERS,
279
+ ),
280
+ ).toEqual(new Set());
281
+ });
282
+
283
+ test('lit edges: only the lines that carry the focused trail / hovered step', () => {
284
+ const lit = (source: { trailId: string; stepIndex?: number }) =>
285
+ [...deriveLitC4Edges(source, DRILLDOWN_EDGES)].sort();
286
+
287
+ // Whole trail → every line its steps fold into.
288
+ expect(lit({ trailId: 'tl-persist-local-store' })).toEqual(['e:cli-host', 'e:host-disk']);
289
+ expect(lit({ trailId: 'tl-site-view-gist' })).toEqual(['e:site-github']);
290
+
291
+ // A boundary step lights exactly its folding line.
292
+ expect(lit({ trailId: 'tl-persist-local-store', stepIndex: 0 })).toEqual(['e:cli-host']);
293
+ expect(lit({ trailId: 'tl-persist-local-store', stepIndex: 1 })).toEqual(['e:host-disk']);
294
+ expect(lit({ trailId: 'tl-site-view-gist', stepIndex: 1 })).toEqual(['e:site-github']);
295
+
296
+ // A step entirely internal to a container folds into NO line — lit is
297
+ // empty, so every edge dims: no drawn edge represents that hop.
298
+ expect(lit({ trailId: 'tl-persist-local-store', stepIndex: 2 })).toEqual([]); // studio-create → index-entry, both in HOST
299
+ expect(lit({ trailId: 'tl-site-view-gist', stepIndex: 0 })).toEqual([]); // site-gist-page → gist-load, both in SITE
300
+ expect(lit({ trailId: 'tl-site-view-gist', stepIndex: 3 })).toEqual([]); // site → react-graph, unclaimed target
301
+
302
+ // The proposed trail is outside the accepted fold: it lights no line.
303
+ expect(lit({ trailId: 'tl-proposed-share-gist' })).toEqual([]);
304
+ expect(lit({ trailId: 'tl-proposed-share-gist', stepIndex: 3 })).toEqual([]);
305
+ });
306
+
307
+ test('code process keys: exactly the processes the accepted element set backs with nothing', () => {
308
+ // An element backs a process by claiming the key (containers) or carrying
309
+ // a member whose component claims it (externals, L3 children). What
310
+ // nobody backs draws as a code boundary in the component graph — package
311
+ // chrome, not the solid runtime chrome a deployment unit wears.
312
+ const code = deriveCodeProcessKeys(DRILLDOWN_C4_MODEL.nodes, DRILLDOWN_COMPONENTS);
313
+ expect([...code].sort()).toEqual([
314
+ 'proposed/studio-gist',
315
+ 'subsystems-core/types',
316
+ 'subsystems-react/ui',
317
+ ]);
318
+
319
+ // Every process an accepted element covers keeps its runtime chrome:
320
+ // the four container-backed units plus the two store/external systems.
321
+ const runtime = [
322
+ 'external/github',
323
+ 'local/disk',
324
+ 'principal-studio-cli',
325
+ 'site/web',
326
+ 'subsystems-studio/host',
327
+ 'subsystems-studio/renderer',
328
+ ];
329
+ for (const key of runtime) expect(code.has(key)).toBe(false);
330
+
331
+ // And the set covers every distinct process in the model — nothing is
332
+ // decided by omission.
333
+ const all = new Set(
334
+ DRILLDOWN_COMPONENTS.map((c) => c.process).filter((p): p is string => Boolean(p)),
335
+ );
336
+ expect(new Set([...all].filter((k) => !code.has(k))).size).toBe(runtime.length);
337
+ });
338
+ });