scene-capability-engine 3.6.64 → 3.6.67

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 (125) hide show
  1. package/CHANGELOG.md +26 -0
  2. package/README.md +17 -6
  3. package/README.zh.md +18 -6
  4. package/bin/scene-capability-engine.js +4 -0
  5. package/docs/README.md +2 -2
  6. package/docs/command-reference.md +385 -8
  7. package/docs/document-governance.md +3 -2
  8. package/docs/integration-modes.md +62 -478
  9. package/docs/integration-philosophy.md +56 -263
  10. package/docs/magicball-cli-invocation-examples.md +1 -0
  11. package/docs/magicball-project-portfolio-contract.md +125 -4
  12. package/docs/project-management/README.md +14 -0
  13. package/docs/project-management/assurance/backup.md +3 -0
  14. package/docs/project-management/assurance/config.md +3 -0
  15. package/docs/project-management/assurance/evidence/README.md +3 -0
  16. package/docs/project-management/assurance/incidents/README.md +3 -0
  17. package/docs/project-management/assurance/logs.md +3 -0
  18. package/docs/project-management/assurance/overview.md +3 -0
  19. package/docs/project-management/assurance/recovery/README.md +3 -0
  20. package/docs/project-management/assurance/resource.md +3 -0
  21. package/docs/project-management/assurance/runbooks/README.md +3 -0
  22. package/docs/project-management/delivery/acceptance/README.md +3 -0
  23. package/docs/project-management/delivery/acceptance/evidence/README.md +3 -0
  24. package/docs/project-management/delivery/acceptance/exceptions/README.md +3 -0
  25. package/docs/project-management/delivery/acceptance/reports/README.md +3 -0
  26. package/docs/project-management/delivery/documents/changes.md +3 -0
  27. package/docs/project-management/delivery/documents/issues.md +3 -0
  28. package/docs/project-management/delivery/documents/overview.md +3 -0
  29. package/docs/project-management/delivery/documents/planning.md +3 -0
  30. package/docs/project-management/delivery/documents/requirements.md +3 -0
  31. package/docs/project-management/delivery/documents/tracking.md +3 -0
  32. package/docs/project-management/delivery/handoffs/README.md +3 -0
  33. package/docs/project-management/delivery/handoffs/evidence/README.md +3 -0
  34. package/docs/project-management/delivery/handoffs/records/README.md +3 -0
  35. package/docs/project-management/delivery/overview.md +10 -0
  36. package/docs/project-management/delivery/releases/README.md +3 -0
  37. package/docs/project-management/delivery/releases/baselines/README.md +3 -0
  38. package/docs/project-management/delivery/releases/evidence/README.md +3 -0
  39. package/docs/project-management/delivery/tables/changes.md +3 -0
  40. package/docs/project-management/delivery/tables/issues.md +3 -0
  41. package/docs/project-management/delivery/tables/planning.md +3 -0
  42. package/docs/project-management/delivery/tables/requirements.md +3 -0
  43. package/docs/project-management/delivery/tables/tracking.md +3 -0
  44. package/docs/project-management/environment/agent-discovery.md +3 -0
  45. package/docs/project-management/environment/development.md +3 -0
  46. package/docs/project-management/environment/overview.md +10 -0
  47. package/docs/project-management/environment/testing.md +3 -0
  48. package/docs/project-management/environment/version-alignment.md +3 -0
  49. package/docs/quick-start-with-ai-tools.md +68 -308
  50. package/docs/releases/README.md +3 -0
  51. package/docs/releases/v3.6.65.md +25 -0
  52. package/docs/releases/v3.6.66.md +23 -0
  53. package/docs/releases/v3.6.67.md +23 -0
  54. package/docs/steering-governance.md +64 -2
  55. package/docs/zh/README.md +2 -2
  56. package/docs/zh/releases/README.md +3 -0
  57. package/docs/zh/releases/v3.6.65.md +25 -0
  58. package/docs/zh/releases/v3.6.66.md +23 -0
  59. package/docs/zh/releases/v3.6.67.md +23 -0
  60. package/lib/commands/adopt.js +24 -0
  61. package/lib/commands/native.js +158 -0
  62. package/lib/commands/project.js +96 -0
  63. package/lib/commands/semantic.js +1459 -0
  64. package/lib/commands/session.js +74 -3
  65. package/lib/commands/spec-bootstrap.js +10 -1
  66. package/lib/commands/spec-gate.js +10 -1
  67. package/lib/commands/spec-pipeline.js +10 -1
  68. package/lib/commands/studio.js +405 -30
  69. package/lib/commands/task.js +141 -7
  70. package/lib/governance/supreme-principles.js +530 -0
  71. package/lib/problem/problem-evaluator.js +4 -0
  72. package/lib/project/candidate-inspection-service.js +24 -1
  73. package/lib/project/portfolio-projection-service.js +315 -5
  74. package/lib/project/project-channel-output.js +94 -0
  75. package/lib/project/project-channel-projection.js +181 -0
  76. package/lib/project/root-onboarding-service.js +107 -7
  77. package/lib/project/semantic-shared-source-projection.js +150 -0
  78. package/lib/project/supervision-action-model.js +277 -0
  79. package/lib/project/supervision-projection-service.js +305 -5
  80. package/lib/project/target-resolution-service.js +70 -5
  81. package/lib/project/visibility-policy.js +93 -0
  82. package/lib/runtime/multi-spec-scene-session.js +8 -1
  83. package/lib/runtime/project-channel-context-store.js +387 -0
  84. package/lib/runtime/project-channel-context.js +406 -0
  85. package/lib/runtime/scene-session-binding.js +46 -0
  86. package/lib/runtime/session-store.js +186 -0
  87. package/lib/runtime/steering-contract.js +7 -1
  88. package/lib/semantic/archive-report.js +283 -0
  89. package/lib/semantic/archive-routing.js +67 -0
  90. package/lib/semantic/backflow-report.js +245 -0
  91. package/lib/semantic/capability-contract.js +30 -0
  92. package/lib/semantic/delta-export.js +145 -0
  93. package/lib/semantic/interaction-observer.js +254 -0
  94. package/lib/semantic/kernel-loader.js +881 -0
  95. package/lib/semantic/native-runtime.js +359 -0
  96. package/lib/semantic/progress-ledger.js +433 -0
  97. package/lib/semantic/replay-evaluator.js +382 -0
  98. package/lib/semantic/shared-publication.js +592 -0
  99. package/lib/semantic/shared-source-config.js +183 -0
  100. package/lib/semantic/shared-source-connect.js +139 -0
  101. package/lib/semantic/shared-source-discovery.js +98 -0
  102. package/lib/semantic/shared-sync-export.js +413 -0
  103. package/lib/semantic/shared-sync-intake.js +592 -0
  104. package/lib/semantic/shared-sync-merge.js +547 -0
  105. package/lib/semantic/shared-sync-release.js +463 -0
  106. package/lib/semantic/supreme-intent-report.js +300 -0
  107. package/lib/state/sce-state-store.js +1360 -0
  108. package/lib/steering/context-sync-manager.js +276 -25
  109. package/lib/studio/spec-intake-governor.js +39 -3
  110. package/lib/studio/task-envelope.js +35 -2
  111. package/lib/workspace/takeover-baseline.js +342 -83
  112. package/package.json +7 -2
  113. package/scripts/agent-governance-baseline-audit.js +395 -0
  114. package/scripts/clarification-first-audit.js +9 -9
  115. package/scripts/deprecated-entry-audit.js +240 -0
  116. package/scripts/release-doc-version-audit.js +24 -0
  117. package/scripts/release-posture-report.js +262 -0
  118. package/template/.sce/README.md +62 -228
  119. package/template/.sce/config/semantic-shared-sources.json +5 -0
  120. package/template/.sce/config/supreme-principles-policy.json +105 -0
  121. package/template/.sce/config/takeover-baseline.json +7 -0
  122. package/template/.sce/steering/CORE_PRINCIPLES.md +23 -63
  123. package/template/.sce/steering/CURRENT_CONTEXT.md +4 -0
  124. package/template/.sce/steering/RULES_GUIDE.md +17 -9
  125. package/template/README.md +32 -96
@@ -4,6 +4,22 @@ const fs = require('fs-extra');
4
4
  const WorkspaceStateManager = require('../workspace/multi/workspace-state-manager');
5
5
  const { SessionStore } = require('../runtime/session-store');
6
6
  const { getCurrentDeviceProfile } = require('../device/current-device');
7
+ const {
8
+ buildEmptySummary,
9
+ buildSemanticSharedSourceProjection
10
+ } = require('./semantic-shared-source-projection');
11
+ const {
12
+ buildEmptyProjectChannelProjection,
13
+ buildProjectChannelProjection
14
+ } = require('./project-channel-projection');
15
+ const { buildSupervisionActionQueue } = require('./supervision-action-model');
16
+ const { buildSupremeIntentGovernanceReport } = require('../semantic/supreme-intent-report');
17
+ const { buildSemanticBackflowReport } = require('../semantic/backflow-report');
18
+ const { buildSemanticArchiveReport } = require('../semantic/archive-report');
19
+ const {
20
+ classifyProjectVisibility,
21
+ isCallerVisibleProject
22
+ } = require('./visibility-policy');
7
23
 
8
24
  function normalizeString(value) {
9
25
  if (typeof value !== 'string') {
@@ -130,6 +146,176 @@ function deriveStatus({ accessible, isCurrentProject, activeSessionCount }) {
130
146
  return 'idle';
131
147
  }
132
148
 
149
+ function buildProjectSupervisionSignalCandidates(projectId, reports = {}) {
150
+ const archiveTotals = reports.archiveReport && reports.archiveReport.totals && typeof reports.archiveReport.totals === 'object'
151
+ ? reports.archiveReport.totals
152
+ : {};
153
+ const backflowTotals = reports.backflowReport && reports.backflowReport.totals && typeof reports.backflowReport.totals === 'object'
154
+ ? reports.backflowReport.totals
155
+ : {};
156
+ const governanceHotspots = Array.isArray(reports.governanceReport && reports.governanceReport.top_scope_hotspots)
157
+ ? reports.governanceReport.top_scope_hotspots
158
+ : [];
159
+ const libraryRecords = Array.isArray(reports.archiveReport && reports.archiveReport.by_library)
160
+ ? reports.archiveReport.by_library
161
+ : [];
162
+ const pendingConnectCount = Number(reports.semanticSharedSources && reports.semanticSharedSources.pendingApprovedDescriptors || 0);
163
+ const items = [];
164
+
165
+ if (Number(archiveTotals.missing_archive_count || 0) > 0) {
166
+ items.push({
167
+ id: `portfolio:archive-missing:${projectId}`,
168
+ kind: 'archive',
169
+ state: 'archive-routing-missing',
170
+ missingArchiveCount: Number(archiveTotals.missing_archive_count || 0),
171
+ updatedAt: reports.generatedAt
172
+ });
173
+ }
174
+
175
+ if (Number(backflowTotals.central_release_blocked || 0) > 0) {
176
+ items.push({
177
+ id: `portfolio:central-release:${projectId}`,
178
+ kind: 'backflow',
179
+ state: 'central-release-blocked',
180
+ specId: Array.isArray(reports.backflowReport && reports.backflowReport.blocked_spec_ids)
181
+ ? normalizeString(reports.backflowReport.blocked_spec_ids[0])
182
+ : '',
183
+ blockedCount: Number(backflowTotals.central_release_blocked || 0),
184
+ updatedAt: reports.generatedAt
185
+ });
186
+ }
187
+
188
+ if (Number(backflowTotals.central_merge_blocked || 0) > 0) {
189
+ items.push({
190
+ id: `portfolio:central-merge:${projectId}`,
191
+ kind: 'backflow',
192
+ state: 'central-merge-blocked',
193
+ specId: Array.isArray(reports.backflowReport && reports.backflowReport.blocked_spec_ids)
194
+ ? normalizeString(reports.backflowReport.blocked_spec_ids[0])
195
+ : '',
196
+ blockedCount: Number(backflowTotals.central_merge_blocked || 0),
197
+ updatedAt: reports.generatedAt
198
+ });
199
+ }
200
+
201
+ if (Number(backflowTotals.local_publish_blocked || 0) > 0) {
202
+ items.push({
203
+ id: `portfolio:local-backflow:${projectId}`,
204
+ kind: 'backflow',
205
+ state: 'local-publish-blocked',
206
+ specId: Array.isArray(reports.backflowReport && reports.backflowReport.blocked_spec_ids)
207
+ ? normalizeString(reports.backflowReport.blocked_spec_ids[0])
208
+ : '',
209
+ blockedCount: Number(backflowTotals.local_publish_blocked || 0),
210
+ updatedAt: reports.generatedAt
211
+ });
212
+ }
213
+
214
+ const topGovernanceHotspot = governanceHotspots.find((item) => Number(item && item.governed_count || 0) > 0);
215
+ if (topGovernanceHotspot) {
216
+ items.push({
217
+ id: `portfolio:governance:${projectId}:${normalizeString(topGovernanceHotspot.channel_id) || 'channel'}`,
218
+ kind: 'governance',
219
+ state: Number(topGovernanceHotspot.refuse || 0) > 0 ? 'refuse-hotspot' : 'rewrite-hotspot',
220
+ channelId: normalizeString(topGovernanceHotspot.channel_id),
221
+ sceneId: normalizeString(topGovernanceHotspot.scene_id),
222
+ specId: normalizeString(topGovernanceHotspot.spec_id),
223
+ governedCount: Number(topGovernanceHotspot.governed_count || 0),
224
+ updatedAt: safeIsoAt(topGovernanceHotspot.latest_at) || reports.generatedAt
225
+ });
226
+ }
227
+
228
+ if (pendingConnectCount > 0) {
229
+ items.push({
230
+ id: `portfolio:shared-source:${projectId}`,
231
+ kind: 'info',
232
+ state: 'pending-connect',
233
+ descriptorFile: '.sce/specs/<spec>/registry/semantic-shared/published/source-descriptor.json',
234
+ updatedAt: reports.generatedAt
235
+ });
236
+ }
237
+
238
+ const archivePressure = libraryRecords.find((item) => (
239
+ Number(item && item.publish_blocked_count || 0) > 0
240
+ || Number(item && item.publish_pending_count || 0) > 0
241
+ || Number(item && item.lesson_count || 0) > 0
242
+ || Number(item && item.ledger_count || 0) > 0
243
+ ));
244
+ if (archivePressure) {
245
+ items.push({
246
+ id: `portfolio:archive:${projectId}:${normalizeString(archivePressure.target_library) || 'library'}`,
247
+ kind: 'archive',
248
+ state: Number(archivePressure.published_shared_count || 0) > 0 ? 'shared-accumulating' : 'local-accumulating',
249
+ targetLibrary: normalizeString(archivePressure.target_library),
250
+ publishPendingCount: Number(archivePressure.publish_pending_count || 0),
251
+ publishBlockedCount: Number(archivePressure.publish_blocked_count || 0),
252
+ updatedAt: reports.generatedAt
253
+ });
254
+ }
255
+
256
+ return items;
257
+ }
258
+
259
+ async function buildProjectSupervisionSignals(projectId, projectRoot, semanticSharedSources, dependencies = {}) {
260
+ const fileSystem = dependencies.fileSystem || fs;
261
+ const archiveReport = await buildSemanticArchiveReport({
262
+ project_id: projectId,
263
+ limit: 50,
264
+ recent_limit: 5
265
+ }, {
266
+ ...dependencies,
267
+ projectPath: projectRoot,
268
+ fileSystem
269
+ });
270
+ const backflowReport = await buildSemanticBackflowReport({
271
+ project_id: projectId,
272
+ limit: 50,
273
+ recent_limit: 5
274
+ }, {
275
+ ...dependencies,
276
+ projectPath: projectRoot,
277
+ fileSystem
278
+ });
279
+ const governanceReport = await buildSupremeIntentGovernanceReport({
280
+ project_id: projectId,
281
+ limit: 50,
282
+ hotspot_limit: 3,
283
+ recent_limit: 3
284
+ }, {
285
+ ...dependencies,
286
+ projectPath: projectRoot,
287
+ fileSystem
288
+ });
289
+ const generatedAt = new Date().toISOString();
290
+ const candidates = buildProjectSupervisionSignalCandidates(projectId, {
291
+ generatedAt,
292
+ archiveReport,
293
+ backflowReport,
294
+ governanceReport,
295
+ semanticSharedSources
296
+ });
297
+ const actionProjection = buildSupervisionActionQueue(candidates, { projectId });
298
+
299
+ return {
300
+ highestSeverity: actionProjection.summary.highestSeverity,
301
+ actionableCount: actionProjection.summary.actionableCount,
302
+ archiveMissingCount: Number(archiveReport.totals.missing_archive_count || 0),
303
+ backflowBlockedCount: Number(backflowReport.totals.local_publish_blocked || 0)
304
+ + Number(backflowReport.totals.central_merge_blocked || 0)
305
+ + Number(backflowReport.totals.central_release_blocked || 0),
306
+ governanceHotspotCount: governanceHotspotsCount(governanceReport),
307
+ pendingConnectCount: Number(semanticSharedSources.pendingApprovedDescriptors || 0),
308
+ ...(actionProjection.summary.actionableCount > 0
309
+ ? { primaryRecommendedCommand: `sce project supervision show --project ${projectId} --json` }
310
+ : {})
311
+ };
312
+ }
313
+
314
+ function governanceHotspotsCount(report = {}) {
315
+ const hotspots = Array.isArray(report.top_scope_hotspots) ? report.top_scope_hotspots : [];
316
+ return hotspots.filter((item) => Number(item && item.governed_count || 0) > 0).length;
317
+ }
318
+
133
319
  async function readSceneRecords(projectRoot, dependencies = {}) {
134
320
  const fileSystem = dependencies.fileSystem || fs;
135
321
  const env = dependencies.env || process.env;
@@ -152,6 +338,16 @@ async function buildRegisteredWorkspaceRecord(workspace, context = {}, dependenc
152
338
  let partial = false;
153
339
  let sceneRecords = [];
154
340
  let specCount = 0;
341
+ let semanticSharedSources = buildEmptySummary();
342
+ let projectChannelContext = buildEmptyProjectChannelProjection().summary;
343
+ let supervisionSignals = {
344
+ highestSeverity: 'none',
345
+ actionableCount: 0,
346
+ archiveMissingCount: 0,
347
+ backflowBlockedCount: 0,
348
+ governanceHotspotCount: 0,
349
+ pendingConnectCount: 0
350
+ };
155
351
 
156
352
  const sceProject = await isValidSceProjectRoot(projectRoot, fileSystem);
157
353
  if (!sceProject) {
@@ -171,6 +367,35 @@ async function buildRegisteredWorkspaceRecord(workspace, context = {}, dependenc
171
367
  partial = true;
172
368
  partialReasons.push('spec_inventory_unavailable');
173
369
  }
370
+ const semanticSharedSourceProjection = await buildSemanticSharedSourceProjection(projectRoot, {
371
+ fileSystem
372
+ });
373
+ semanticSharedSources = semanticSharedSourceProjection.summary;
374
+ if (semanticSharedSourceProjection.partial) {
375
+ partial = true;
376
+ partialReasons.push(...semanticSharedSourceProjection.partialReasons);
377
+ }
378
+ const projectChannelProjection = await buildProjectChannelProjection(projectRoot, {
379
+ preferredProjectIds: [projectId, workspaceId]
380
+ }, {
381
+ fileSystem
382
+ });
383
+ projectChannelContext = projectChannelProjection.summary;
384
+ if (projectChannelProjection.partial) {
385
+ partial = true;
386
+ partialReasons.push(...projectChannelProjection.partialReasons);
387
+ }
388
+ try {
389
+ supervisionSignals = await buildProjectSupervisionSignals(
390
+ projectId,
391
+ projectRoot,
392
+ semanticSharedSources,
393
+ dependencies
394
+ );
395
+ } catch (_error) {
396
+ partial = true;
397
+ partialReasons.push('supervision_signals_unavailable');
398
+ }
174
399
  }
175
400
 
176
401
  const sceneCount = Array.isArray(sceneRecords) ? sceneRecords.length : 0;
@@ -180,12 +405,19 @@ async function buildRegisteredWorkspaceRecord(workspace, context = {}, dependenc
180
405
  const lastActivityAt = collectRecordActivityTimestamps(sceneRecords);
181
406
  const uniquePartialReasons = Array.from(new Set(partialReasons));
182
407
  const isCurrentProject = context.projectId === projectId;
408
+ const visibilityInfo = classifyProjectVisibility({
409
+ workspaceId,
410
+ projectRoot,
411
+ projectName: path.basename(projectRoot) || workspaceId
412
+ });
183
413
 
184
414
  return {
185
415
  projectId,
186
416
  workspaceId,
187
417
  projectRoot,
188
418
  projectName: path.basename(projectRoot) || workspaceId,
419
+ visibility: visibilityInfo.visibility,
420
+ lifecycle: visibilityInfo.lifecycle,
189
421
  provenance: 'registered',
190
422
  readiness: deriveReadiness({
191
423
  accessible,
@@ -208,6 +440,9 @@ async function buildRegisteredWorkspaceRecord(workspace, context = {}, dependenc
208
440
  sceneCount,
209
441
  specCount
210
442
  },
443
+ semanticSharedSources,
444
+ supervisionSignals,
445
+ projectChannelContext,
211
446
  partial,
212
447
  partialReasons: uniquePartialReasons
213
448
  };
@@ -221,6 +456,16 @@ async function buildLocalProjectRecord(projectRoot, context = {}, dependencies =
221
456
  let specCount = 0;
222
457
  const partialReasons = ['unregistered_project'];
223
458
  let partial = true;
459
+ let semanticSharedSources = buildEmptySummary();
460
+ let projectChannelContext = buildEmptyProjectChannelProjection().summary;
461
+ let supervisionSignals = {
462
+ highestSeverity: 'none',
463
+ actionableCount: 0,
464
+ archiveMissingCount: 0,
465
+ backflowBlockedCount: 0,
466
+ governanceHotspotCount: 0,
467
+ pendingConnectCount: 0
468
+ };
224
469
 
225
470
  try {
226
471
  sceneRecords = await readSceneRecords(normalizedRoot, dependencies);
@@ -232,6 +477,32 @@ async function buildLocalProjectRecord(projectRoot, context = {}, dependencies =
232
477
  } catch (_error) {
233
478
  partialReasons.push('spec_inventory_unavailable');
234
479
  }
480
+ const semanticSharedSourceProjection = await buildSemanticSharedSourceProjection(normalizedRoot, {
481
+ fileSystem
482
+ });
483
+ semanticSharedSources = semanticSharedSourceProjection.summary;
484
+ if (semanticSharedSourceProjection.partial) {
485
+ partialReasons.push(...semanticSharedSourceProjection.partialReasons);
486
+ }
487
+ const projectChannelProjection = await buildProjectChannelProjection(normalizedRoot, {
488
+ preferredProjectIds: [projectId, path.basename(normalizedRoot)]
489
+ }, {
490
+ fileSystem
491
+ });
492
+ projectChannelContext = projectChannelProjection.summary;
493
+ if (projectChannelProjection.partial) {
494
+ partialReasons.push(...projectChannelProjection.partialReasons);
495
+ }
496
+ try {
497
+ supervisionSignals = await buildProjectSupervisionSignals(
498
+ projectId,
499
+ normalizedRoot,
500
+ semanticSharedSources,
501
+ dependencies
502
+ );
503
+ } catch (_error) {
504
+ partialReasons.push('supervision_signals_unavailable');
505
+ }
235
506
 
236
507
  const sceneCount = Array.isArray(sceneRecords) ? sceneRecords.length : 0;
237
508
  const activeSessionCount = Array.isArray(sceneRecords)
@@ -240,11 +511,17 @@ async function buildLocalProjectRecord(projectRoot, context = {}, dependencies =
240
511
  const lastActivityAt = collectRecordActivityTimestamps(sceneRecords);
241
512
  const uniquePartialReasons = Array.from(new Set(partialReasons));
242
513
  const isCurrentProject = context.projectId === projectId;
514
+ const visibilityInfo = classifyProjectVisibility({
515
+ projectRoot: normalizedRoot,
516
+ projectName: path.basename(normalizedRoot) || 'local-project'
517
+ });
243
518
 
244
519
  return {
245
520
  projectId,
246
521
  projectRoot: normalizedRoot,
247
522
  projectName: path.basename(normalizedRoot) || 'local-project',
523
+ visibility: visibilityInfo.visibility,
524
+ lifecycle: visibilityInfo.lifecycle,
248
525
  provenance: 'discovered',
249
526
  readiness: deriveReadiness({
250
527
  accessible: true,
@@ -267,6 +544,9 @@ async function buildLocalProjectRecord(projectRoot, context = {}, dependencies =
267
544
  sceneCount,
268
545
  specCount
269
546
  },
547
+ semanticSharedSources,
548
+ supervisionSignals,
549
+ projectChannelContext,
270
550
  partial,
271
551
  partialReasons: uniquePartialReasons
272
552
  };
@@ -282,6 +562,12 @@ async function resolveCurrentProjectContext(options = {}, dependencies = {}) {
282
562
  if (!workspace) {
283
563
  throw new Error(`workspace not found: ${explicitWorkspace}`);
284
564
  }
565
+ if (!isCallerVisibleProject({
566
+ workspaceId: workspace.name,
567
+ projectRoot: normalizePath(workspace.path)
568
+ })) {
569
+ throw new Error(`workspace not visible: ${explicitWorkspace}`);
570
+ }
285
571
  return {
286
572
  workspaceId: workspace.name,
287
573
  projectId: buildWorkspaceProjectId(workspace.name),
@@ -291,7 +577,10 @@ async function resolveCurrentProjectContext(options = {}, dependencies = {}) {
291
577
  }
292
578
 
293
579
  const matchedWorkspace = await stateManager.findWorkspaceByPath(currentDir);
294
- if (matchedWorkspace) {
580
+ if (matchedWorkspace && isCallerVisibleProject({
581
+ workspaceId: matchedWorkspace.name,
582
+ projectRoot: normalizePath(matchedWorkspace.path)
583
+ })) {
295
584
  return {
296
585
  workspaceId: matchedWorkspace.name,
297
586
  projectId: buildWorkspaceProjectId(matchedWorkspace.name),
@@ -301,7 +590,10 @@ async function resolveCurrentProjectContext(options = {}, dependencies = {}) {
301
590
  }
302
591
 
303
592
  const activeWorkspace = await stateManager.getActiveWorkspace();
304
- if (activeWorkspace) {
593
+ if (activeWorkspace && isCallerVisibleProject({
594
+ workspaceId: activeWorkspace.name,
595
+ projectRoot: normalizePath(activeWorkspace.path)
596
+ })) {
305
597
  return {
306
598
  workspaceId: activeWorkspace.name,
307
599
  projectId: buildWorkspaceProjectId(activeWorkspace.name),
@@ -310,7 +602,10 @@ async function resolveCurrentProjectContext(options = {}, dependencies = {}) {
310
602
  };
311
603
  }
312
604
 
313
- if (await isValidSceProjectRoot(currentDir, dependencies.fileSystem || fs)) {
605
+ if (
606
+ await isValidSceProjectRoot(currentDir, dependencies.fileSystem || fs)
607
+ && isCallerVisibleProject({ projectRoot: currentDir })
608
+ ) {
314
609
  return {
315
610
  workspaceId: null,
316
611
  projectId: buildLocalProjectId(currentDir),
@@ -347,8 +642,20 @@ async function buildProjectPortfolioProjection(options = {}, dependencies = {})
347
642
 
348
643
  const workspaces = await stateManager.listWorkspaces();
349
644
  const records = [];
645
+ const hiddenWorkspaceIds = [];
350
646
  for (const workspace of workspaces) {
351
- records.push(await buildRegisteredWorkspaceRecord(workspace, currentContext, dependencies));
647
+ const record = await buildRegisteredWorkspaceRecord(workspace, currentContext, dependencies);
648
+ if (record.visibility !== 'managed') {
649
+ if (record.lifecycle === 'ephemeral' && record.availability === 'inaccessible') {
650
+ hiddenWorkspaceIds.push(record.workspaceId);
651
+ }
652
+ continue;
653
+ }
654
+ records.push(record);
655
+ }
656
+
657
+ for (const workspaceId of hiddenWorkspaceIds) {
658
+ await stateManager.removeWorkspace(workspaceId);
352
659
  }
353
660
 
354
661
  const localRoot = normalizeString(currentContext.projectRoot);
@@ -357,7 +664,10 @@ async function buildProjectPortfolioProjection(options = {}, dependencies = {})
357
664
  && localRoot
358
665
  && !currentDirWorkspace;
359
666
  if (shouldAddLocalProject) {
360
- records.push(await buildLocalProjectRecord(localRoot, currentContext, dependencies));
667
+ const localRecord = await buildLocalProjectRecord(localRoot, currentContext, dependencies);
668
+ if (localRecord.visibility === 'managed') {
669
+ records.push(localRecord);
670
+ }
361
671
  }
362
672
 
363
673
  records.sort((left, right) => {
@@ -0,0 +1,94 @@
1
+ function normalizeString(value) {
2
+ if (typeof value !== 'string') {
3
+ return '';
4
+ }
5
+ return value.trim();
6
+ }
7
+
8
+ function buildProjectChannelOutput(options = {}) {
9
+ const context = options.context && typeof options.context === 'object'
10
+ ? options.context
11
+ : null;
12
+ const channel = options.channel && typeof options.channel === 'object'
13
+ ? options.channel
14
+ : null;
15
+ const session = options.session && typeof options.session === 'object'
16
+ ? options.session
17
+ : null;
18
+ const fallback = options.fallback && typeof options.fallback === 'object'
19
+ ? options.fallback
20
+ : {};
21
+ const requestedChannelId = normalizeString(
22
+ options.requestedChannelId !== undefined
23
+ ? options.requestedChannelId
24
+ : fallback.requested_channel_id
25
+ ) || null;
26
+ const resolvedChannelId = normalizeString(
27
+ options.resolvedChannelId !== undefined
28
+ ? options.resolvedChannelId
29
+ : (channel && channel.channelId)
30
+ ) || null;
31
+ const focusedChannelId = normalizeString(
32
+ options.focusedChannelId !== undefined
33
+ ? options.focusedChannelId
34
+ : (context && context.focusedChannelId)
35
+ ) || normalizeString(fallback.focused_channel_id) || null;
36
+ const contextAvailable = options.contextAvailable !== undefined
37
+ ? options.contextAvailable === true
38
+ : context !== null;
39
+ const storageMode = normalizeString(
40
+ options.storageMode !== undefined
41
+ ? options.storageMode
42
+ : fallback.storage_mode
43
+ ) || (contextAvailable ? 'unknown' : 'none');
44
+
45
+ return {
46
+ project_id: normalizeString(
47
+ options.projectId !== undefined
48
+ ? options.projectId
49
+ : fallback.project_id
50
+ ) || null,
51
+ canonical_project_id: normalizeString(
52
+ options.canonicalProjectId !== undefined
53
+ ? options.canonicalProjectId
54
+ : fallback.canonical_project_id
55
+ ) || null,
56
+ context_available: contextAvailable,
57
+ storage_mode: storageMode,
58
+ requested_channel_id: requestedChannelId,
59
+ focused_channel_id: focusedChannelId,
60
+ channel_id: resolvedChannelId || normalizeString(fallback.channel_id) || null,
61
+ active_scene: normalizeString(channel ? channel.activeScene : fallback.active_scene) || null,
62
+ active_spec_id: normalizeString(channel ? channel.activeSpecId : fallback.active_spec_id) || null,
63
+ active_doc: normalizeString(channel ? channel.activeDoc : fallback.active_doc) || null,
64
+ active_session_path: normalizeString(channel ? channel.activeSessionPath : fallback.active_session_path) || null,
65
+ run_state: normalizeString(channel ? channel.runState : fallback.run_state) || null,
66
+ updated_at: normalizeString(channel ? channel.updatedAt : fallback.updated_at) || null,
67
+ resolved_session_id: normalizeString(
68
+ options.resolvedSessionId !== undefined
69
+ ? options.resolvedSessionId
70
+ : (session && session.session_id)
71
+ ) || normalizeString(fallback.resolved_session_id) || null
72
+ };
73
+ }
74
+
75
+ function buildProjectChannelOutputFromProjection(projection = {}, options = {}) {
76
+ const summary = projection && projection.summary && typeof projection.summary === 'object'
77
+ ? projection.summary
78
+ : {};
79
+ return buildProjectChannelOutput({
80
+ projectId: options.projectId !== undefined ? options.projectId : summary.contextProjectId,
81
+ canonicalProjectId: options.canonicalProjectId,
82
+ requestedChannelId: options.requestedChannelId,
83
+ focusedChannelId: summary.focusedChannelId,
84
+ contextAvailable: summary.available === true,
85
+ storageMode: summary.storageMode,
86
+ channel: projection && projection.resolvedChannel ? projection.resolvedChannel : null,
87
+ resolvedSessionId: options.resolvedSessionId
88
+ });
89
+ }
90
+
91
+ module.exports = {
92
+ buildProjectChannelOutput,
93
+ buildProjectChannelOutputFromProjection
94
+ };