@topogram/cli 0.3.48 → 0.3.50

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.
package/CHANGELOG.md CHANGED
@@ -53,11 +53,12 @@
53
53
  ## 0.3.11 - 2026-05-01
54
54
 
55
55
  - Add `domain` statement kind for grouping the spec by business slice
56
- (FIS, RNF, DrugTrac, etc.). Identifier prefix `dom_`. Required fields:
57
- `name`, `description`, `status`. Optional: `in_scope`, `out_of_scope`,
58
- `owners`, `parent_domain`, `aliases`. Validator enforces identifier
59
- prefix, scope-list shapes, owner refs (`actor`|`role`), parent_domain
60
- refs, and parent-domain cycle detection.
56
+ (order fulfillment, billing, support, reporting, etc.). Identifier
57
+ prefix `dom_`. Required fields: `name`, `description`, `status`.
58
+ Optional: `in_scope`, `out_of_scope`, `owners`, `parent_domain`,
59
+ `aliases`. Validator enforces identifier prefix, scope-list shapes,
60
+ owner refs (`actor`|`role`), parent_domain refs, and parent-domain
61
+ cycle detection.
61
62
  - Add optional singular `domain` field on `capability`, `entity`, `rule`,
62
63
  `verification`, `orchestration`, `operation`, and `decision`. Cross-kind
63
64
  validator hard-errors on unknown ids and wrong-kind references.
@@ -83,9 +84,9 @@
83
84
  with the `domain` row and the optional-field paragraph;
84
85
  `docs/topogram-workspace-layout.md` appends a "Domain organization"
85
86
  subsection.
86
- - New fixture `engine/tests/fixtures/domains/feedlot/` (3 domains,
87
- 10 capabilities, 12 entities, 4 cross-platform projections) and
88
- golden tests at `engine/tests/active/domain-kind.test.js`.
87
+ - New multi-domain fixture (3 domains, 10 capabilities, 12 entities, 4
88
+ cross-platform projections) and golden tests at
89
+ `engine/tests/active/domain-kind.test.js`.
89
90
 
90
91
  ### SDLC layer (Phase 2)
91
92
 
package/README.md CHANGED
@@ -6,7 +6,7 @@ The active product workflow is authoring-to-generated-app. Engine development sh
6
6
 
7
7
  ## Package Shape
8
8
 
9
- The engine is the publishable private CLI package:
9
+ The engine is the publishable CLI package:
10
10
 
11
11
  ```json
12
12
  {
@@ -17,7 +17,7 @@ The engine is the publishable private CLI package:
17
17
  }
18
18
  ```
19
19
 
20
- This lets source checkouts and private-package consumers call:
20
+ This lets source checkouts and package consumers call:
21
21
 
22
22
  ```bash
23
23
  topogram new ../my-app
@@ -111,7 +111,7 @@ topogram new ../todo-demo --template @topogram/template-todo
111
111
  topogram new ../todo-demo --template todo
112
112
  ```
113
113
 
114
- Catalog aliases resolve through the private catalog index at
114
+ Catalog aliases resolve through the public catalog index at
115
115
  `github:attebury/topograms/topograms.catalog.json`. The catalog is package
116
116
  backed; executable starter content still lives in template packages. Use
117
117
  `topogram catalog show <id>` to inspect an entry and get the correct `new` or
@@ -131,7 +131,7 @@ template update metadata while keeping normal check/generate behavior.
131
131
  Do not create generated projects under `engine/`. The CLI refuses paths inside the engine directory.
132
132
 
133
133
  Template pack authoring and trust policy are documented in `../docs/template-authoring.md`.
134
- Catalog layout and private access are documented in `../docs/catalog.md`.
134
+ Catalog layout and optional private-source access are documented in `../docs/catalog.md`.
135
135
  Projects created from executable templates include `.topogram-template-trust.json`;
136
136
  regenerate it with `topogram trust template` after reviewing copied
137
137
  `implementation/` code. Use `topogram template status` for the lifecycle
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@topogram/cli",
3
- "version": "0.3.48",
3
+ "version": "0.3.50",
4
4
  "description": "Topogram CLI for checking Topogram workspaces and generating app bundles.",
5
5
  "license": "Apache-2.0",
6
6
  "repository": {
@@ -194,6 +194,7 @@ function projectionSlice(graph, projectionId) {
194
194
  verification_targets: recommendedVerificationTargets(graph, [projectionId, ...capabilities, ...entities, ...shapes, ...components], {
195
195
  rationale: "Projection slices affect generated contract and runtime surfaces, so verification should follow the projection closure."
196
196
  }),
197
+ ui_agent_packet: uiAgentPacketForProjection(graph, projection),
197
198
  write_scope: buildDefaultWriteScope(),
198
199
  review_boundary: projection.reviewBoundary || {
199
200
  automation_class: "review_required",
@@ -286,6 +287,7 @@ function componentSlice(graph, componentId) {
286
287
  verification_targets: recommendedVerificationTargets(graph, verificationScope, {
287
288
  rationale: "Component changes affect every related projection — verification should follow the component contract closure."
288
289
  }),
290
+ ui_agent_packet: uiAgentPacketForComponent(graph, component, projections),
289
291
  write_scope: buildDefaultWriteScope(),
290
292
  review_boundary: {
291
293
  automation_class: "review_required",
@@ -331,6 +333,135 @@ function componentDependencyKind(dependency) {
331
333
  return prefix || null;
332
334
  }
333
335
 
336
+ function uiAgentPacketForProjection(graph, projection) {
337
+ if (!String(projection.platform || "").startsWith("ui_")) {
338
+ return null;
339
+ }
340
+ const sharedProjection = projection.platform === "ui_shared"
341
+ ? projection
342
+ : sharedUiProjectionFor(graph, projection);
343
+ const ownerProjection = sharedProjection || projection;
344
+ return {
345
+ type: "ui_agent_packet",
346
+ version: 1,
347
+ ownership: {
348
+ componentPlacement: "ui_shared",
349
+ designIntent: "ui_shared",
350
+ concreteSurfaceOwns: ["routes", "surface_hints"]
351
+ },
352
+ sharedProjection: sharedProjection
353
+ ? {
354
+ id: sharedProjection.id,
355
+ name: sharedProjection.name || sharedProjection.id
356
+ }
357
+ : null,
358
+ screens: (ownerProjection.uiScreens || []).map((screen) => ({
359
+ id: screen.id,
360
+ kind: screen.kind,
361
+ title: screen.title || screen.id
362
+ })),
363
+ routes: (projection.uiRoutes || []).map((route) => ({
364
+ screenId: route.screenId,
365
+ path: route.path
366
+ })),
367
+ components: (ownerProjection.uiComponents || []).map((usage) => componentUsagePacket(usage)),
368
+ design: designIntentPacket(ownerProjection),
369
+ requiredGates: uiRequiredGates(projection.id)
370
+ };
371
+ }
372
+
373
+ function uiAgentPacketForComponent(graph, component, projectionIds) {
374
+ const projectionSet = new Set(projectionIds);
375
+ const sourceUsages = [];
376
+ for (const projection of graph.byKind.projection || []) {
377
+ for (const usage of projection.uiComponents || []) {
378
+ if (usage.component?.id !== component.id) continue;
379
+ sourceUsages.push({
380
+ projection: {
381
+ id: projection.id,
382
+ platform: projection.platform,
383
+ ownership: projection.platform === "ui_shared" ? "owner" : "concrete"
384
+ },
385
+ usage: componentUsagePacket(usage),
386
+ design: designIntentPacket(projection)
387
+ });
388
+ projectionSet.add(projection.id);
389
+ }
390
+ }
391
+
392
+ return {
393
+ type: "ui_agent_packet",
394
+ version: 1,
395
+ ownership: {
396
+ componentContract: "component",
397
+ componentPlacement: "ui_shared",
398
+ concreteSurfacesInherit: true
399
+ },
400
+ component: {
401
+ id: component.id,
402
+ name: component.name || component.id,
403
+ category: component.category || null,
404
+ patterns: component.componentContract?.patterns || [],
405
+ regions: component.componentContract?.regions || [],
406
+ behaviors: component.componentContract?.behaviors || []
407
+ },
408
+ sourceUsages,
409
+ inheritedBy: [...projectionSet]
410
+ .filter((projectionId) => !sourceUsages.some((entry) => entry.projection.id === projectionId))
411
+ .sort(),
412
+ requiredGates: uiRequiredGates(null, component.id)
413
+ };
414
+ }
415
+
416
+ function sharedUiProjectionFor(graph, projection) {
417
+ for (const reference of projection.realizes || []) {
418
+ const candidate = (graph.byKind.projection || []).find((entry) => entry.id === reference.id);
419
+ if (candidate?.platform === "ui_shared") {
420
+ return candidate;
421
+ }
422
+ }
423
+ return null;
424
+ }
425
+
426
+ function componentUsagePacket(usage) {
427
+ return {
428
+ screenId: usage.screenId || null,
429
+ region: usage.region || null,
430
+ componentId: usage.component?.id || null,
431
+ dataBindings: (usage.dataBindings || []).map((binding) => ({
432
+ prop: binding.prop || null,
433
+ source: binding.source?.id || binding.source || null
434
+ })),
435
+ eventBindings: (usage.eventBindings || []).map((binding) => ({
436
+ event: binding.event || null,
437
+ action: binding.action || null,
438
+ target: binding.target?.id || binding.target || null
439
+ }))
440
+ };
441
+ }
442
+
443
+ function designIntentPacket(projection) {
444
+ return (projection.uiDesign || []).map((entry) => ({
445
+ key: entry.key,
446
+ role: entry.role,
447
+ value: entry.value
448
+ }));
449
+ }
450
+
451
+ function uiRequiredGates(projectionId = null, componentId = null) {
452
+ return [
453
+ { command: "topogram check", reason: "Validate shared UI ownership, taxonomy, references, and topology." },
454
+ {
455
+ command: `topogram component check${projectionId ? ` --projection ${projectionId}` : ""}${componentId ? ` --component ${componentId}` : ""}`,
456
+ reason: "Validate component placement, props, events, regions, and patterns."
457
+ },
458
+ {
459
+ command: `topogram component behavior${projectionId ? ` --projection ${projectionId}` : ""}${componentId ? ` --component ${componentId}` : ""}`,
460
+ reason: "Inspect behavior realizations and partial bindings before code changes."
461
+ }
462
+ ];
463
+ }
464
+
334
465
  function journeySlice(graph, journeyId) {
335
466
  const journey = getJourneyDoc(graph, journeyId);
336
467
  const capabilities = [...(journey.relatedCapabilities || [])].sort();
@@ -3,6 +3,7 @@
3
3
  import fs from "node:fs";
4
4
  import path from "node:path";
5
5
  import { createRequire } from "node:module";
6
+ import { UI_GENERATOR_RENDERED_COMPONENT_PATTERNS } from "../ui/taxonomy.js";
6
7
 
7
8
  /**
8
9
  * @typedef {Object} GeneratorManifest
@@ -23,6 +24,8 @@ import { createRequire } from "node:module";
23
24
  * @property {boolean} [planned]
24
25
  */
25
26
 
27
+ const RENDERED_COMPONENT_PATTERNS = [...UI_GENERATOR_RENDERED_COMPONENT_PATTERNS];
28
+
26
29
  /** @type {GeneratorManifest[]} */
27
30
  export const GENERATOR_MANIFESTS = [
28
31
  {
@@ -60,7 +63,7 @@ export const GENERATOR_MANIFESTS = [
60
63
  inputs: ["ui-web-contract"],
61
64
  outputs: ["web-app", "generation-coverage"],
62
65
  stack: { runtime: "browser", framework: "vanilla", language: "javascript" },
63
- capabilities: { routes: true, components: false, coverage: false },
66
+ capabilities: { routes: true, components: false, coverage: true },
64
67
  componentSupport: { patterns: [], behaviors: [], unsupported: "contract-only" },
65
68
  source: "bundled",
66
69
  profile: "vanilla"
@@ -76,7 +79,7 @@ export const GENERATOR_MANIFESTS = [
76
79
  stack: { runtime: "node", framework: "sveltekit", language: "typescript" },
77
80
  capabilities: { routes: true, components: true, coverage: true },
78
81
  componentSupport: {
79
- patterns: ["summary_stats", "board_view", "calendar_view", "resource_table", "data_grid_view"],
82
+ patterns: RENDERED_COMPONENT_PATTERNS,
80
83
  behaviors: ["selection", "sorting", "filtering", "search", "pagination", "bulk_action", "optimistic_update"],
81
84
  unsupported: "warning"
82
85
  },
@@ -94,7 +97,7 @@ export const GENERATOR_MANIFESTS = [
94
97
  stack: { runtime: "browser", framework: "react", language: "typescript" },
95
98
  capabilities: { routes: true, components: true, coverage: true },
96
99
  componentSupport: {
97
- patterns: ["summary_stats", "board_view", "calendar_view", "resource_table", "data_grid_view"],
100
+ patterns: RENDERED_COMPONENT_PATTERNS,
98
101
  behaviors: ["selection", "sorting", "filtering", "search", "pagination", "bulk_action", "optimistic_update"],
99
102
  unsupported: "warning"
100
103
  },
@@ -17,6 +17,7 @@ import {
17
17
  buildVerificationSummary,
18
18
  getDefaultEnvironmentProjections,
19
19
  resolveRuntimeTopology,
20
+ runtimeDemoUserId,
20
21
  runtimePorts,
21
22
  runtimeUrls
22
23
  } from "./shared.js";
@@ -104,7 +105,7 @@ function buildAppBundlePlan(graph, options = {}) {
104
105
  }
105
106
 
106
107
  function renderAppBundleEnvExample(plan) {
107
- const demo = plan.runtimeReference.demoEnv;
108
+ const demoUserId = runtimeDemoUserId(plan.runtimeReference);
108
109
  const databaseName = plan.runtimeReference.environment.databaseName || "topogram_app";
109
110
  const topology = {
110
111
  primaryApi: { port: plan.topology.components.find((component) => component.type === "api")?.port },
@@ -118,8 +119,8 @@ TOPOGRAM_ENVIRONMENT_PROFILE=${plan.profiles.environment}
118
119
  TOPOGRAM_DEPLOY_PROFILE=${plan.profiles.deployment}
119
120
 
120
121
  # Local runtime defaults
121
- ${plan.projections.api ? `SERVER_PORT=${ports.server}\n` : ""}${plan.projections.ui ? `WEB_PORT=${ports.web}\n` : ""}${plan.projections.api && plan.projections.ui ? `PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}\n` : ""}PUBLIC_TOPOGRAM_DEMO_USER_ID=${demo.userId}
122
- TOPOGRAM_DEMO_USER_ID=${demo.userId}
122
+ ${plan.projections.api ? `SERVER_PORT=${ports.server}\n` : ""}${plan.projections.ui ? `WEB_PORT=${ports.web}\n` : ""}${plan.projections.api && plan.projections.ui ? `PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}\n` : ""}PUBLIC_TOPOGRAM_DEMO_USER_ID=${demoUserId}
123
+ TOPOGRAM_DEMO_USER_ID=${demoUserId}
123
124
  ${plan.runtimeReference.environment.envExample || ""}
124
125
 
125
126
  # Smoke-test defaults
@@ -135,8 +136,8 @@ SERVER_PORT=${ports.server}
135
136
  WEB_PORT=${ports.web}
136
137
  DATABASE_URL=file:./var/${databaseName}.sqlite
137
138
  PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
138
- PUBLIC_TOPOGRAM_DEMO_USER_ID=${demo.userId}
139
- TOPOGRAM_DEMO_USER_ID=${demo.userId}
139
+ PUBLIC_TOPOGRAM_DEMO_USER_ID=${demoUserId}
140
+ TOPOGRAM_DEMO_USER_ID=${demoUserId}
140
141
  ${plan.runtimeReference.environment.envExample || ""}
141
142
  TOPOGRAM_SEED_DEMO=true
142
143
 
@@ -159,8 +160,8 @@ POSTGRES_PASSWORD=postgres
159
160
  DATABASE_URL=postgresql://\${POSTGRES_USER}@localhost:5432/${databaseName}
160
161
  DATABASE_ADMIN_URL=postgresql://\${POSTGRES_USER}@localhost:5432/postgres
161
162
  PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
162
- PUBLIC_TOPOGRAM_DEMO_USER_ID=${demo.userId}
163
- TOPOGRAM_DEMO_USER_ID=${demo.userId}
163
+ PUBLIC_TOPOGRAM_DEMO_USER_ID=${demoUserId}
164
+ TOPOGRAM_DEMO_USER_ID=${demoUserId}
164
165
  ${plan.runtimeReference.environment.envExample || ""}
165
166
  TOPOGRAM_SEED_DEMO=true
166
167
 
@@ -3,6 +3,7 @@ import {
3
3
  generateWebBundle,
4
4
  getDefaultEnvironmentProjections,
5
5
  resolveRuntimeTopology,
6
+ runtimeDemoUserId,
6
7
  runtimeUrls
7
8
  } from "./shared.js";
8
9
  import { getExampleImplementation } from "../../example-implementation.js";
@@ -75,13 +76,13 @@ function renderCompileCheckEnvExample(graph, options = {}) {
75
76
  if (dbProjection?.platform === "db_sqlite") {
76
77
  return `DATABASE_URL=./var/${runtimeReference.environment.databaseName || "topogram_app"}.sqlite
77
78
  PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
78
- PUBLIC_TOPOGRAM_DEMO_USER_ID=${runtimeReference.demoEnv.userId}
79
+ PUBLIC_TOPOGRAM_DEMO_USER_ID=${runtimeDemoUserId(runtimeReference)}
79
80
  ${runtimeReference.environment.envExample || ""}
80
81
  `;
81
82
  }
82
83
  return `DATABASE_URL=postgresql://postgres:postgres@localhost:5432/${runtimeReference.environment.databaseName || "topogram_app"}?schema=public
83
84
  PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
84
- PUBLIC_TOPOGRAM_DEMO_USER_ID=${runtimeReference.demoEnv.userId}
85
+ PUBLIC_TOPOGRAM_DEMO_USER_ID=${runtimeDemoUserId(runtimeReference)}
85
86
  ${runtimeReference.environment.envExample || ""}
86
87
  `;
87
88
  }
@@ -7,6 +7,7 @@ import {
7
7
  dbEnvVarsForComponent,
8
8
  getDefaultEnvironmentProjections,
9
9
  resolveRuntimeTopology,
10
+ runtimeDemoUserId,
10
11
  runtimePorts,
11
12
  runtimeUrls
12
13
  } from "./shared.js";
@@ -158,7 +159,7 @@ function buildEnvironmentPlan(graph, options = {}) {
158
159
  }
159
160
 
160
161
  function renderEnvironmentEnvExample(plan) {
161
- const demo = plan.runtimeReference.demoEnv;
162
+ const demoUserId = runtimeDemoUserId(plan.runtimeReference);
162
163
  const databaseName = plan.runtimeReference.environment.databaseName || "topogram_app";
163
164
  const urls = runtimeUrls(plan.runtimeReference, {
164
165
  primaryApi: { port: plan.ports.server },
@@ -184,8 +185,8 @@ function renderEnvironmentEnvExample(plan) {
184
185
  TOPOGRAM_ENVIRONMENT_PROFILE=${plan.environment.profile}
185
186
 
186
187
  # Local stack ports
187
- ${plan.components.apis.length ? `SERVER_PORT=${plan.ports.server}\n` : ""}${plan.components.webs.length ? `WEB_PORT=${plan.ports.web}\n` : ""}${plan.components.webs.length && plan.components.apis.length ? `PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}\n` : ""}${plan.components.webs.length ? `TOPOGRAM_CORS_ORIGINS=${urls.web},http://127.0.0.1:${plan.ports.web}\n` : ""}PUBLIC_TOPOGRAM_DEMO_USER_ID=${demo.userId}
188
- TOPOGRAM_DEMO_USER_ID=${demo.userId}
188
+ ${plan.components.apis.length ? `SERVER_PORT=${plan.ports.server}\n` : ""}${plan.components.webs.length ? `WEB_PORT=${plan.ports.web}\n` : ""}${plan.components.webs.length && plan.components.apis.length ? `PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}\n` : ""}${plan.components.webs.length ? `TOPOGRAM_CORS_ORIGINS=${urls.web},http://127.0.0.1:${plan.ports.web}\n` : ""}PUBLIC_TOPOGRAM_DEMO_USER_ID=${demoUserId}
189
+ TOPOGRAM_DEMO_USER_ID=${demoUserId}
189
190
  ${plan.runtimeReference.environment.envExample || ""}
190
191
  TOPOGRAM_SEED_DEMO=true
191
192
  `;
@@ -204,8 +205,8 @@ WEB_PORT=${plan.ports.web}
204
205
  DATABASE_URL=file:./var/${databaseName}.sqlite
205
206
  ${extraDatabaseLines ? `${extraDatabaseLines}\n` : ""}PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
206
207
  TOPOGRAM_CORS_ORIGINS=${urls.web},http://127.0.0.1:${plan.ports.web}
207
- PUBLIC_TOPOGRAM_DEMO_USER_ID=${demo.userId}
208
- TOPOGRAM_DEMO_USER_ID=${demo.userId}
208
+ PUBLIC_TOPOGRAM_DEMO_USER_ID=${demoUserId}
209
+ TOPOGRAM_DEMO_USER_ID=${demoUserId}
209
210
  ${plan.runtimeReference.environment.envExample || ""}
210
211
  TOPOGRAM_SEED_DEMO=true
211
212
  `;
@@ -229,8 +230,8 @@ DATABASE_URL=postgresql://\${POSTGRES_USER}@localhost:${plan.ports.database || 5
229
230
  DATABASE_ADMIN_URL=postgresql://\${POSTGRES_USER}@localhost:${plan.ports.database || 5432}/postgres
230
231
  ${extraDatabaseLines ? `${extraDatabaseLines}\n` : ""}PUBLIC_TOPOGRAM_API_BASE_URL=${urls.api}
231
232
  TOPOGRAM_CORS_ORIGINS=${urls.web},http://127.0.0.1:${plan.ports.web}
232
- PUBLIC_TOPOGRAM_DEMO_USER_ID=${demo.userId}
233
- TOPOGRAM_DEMO_USER_ID=${demo.userId}
233
+ PUBLIC_TOPOGRAM_DEMO_USER_ID=${demoUserId}
234
+ TOPOGRAM_DEMO_USER_ID=${demoUserId}
234
235
  ${plan.runtimeReference.environment.envExample || ""}
235
236
  TOPOGRAM_SEED_DEMO=true
236
237
  `;
@@ -3,6 +3,7 @@ import {
3
3
  buildVerificationSummary,
4
4
  getDefaultEnvironmentProjections,
5
5
  resolveRuntimeTopology,
6
+ runtimeDemoUserId,
6
7
  selectChecksByVerification,
7
8
  runtimePorts,
8
9
  runtimeUrls
@@ -74,11 +75,10 @@ function buildRuntimeCheckPlan(graph, options = {}) {
74
75
 
75
76
  function renderRuntimeCheckEnvExample(graph, options = {}) {
76
77
  const runtimeReference = getExampleImplementation(graph, options).runtime.reference;
77
- const demo = runtimeReference.demoEnv;
78
78
  const urls = runtimeUrls(runtimeReference, resolveRuntimeTopology(graph, options));
79
79
  return `TOPOGRAM_API_BASE_URL=${urls.api}
80
80
  TOPOGRAM_WEB_BASE_URL=${urls.web}
81
- TOPOGRAM_DEMO_USER_ID=${demo.userId}
81
+ TOPOGRAM_DEMO_USER_ID=${runtimeDemoUserId(runtimeReference)}
82
82
  ${runtimeReference.environment.envExample || ""}
83
83
  `;
84
84
  }
@@ -166,9 +166,9 @@ function envValue(name) {
166
166
  const fallbackMap = {
167
167
  TOPOGRAM_API_BASE_URL: process.env.PUBLIC_TOPOGRAM_API_BASE_URL || "http://localhost:${ports.server}",
168
168
  TOPOGRAM_WEB_BASE_URL: process.env.PUBLIC_TOPOGRAM_WEB_BASE_URL || \`http://localhost:\${process.env.WEB_PORT || "${ports.web}"}\`,
169
- ${runtimeReference.runtimeCheck.demoContainerEnvVar}: process.env.PUBLIC_TOPOGRAM_DEMO_CONTAINER_ID || process.env.PUBLIC_TOPOGRAM_DEMO_PROJECT_ID || "",
170
- ${runtimeReference.runtimeCheck.demoPrimaryEnvVar}: process.env.PUBLIC_TOPOGRAM_DEMO_PRIMARY_ID || process.env.PUBLIC_TOPOGRAM_DEMO_TASK_ID || process.env.PUBLIC_TOPOGRAM_DEMO_ISSUE_ID || "",
171
- TOPOGRAM_DEMO_USER_ID: process.env.PUBLIC_TOPOGRAM_DEMO_USER_ID || "",
169
+ ${runtimeReference.runtimeCheck.demoContainerEnvVar}: process.env.PUBLIC_TOPOGRAM_DEMO_CONTAINER_ID || "",
170
+ ${runtimeReference.runtimeCheck.demoPrimaryEnvVar}: process.env.PUBLIC_TOPOGRAM_DEMO_PRIMARY_ID || "",
171
+ TOPOGRAM_DEMO_USER_ID: process.env.PUBLIC_TOPOGRAM_AUTH_USER_ID || process.env.PUBLIC_TOPOGRAM_DEMO_USER_ID || "",
172
172
  TOPOGRAM_AUTH_USER_ID: process.env.TOPOGRAM_DEMO_USER_ID || ""
173
173
  };
174
174
 
@@ -513,3 +513,12 @@ export function runtimeUrls(runtimeReference, topology = null) {
513
513
  web: `http://localhost:${ports.web}`
514
514
  };
515
515
  }
516
+
517
+ /**
518
+ * @param {Record<string, any>|null|undefined} runtimeReference
519
+ * @returns {string}
520
+ */
521
+ export function runtimeDemoUserId(runtimeReference) {
522
+ const demo = runtimeReference?.demoEnv || {};
523
+ return demo.userId || demo.memberId || demo.ownerId || demo.primaryActorId || "";
524
+ }
@@ -2,6 +2,7 @@ import {
2
2
  buildVerificationSummary,
3
3
  getDefaultEnvironmentProjections,
4
4
  resolveRuntimeTopology,
5
+ runtimeDemoUserId,
5
6
  runtimeUrls,
6
7
  selectChecksByVerification
7
8
  } from "./shared.js";
@@ -126,7 +127,7 @@ process.on("unhandledRejection", reportFatal);
126
127
  const apiBase = process.env.TOPOGRAM_API_BASE_URL || "";
127
128
  const webBase = process.env.TOPOGRAM_WEB_BASE_URL || "";
128
129
  const demoContainerId = process.env.${runtimeReference.smoke.defaultContainerEnvVar} || "${runtimeReference.demoEnv.containerId}";
129
- const demoUserId = process.env.TOPOGRAM_DEMO_USER_ID || "${runtimeReference.demoEnv.userId}";
130
+ const demoUserId = process.env.TOPOGRAM_AUTH_USER_ID || process.env.TOPOGRAM_DEMO_USER_ID || "${runtimeDemoUserId(runtimeReference)}";
130
131
  const authToken = process.env.TOPOGRAM_AUTH_TOKEN || "";
131
132
 
132
133
  if (!apiBase || !webBase) {
@@ -38,7 +38,8 @@ export function uiProjectionCandidates(graph) {
38
38
  (projection.uiAppShell || []).length > 0 ||
39
39
  (projection.uiNavigation || []).length > 0 ||
40
40
  (projection.uiScreenRegions || []).length > 0 ||
41
- (projection.uiComponents || []).length > 0
41
+ (projection.uiComponents || []).length > 0 ||
42
+ (projection.uiDesign || []).length > 0
42
43
  );
43
44
  }
44
45