@wildo-ai/platform-core-shared 1.1.4 → 1.1.6

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 (82) hide show
  1. package/dist/esm/core/resources/resources-operations.custom.shared.definitions.d.ts.map +1 -1
  2. package/dist/esm/core/resources/resources-relationships.custom.shared.definitions.d.ts.map +1 -1
  3. package/dist/esm/index.d.ts +4 -1
  4. package/dist/esm/index.d.ts.map +1 -1
  5. package/dist/esm/index.js +4 -1
  6. package/dist/esm/index.js.map +1 -1
  7. package/dist/esm/runtime/application-backend-env.contracts.d.ts +49 -4
  8. package/dist/esm/runtime/application-backend-env.contracts.d.ts.map +1 -1
  9. package/dist/esm/runtime/application-backend-env.contracts.js +48 -1
  10. package/dist/esm/runtime/application-backend-env.contracts.js.map +1 -1
  11. package/dist/esm/runtime/http-server-env.contracts.d.ts +46 -0
  12. package/dist/esm/runtime/http-server-env.contracts.d.ts.map +1 -0
  13. package/dist/esm/runtime/http-server-env.contracts.js +55 -0
  14. package/dist/esm/runtime/http-server-env.contracts.js.map +1 -0
  15. package/dist/esm/runtime/runtime-topology.contracts.d.ts +0 -4
  16. package/dist/esm/runtime/runtime-topology.contracts.d.ts.map +1 -1
  17. package/dist/esm/runtime/runtime-topology.contracts.js +0 -12
  18. package/dist/esm/runtime/runtime-topology.contracts.js.map +1 -1
  19. package/dist/esm/security/authorizations/roles.custom.shared.definitions.d.ts.map +1 -1
  20. package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.resources-config.schemas.d.ts +166 -70
  21. package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.resources-config.schemas.d.ts.map +1 -1
  22. package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.schemas.d.ts +166 -70
  23. package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.schemas.d.ts.map +1 -1
  24. package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.schemas.js +2 -2
  25. package/dist/esm/wildo-application-executions/wildo-application-executions.custom.shared.schemas.js.map +1 -1
  26. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.resources-config.schemas.d.ts +21 -10
  27. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.resources-config.schemas.d.ts.map +1 -1
  28. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.d.ts +21 -10
  29. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.d.ts.map +1 -1
  30. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.js +26 -28
  31. package/dist/esm/wildo-application-tasks/wildo-application-tasks.custom.shared.schemas.js.map +1 -1
  32. package/dist/esm/wildo-applications/wildo-applications.custom.shared.resources-config.schemas.d.ts.map +1 -1
  33. package/dist/esm/wildo-applications/wildo-applications.custom.shared.resources-config.schemas.js.map +1 -1
  34. package/dist/esm/wildo-applications/wildo-applications.custom.shared.schemas.d.ts +29 -0
  35. package/dist/esm/wildo-applications/wildo-applications.custom.shared.schemas.d.ts.map +1 -1
  36. package/dist/esm/wildo-applications/wildo-applications.custom.shared.schemas.js +58 -0
  37. package/dist/esm/wildo-applications/wildo-applications.custom.shared.schemas.js.map +1 -1
  38. package/dist/esm/wildo-applications-versions/wildo-applications-versions.custom.shared.resources-config.schemas.d.ts.map +1 -1
  39. package/dist/esm/wildo-applications-versions/wildo-applications-versions.custom.shared.resources-config.schemas.js.map +1 -1
  40. package/dist/esm/wildo-applications-versions/wildo-applications-versions.custom.shared.schemas.d.ts.map +1 -1
  41. package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.d.ts +13 -0
  42. package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.d.ts.map +1 -1
  43. package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.js +441 -1
  44. package/dist/esm/workbench/workbench-aspect-resources.custom.shared.schemas.js.map +1 -1
  45. package/dist/esm/workbench/workbench-creation-console.custom.shared.schemas.d.ts +279 -0
  46. package/dist/esm/workbench/workbench-creation-console.custom.shared.schemas.d.ts.map +1 -0
  47. package/dist/esm/workbench/workbench-creation-console.custom.shared.schemas.js +303 -0
  48. package/dist/esm/workbench/workbench-creation-console.custom.shared.schemas.js.map +1 -0
  49. package/dist/esm/workbench/workbench-creation-conversation.custom.shared.schemas.d.ts +199 -0
  50. package/dist/esm/workbench/workbench-creation-conversation.custom.shared.schemas.d.ts.map +1 -0
  51. package/dist/esm/workbench/workbench-creation-conversation.custom.shared.schemas.js +185 -0
  52. package/dist/esm/workbench/workbench-creation-conversation.custom.shared.schemas.js.map +1 -0
  53. package/dist/esm/workbench/workbench-creation-dossier.custom.shared.schemas.d.ts +49 -0
  54. package/dist/esm/workbench/workbench-creation-dossier.custom.shared.schemas.d.ts.map +1 -0
  55. package/dist/esm/workbench/workbench-creation-dossier.custom.shared.schemas.js +42 -0
  56. package/dist/esm/workbench/workbench-creation-dossier.custom.shared.schemas.js.map +1 -0
  57. package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.d.ts +461 -11
  58. package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.d.ts.map +1 -1
  59. package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.js +509 -6
  60. package/dist/esm/workbench/workbench-creation-plane.custom.shared.resources-config.schemas.js.map +1 -1
  61. package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.d.ts +1102 -12
  62. package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.d.ts.map +1 -1
  63. package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.js +1520 -19
  64. package/dist/esm/workbench/workbench-creation-plane.custom.shared.schemas.js.map +1 -1
  65. package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.d.ts +123 -1
  66. package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.d.ts.map +1 -1
  67. package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.js +168 -0
  68. package/dist/esm/workbench/workbench-resources-types.custom.shared.schemas.js.map +1 -1
  69. package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.d.ts +229 -9
  70. package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.d.ts.map +1 -1
  71. package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.js +2 -2
  72. package/dist/esm/workbench/workbench-resources.custom.shared.resources-config.schemas.js.map +1 -1
  73. package/dist/esm/workbench/workbench-resources.custom.shared.schemas.d.ts +18 -3
  74. package/dist/esm/workbench/workbench-resources.custom.shared.schemas.d.ts.map +1 -1
  75. package/dist/esm/workbench/workbench-resources.custom.shared.schemas.js +35 -7
  76. package/dist/esm/workbench/workbench-resources.custom.shared.schemas.js.map +1 -1
  77. package/dist/tsconfig.build.tsbuildinfo +1 -1
  78. package/package.json +4 -13
  79. package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.d.ts +0 -98
  80. package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.d.ts.map +0 -1
  81. package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.js +0 -86
  82. package/dist/esm/workbench/workbench-creation-board.custom.shared.schemas.js.map +0 -1
@@ -101,6 +101,12 @@ export const WORKBENCH_ASPECT_DESCRIPTORS = [
101
101
  rowsPath: 'marketIcps',
102
102
  identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
103
103
  edges: [
104
+ /*
105
+ * The SELF-edge (#1427): an ICP's `influences` name OTHER ICPs, carried inside objects and
106
+ * invisible to the registry until it walked one level down. It is what makes a buying group
107
+ * legible — the economic buyer an end user influences is why both are on record.
108
+ */
109
+ { field: 'influencesIcpIds', targetAspectKey: 'market.icp', many: true },
104
110
  { field: 'personaIds', targetAspectKey: 'market.persona', many: true },
105
111
  { field: 'jobIds', targetAspectKey: 'market.job', many: true },
106
112
  { field: 'painIds', targetAspectKey: 'market.pain', many: true },
@@ -316,6 +322,15 @@ export const WORKBENCH_ASPECT_DESCRIPTORS = [
316
322
  identity: WorkbenchAspectIdentityKind.SINGLETON,
317
323
  edges: [],
318
324
  },
325
+ {
326
+ aspectKey: 'brand.audience',
327
+ resourceType: WorkbenchShared_ResourceType.ASPECT_BRAND_AUDIENCE,
328
+ rowsPath: 'brandAudiences',
329
+ identity: WorkbenchAspectIdentityKind.SINGLETON,
330
+ edges: [
331
+ { field: 'targetIcpIds', targetAspectKey: 'market.icp', many: true },
332
+ ],
333
+ },
319
334
  {
320
335
  aspectKey: 'brand.asset',
321
336
  resourceType: WorkbenchShared_ResourceType.ASPECT_BRAND_ASSET,
@@ -325,6 +340,120 @@ export const WORKBENCH_ASPECT_DESCRIPTORS = [
325
340
  { field: 'derivedFromId', targetAspectKey: 'brand.asset', many: false },
326
341
  ],
327
342
  },
343
+ /*
344
+ * THE COMPLIANCE PROGRAMME (#1431, surfaced #1512). Ten aspects, and the edge set is the point:
345
+ * an obligation reaches the decision that made it applicable, the assignment that owns it, the
346
+ * gap that blocks it and the coverage decision that reviewed it — the whole reason this family is
347
+ * addressable at all.
348
+ *
349
+ * SEVERAL registry fields collapse into ONE spine field wherever they share a target, because the
350
+ * projection joins a reference to the descriptor edge whose target matches. A responsibility
351
+ * assignment names five distinct ROLE fields and they become one `roleIds` link; the distinction
352
+ * is not lost, it is on the row, where each field renders under its own word. What the edge
353
+ * carries is the click-through, which is the same for all five.
354
+ */
355
+ {
356
+ aspectKey: 'compliance-program.scope-selector',
357
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_SCOPE_SELECTOR,
358
+ rowsPath: 'complianceScopeSelectors',
359
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
360
+ edges: [],
361
+ },
362
+ {
363
+ aspectKey: 'compliance-program.applicability-decision',
364
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_APPLICABILITY_DECISION,
365
+ rowsPath: 'complianceApplicabilityDecisions',
366
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
367
+ edges: [
368
+ { field: 'scopeSelectorId', targetAspectKey: 'compliance-program.scope-selector', many: false },
369
+ ],
370
+ },
371
+ {
372
+ aspectKey: 'compliance-program.obligation',
373
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_OBLIGATION,
374
+ rowsPath: 'complianceObligations',
375
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
376
+ edges: [
377
+ { field: 'scopeSelectorIds', targetAspectKey: 'compliance-program.scope-selector', many: true },
378
+ { field: 'responsibilityAssignmentIds', targetAspectKey: 'compliance-program.responsibility-assignment', many: true },
379
+ { field: 'reviewOwnerRoleIds', targetAspectKey: 'compliance-program.role-authority', many: true },
380
+ { field: 'gapIds', targetAspectKey: 'compliance-program.gap', many: true },
381
+ { field: 'coverageDecisionId', targetAspectKey: 'compliance-program.obligation-coverage-decision', many: false },
382
+ { field: 'sourceApplicabilityDecisionIds', targetAspectKey: 'compliance-program.applicability-decision', many: true },
383
+ // A SELF-edge: an obligation may depend on another obligation being met first.
384
+ { field: 'dependencyObligationIds', targetAspectKey: 'compliance-program.obligation', many: true },
385
+ { field: 'sharedResponsibilityRuleId', targetAspectKey: 'compliance-program.shared-responsibility-rule', many: false },
386
+ ],
387
+ },
388
+ {
389
+ aspectKey: 'compliance-program.obligation-coverage-decision',
390
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_COVERAGE_DECISION,
391
+ rowsPath: 'complianceCoverageDecisions',
392
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
393
+ edges: [
394
+ { field: 'obligationId', targetAspectKey: 'compliance-program.obligation', many: false },
395
+ { field: 'reviewedByRoleId', targetAspectKey: 'compliance-program.role-authority', many: false },
396
+ ],
397
+ },
398
+ {
399
+ aspectKey: 'compliance-program.implementation-decision',
400
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_IMPLEMENTATION_DECISION,
401
+ rowsPath: 'complianceImplementationDecisions',
402
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
403
+ edges: [
404
+ { field: 'scopeSelectorIds', targetAspectKey: 'compliance-program.scope-selector', many: true },
405
+ { field: 'gapIds', targetAspectKey: 'compliance-program.gap', many: true },
406
+ ],
407
+ },
408
+ {
409
+ aspectKey: 'compliance-program.responsibility-assignment',
410
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_RESPONSIBILITY_ASSIGNMENT,
411
+ rowsPath: 'complianceResponsibilityAssignments',
412
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
413
+ edges: [
414
+ { field: 'scopeSelectorId', targetAspectKey: 'compliance-program.scope-selector', many: false },
415
+ // FIVE registry fields — responsible, operating, evidence-provider, approving, escalation —
416
+ // one target, so one link. Each renders under its own word on the row.
417
+ { field: 'roleIds', targetAspectKey: 'compliance-program.role-authority', many: true },
418
+ ],
419
+ },
420
+ {
421
+ aspectKey: 'compliance-program.gap',
422
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_GAP,
423
+ rowsPath: 'complianceGaps',
424
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
425
+ edges: [
426
+ { field: 'scopeSelectorId', targetAspectKey: 'compliance-program.scope-selector', many: false },
427
+ { field: 'ownerRoleId', targetAspectKey: 'compliance-program.role-authority', many: false },
428
+ ],
429
+ },
430
+ {
431
+ aspectKey: 'compliance-program.documentation-requirement',
432
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_DOCUMENTATION_REQUIREMENT,
433
+ rowsPath: 'complianceDocumentationRequirements',
434
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
435
+ edges: [
436
+ { field: 'approvingRoleIds', targetAspectKey: 'compliance-program.role-authority', many: true },
437
+ ],
438
+ },
439
+ {
440
+ aspectKey: 'compliance-program.role-authority',
441
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_ROLE_AUTHORITY,
442
+ rowsPath: 'complianceRoleAuthorities',
443
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
444
+ edges: [],
445
+ },
446
+ {
447
+ aspectKey: 'compliance-program.shared-responsibility-rule',
448
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_SHARED_RESPONSIBILITY_RULE,
449
+ rowsPath: 'complianceSharedResponsibilityRules',
450
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
451
+ edges: [
452
+ { field: 'scopeSelectorIds', targetAspectKey: 'compliance-program.scope-selector', many: true },
453
+ // Five role fields again — accountable, operating, evidence-provider, approving, conflict.
454
+ { field: 'roleIds', targetAspectKey: 'compliance-program.role-authority', many: true },
455
+ ],
456
+ },
328
457
  {
329
458
  aspectKey: 'domain-plan.module',
330
459
  resourceType: WorkbenchShared_ResourceType.ASPECT_DOMAIN_PLAN_MODULE,
@@ -332,12 +461,40 @@ export const WORKBENCH_ASPECT_DESCRIPTORS = [
332
461
  identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
333
462
  edges: [],
334
463
  },
464
+ /*
465
+ * The DERIVED resource (#1482, surfaced #1512). Its rows are a projection of
466
+ * `modules[].mainResources` rather than a member of the snapshot — a domain plan lists resources
467
+ * as plain strings — and it is the single most-travelled link in the build lane: a build task's
468
+ * `target.resourceRef` and `produces.resourceRef`, a placement decision's `subject.resourceRef`
469
+ * and an experience view's `resourceId` all land here. Without this row every one of those was an
470
+ * edge the graph could declare and not follow.
471
+ */
472
+ {
473
+ aspectKey: 'domain-plan.resource',
474
+ resourceType: WorkbenchShared_ResourceType.ASPECT_DOMAIN_PLAN_RESOURCE,
475
+ rowsPath: 'domainPlanResources',
476
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
477
+ edges: [
478
+ // Back to the module that lists it — the one edge a projected row carries.
479
+ { field: 'moduleId', targetAspectKey: 'domain-plan.module', many: false },
480
+ ],
481
+ },
335
482
  {
336
483
  aspectKey: 'domain-plan.relationship',
337
484
  resourceType: WorkbenchShared_ResourceType.ASPECT_DOMAIN_PLAN_RELATIONSHIP,
338
485
  rowsPath: 'domainPlanRelationships',
339
486
  identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
340
- edges: [],
487
+ edges: [
488
+ /*
489
+ * The two ENDS, as ONE spine field. The registry declares `sourceResourceRef` and
490
+ * `targetResourceRef` separately and both land on the same aspect, and the projection joins a
491
+ * reference to the descriptor edge whose target matches — so one target is one field, by
492
+ * construction. The DIRECTION is not lost: it is on the row itself, where `sourceResourceRef`
493
+ * and `targetResourceRef` render as "From" and "To"; what this field carries is the
494
+ * click-through, which is the same for both ends.
495
+ */
496
+ { field: 'relatesResourceIds', targetAspectKey: 'domain-plan.resource', many: true },
497
+ ],
341
498
  },
342
499
  {
343
500
  aspectKey: 'build-plan.task',
@@ -346,11 +503,218 @@ export const WORKBENCH_ASPECT_DESCRIPTORS = [
346
503
  identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
347
504
  edges: [
348
505
  { field: 'prerequisiteTaskIds', targetAspectKey: 'build-plan.task', many: true },
506
+ /*
507
+ * THE LINK BACK TO THE DECISION (#1427): a task's `target` names the domain-plan module or
508
+ * relationship it builds. Carried inside an object, so the registry could not see it until its
509
+ * drift pin walked one level down — and the spine could not link it until the registry did.
510
+ */
511
+ { field: 'targetModuleId', targetAspectKey: 'domain-plan.module', many: false },
512
+ { field: 'targetRelationshipId', targetAspectKey: 'domain-plan.relationship', many: false },
513
+ /*
514
+ * The most-travelled link in the build lane (#1482): a task's `target.resourceRef` and its
515
+ * `produces.resourceRef` both name the resource it builds. Two registry fields, one target,
516
+ * so one spine field — what is built and what it produces are the same resource.
517
+ */
518
+ { field: 'resourceIds', targetAspectKey: 'domain-plan.resource', many: true },
519
+ /* A task may serve a DRAFTED FEATURE (#1433 row 4) — declared once the data was measured. */
520
+ { field: 'servesFeatureDraftIds', targetAspectKey: 'feature-drafts.draft', many: true },
521
+ { field: 'servesFunctionalRequirementIds', targetAspectKey: 'requirements.functional', many: true },
522
+ { field: 'servesNonFunctionalRequirementIds', targetAspectKey: 'requirements.non-functional', many: true },
523
+ { field: 'servesRoadmapPhaseIds', targetAspectKey: 'roadmap.phase', many: true },
524
+ /* The vision edges (#629) — see the registry for why a build task may serve these. */
525
+ { field: 'servesSuccessCriterionIds', targetAspectKey: 'vision.success-criterion', many: true },
526
+ { field: 'servesConstraintIds', targetAspectKey: 'vision.constraint', many: true },
527
+ /*
528
+ * The experience plan's entries on the PRODUCT side (#1660): a view, milestone or guidance task
529
+ * names the entry it builds by the plan's own ref, so the task links to the decision.
530
+ */
531
+ { field: 'producesViewIds', targetAspectKey: 'experience-plan.view', many: true },
532
+ { field: 'producesMilestoneIds', targetAspectKey: 'experience-plan.milestone', many: true },
533
+ { field: 'producesGuidanceIds', targetAspectKey: 'experience-plan.guidance', many: true },
534
+ /* A billing-catalogue task names the pricing plan it realises in `servesRefs` (#1835). */
535
+ { field: 'servesPricingPlanIds', targetAspectKey: 'business-model.plan', many: true },
536
+ ],
537
+ },
538
+ {
539
+ /*
540
+ * One composition decision per framework scenario (#1491) — NO edges: a decision names a
541
+ * SCENARIO (a framework template) and a basis (a closed vocabulary), neither of which is a
542
+ * decided element another aspect addresses.
543
+ */
544
+ aspectKey: 'capability-composition.decision',
545
+ resourceType: WorkbenchShared_ResourceType.ASPECT_CAPABILITY_COMPOSITION_DECISION,
546
+ rowsPath: 'compositionDecisions',
547
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
548
+ edges: [],
549
+ },
550
+ {
551
+ /*
552
+ * One planned website page (#1492). Its two edges reach the MARKET — a page is written for an
553
+ * ICP, in a tone of voice — which is what makes the plan checkable from the snapshot's side.
554
+ */
555
+ aspectKey: 'website-plan.page',
556
+ resourceType: WorkbenchShared_ResourceType.ASPECT_WEBSITE_PAGE,
557
+ rowsPath: 'websitePages',
558
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
559
+ edges: [
560
+ { field: 'audienceIcpIds', targetAspectKey: 'market.icp', many: true },
561
+ { field: 'toneOfVoiceId', targetAspectKey: 'market.tone-of-voice', many: false },
562
+ ],
563
+ },
564
+ {
565
+ /* One planned surface of the application's experience (#1493) — where it lives, and what asked for it. */
566
+ aspectKey: 'experience-plan.view',
567
+ resourceType: WorkbenchShared_ResourceType.ASPECT_EXPERIENCE_VIEW,
568
+ rowsPath: 'experienceViews',
569
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
570
+ edges: [
571
+ { field: 'moduleId', targetAspectKey: 'domain-plan.module', many: false },
572
+ { field: 'resourceId', targetAspectKey: 'domain-plan.resource', many: false },
573
+ { field: 'servesFunctionalRequirementIds', targetAspectKey: 'requirements.functional', many: true },
574
+ { field: 'servesNonFunctionalRequirementIds', targetAspectKey: 'requirements.non-functional', many: true },
575
+ { field: 'servesRoadmapPhaseIds', targetAspectKey: 'roadmap.phase', many: true },
576
+ { field: 'servesFeatureDraftIds', targetAspectKey: 'feature-drafts.draft', many: true },
577
+ { field: 'servesSuccessCriterionIds', targetAspectKey: 'vision.success-criterion', many: true },
578
+ ],
579
+ },
580
+ {
581
+ /* One configuration surface the application composes — no edge: it names files and tokens, not elements. */
582
+ aspectKey: 'experience-plan.configuration',
583
+ resourceType: WorkbenchShared_ResourceType.ASPECT_EXPERIENCE_CONFIGURATION,
584
+ rowsPath: 'experienceConfigurations',
585
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
586
+ edges: [],
587
+ },
588
+ {
589
+ /* One moment the application recognises — the module declaring it and the resource that earns it. */
590
+ aspectKey: 'experience-plan.milestone',
591
+ resourceType: WorkbenchShared_ResourceType.ASPECT_EXPERIENCE_MILESTONE,
592
+ rowsPath: 'experienceMilestones',
593
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
594
+ edges: [
595
+ { field: 'moduleId', targetAspectKey: 'domain-plan.module', many: false },
596
+ { field: 'triggerResourceId', targetAspectKey: 'domain-plan.resource', many: false },
349
597
  { field: 'servesFunctionalRequirementIds', targetAspectKey: 'requirements.functional', many: true },
350
598
  { field: 'servesNonFunctionalRequirementIds', targetAspectKey: 'requirements.non-functional', many: true },
351
599
  { field: 'servesRoadmapPhaseIds', targetAspectKey: 'roadmap.phase', many: true },
600
+ { field: 'servesFeatureDraftIds', targetAspectKey: 'feature-drafts.draft', many: true },
601
+ { field: 'servesSuccessCriterionIds', targetAspectKey: 'vision.success-criterion', many: true },
352
602
  ],
353
603
  },
604
+ {
605
+ /* One piece of guidance, linked to the milestones a person must hold first — the journey as one path. */
606
+ aspectKey: 'experience-plan.guidance',
607
+ resourceType: WorkbenchShared_ResourceType.ASPECT_EXPERIENCE_GUIDANCE,
608
+ rowsPath: 'experienceGuidance',
609
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
610
+ edges: [
611
+ { field: 'moduleId', targetAspectKey: 'domain-plan.module', many: false },
612
+ { field: 'gatedByMilestoneIds', targetAspectKey: 'experience-plan.milestone', many: true },
613
+ { field: 'hostViewId', targetAspectKey: 'experience-plan.view', many: false },
614
+ { field: 'servesFunctionalRequirementIds', targetAspectKey: 'requirements.functional', many: true },
615
+ { field: 'servesNonFunctionalRequirementIds', targetAspectKey: 'requirements.non-functional', many: true },
616
+ { field: 'servesRoadmapPhaseIds', targetAspectKey: 'roadmap.phase', many: true },
617
+ { field: 'servesFeatureDraftIds', targetAspectKey: 'feature-drafts.draft', many: true },
618
+ { field: 'servesSuccessCriterionIds', targetAspectKey: 'vision.success-criterion', many: true },
619
+ ],
620
+ },
621
+ {
622
+ /*
623
+ * One worker the backend delegates to (#1495). Its only edges are what asked for it: a worker's
624
+ * routes are the BUILT package's units of work, not decided elements.
625
+ */
626
+ aspectKey: 'integration-plan.worker',
627
+ resourceType: WorkbenchShared_ResourceType.ASPECT_INTEGRATION_WORKER,
628
+ rowsPath: 'integrationWorkers',
629
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
630
+ edges: [
631
+ { field: 'servesFunctionalRequirementIds', targetAspectKey: 'requirements.functional', many: true },
632
+ { field: 'servesNonFunctionalRequirementIds', targetAspectKey: 'requirements.non-functional', many: true },
633
+ { field: 'servesRoadmapPhaseIds', targetAspectKey: 'roadmap.phase', many: true },
634
+ { field: 'servesFeatureDraftIds', targetAspectKey: 'feature-drafts.draft', many: true },
635
+ { field: 'servesSuccessCriterionIds', targetAspectKey: 'vision.success-criterion', many: true },
636
+ ],
637
+ },
638
+ {
639
+ /*
640
+ * One cron minion (#1495) — and the ONE aspect whose edges are not unique by target: its
641
+ * containment whitelist reaches `domain-plan.resource` TWICE, once per direction, so both
642
+ * carry `sourceField`. The direction is the decision, and collapsing the two would report a
643
+ * minion as reading what it only writes.
644
+ */
645
+ aspectKey: 'integration-plan.minion',
646
+ resourceType: WorkbenchShared_ResourceType.ASPECT_INTEGRATION_MINION,
647
+ rowsPath: 'integrationMinions',
648
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
649
+ edges: [
650
+ { field: 'readsResourceIds', sourceField: 'readsResourceRefs', targetAspectKey: 'domain-plan.resource', many: true },
651
+ { field: 'writesResourceIds', sourceField: 'writesResourceRefs', targetAspectKey: 'domain-plan.resource', many: true },
652
+ { field: 'servesFunctionalRequirementIds', targetAspectKey: 'requirements.functional', many: true },
653
+ { field: 'servesNonFunctionalRequirementIds', targetAspectKey: 'requirements.non-functional', many: true },
654
+ { field: 'servesRoadmapPhaseIds', targetAspectKey: 'roadmap.phase', many: true },
655
+ { field: 'servesFeatureDraftIds', targetAspectKey: 'feature-drafts.draft', many: true },
656
+ { field: 'servesSuccessCriterionIds', targetAspectKey: 'vision.success-criterion', many: true },
657
+ ],
658
+ },
659
+ {
660
+ /* One provider bound into one scope — no edge: a provider ref is the ENGINE's catalogue, not a decided element. */
661
+ aspectKey: 'integration-plan.provider-binding',
662
+ resourceType: WorkbenchShared_ResourceType.ASPECT_INTEGRATION_PROVIDER_BINDING,
663
+ rowsPath: 'integrationProviderBindings',
664
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
665
+ edges: [],
666
+ },
667
+ {
668
+ /* One channel work arrives on (#1630). Its only edges are what asked for it, like a worker's. */
669
+ aspectKey: 'integration-plan.inbound-channel',
670
+ resourceType: WorkbenchShared_ResourceType.ASPECT_INTEGRATION_INBOUND_CHANNEL,
671
+ rowsPath: 'integrationInboundChannels',
672
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
673
+ edges: [
674
+ { field: 'servesFunctionalRequirementIds', targetAspectKey: 'requirements.functional', many: true },
675
+ { field: 'servesNonFunctionalRequirementIds', targetAspectKey: 'requirements.non-functional', many: true },
676
+ { field: 'servesRoadmapPhaseIds', targetAspectKey: 'roadmap.phase', many: true },
677
+ { field: 'servesFeatureDraftIds', targetAspectKey: 'feature-drafts.draft', many: true },
678
+ { field: 'servesSuccessCriterionIds', targetAspectKey: 'vision.success-criterion', many: true },
679
+ ],
680
+ },
681
+ {
682
+ /*
683
+ * One requirement's standing (#1496), and the aspect that carries BOTH multi-edge shapes at
684
+ * once — which is why every edge here names its source field.
685
+ *
686
+ * Its business key IS its edge: the row exists to say something about ONE requirement, and
687
+ * `requirementRef` reaches both halves of the family — one field, two targets — routed by
688
+ * MEMBERSHIP rather than by the row's own `half` field, which it does carry. An aspect edge
689
+ * declares its targets ONCE and has no per-row selection, and membership MEASURES where a ref
690
+ * lives instead of trusting a landed file's claim about it. `landedTaskIds` and
691
+ * `provenTaskIds` are the other shape, two fields to ONE target, and they must stay apart
692
+ * because proven is a subset of landed and collapsing them would report work as verified that
693
+ * only exists.
694
+ *
695
+ * The projection therefore joins on the PAIR. Naming the source field on all four is what makes
696
+ * that join load-bearing rather than incidental: with them omitted the target-only fallback
697
+ * happens to resolve the same rows, and a regression in the join would go unnoticed.
698
+ */
699
+ aspectKey: 'application-maturity.requirement',
700
+ resourceType: WorkbenchShared_ResourceType.ASPECT_REQUIREMENT_MATURITY,
701
+ rowsPath: 'requirementMaturities',
702
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
703
+ edges: [
704
+ { field: 'functionalRequirementId', sourceField: 'requirementRef', targetAspectKey: 'requirements.functional', many: false },
705
+ { field: 'nonFunctionalRequirementId', sourceField: 'requirementRef', targetAspectKey: 'requirements.non-functional', many: false },
706
+ { field: 'landedTaskIds', sourceField: 'landedTaskRefs', targetAspectKey: 'build-plan.task', many: true },
707
+ { field: 'provenTaskIds', sourceField: 'provenTaskRefs', targetAspectKey: 'build-plan.task', many: true },
708
+ ],
709
+ },
710
+ {
711
+ /* One planned documentation chapter (#1494) — no edge: it cites the BUILT product's own specifications. */
712
+ aspectKey: 'documentation-plan.chapter',
713
+ resourceType: WorkbenchShared_ResourceType.ASPECT_DOCUMENTATION_CHAPTER,
714
+ rowsPath: 'documentationChapters',
715
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
716
+ edges: [],
717
+ },
354
718
  {
355
719
  aspectKey: 'placement.group',
356
720
  resourceType: WorkbenchShared_ResourceType.ASPECT_PLACEMENT_GROUP,
@@ -365,6 +729,13 @@ export const WORKBENCH_ASPECT_DESCRIPTORS = [
365
729
  identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
366
730
  edges: [
367
731
  { field: 'groupId', targetAspectKey: 'placement.group', many: false },
732
+ /*
733
+ * A placement decision's SUBJECT is a domain-plan module or a RESOURCE (#1427/#1482), both
734
+ * carried inside an object. The resource half was the one the graph could not follow until
735
+ * the projected resource got a row of its own.
736
+ */
737
+ { field: 'subjectModuleId', targetAspectKey: 'domain-plan.module', many: false },
738
+ { field: 'subjectResourceId', targetAspectKey: 'domain-plan.resource', many: false },
368
739
  ],
369
740
  },
370
741
  {
@@ -372,6 +743,10 @@ export const WORKBENCH_ASPECT_DESCRIPTORS = [
372
743
  resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_CANDIDATE_SECTION,
373
744
  rowsPath: 'complianceCandidateSections',
374
745
  identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
746
+ /*
747
+ * No edges since #1465: the producer family lands into the SET and is never indexed, so its
748
+ * clause → fact link moved to `compliance-candidate-set.section`, the aspect that is read.
749
+ */
375
750
  edges: [],
376
751
  },
377
752
  {
@@ -413,6 +788,18 @@ export const WORKBENCH_ASPECT_DESCRIPTORS = [
413
788
  identity: WorkbenchAspectIdentityKind.INDEXED,
414
789
  edges: [],
415
790
  },
791
+ {
792
+ /*
793
+ * BUSINESS_KEY, unlike the processor entry above: a risk entry carries `entryRef`, which the
794
+ * family validator holds unique and which `reviewOrder` addresses rows by. An indexed identity
795
+ * here would make the register's own reading order unable to name the rows it orders.
796
+ */
797
+ aspectKey: 'risk-register.entry',
798
+ resourceType: WorkbenchShared_ResourceType.ASPECT_RISK_REGISTER_ENTRY,
799
+ rowsPath: 'riskRegisterEntries',
800
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
801
+ edges: [],
802
+ },
416
803
  {
417
804
  aspectKey: 'release-policy.selection',
418
805
  resourceType: WorkbenchShared_ResourceType.ASPECT_RELEASE_POLICY_SELECTION,
@@ -427,6 +814,59 @@ export const WORKBENCH_ASPECT_DESCRIPTORS = [
427
814
  identity: WorkbenchAspectIdentityKind.INDEXED,
428
815
  edges: [],
429
816
  },
817
+ /*
818
+ * The two landed SET families of the governed-document lane (#1105). A drafting run EMITS one
819
+ * candidate (the singular family the two `compliance-candidate.*` descriptors above transcribe) and
820
+ * LANDS into the set; a composed release lands into the release set. Without these two, the set that
821
+ * actually landed had no popup and no graph row — a journey that drafted the privacy notice showed the
822
+ * draft's sections only through the producer's contract.
823
+ */
824
+ {
825
+ aspectKey: 'compliance-candidate-set.candidate',
826
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_CANDIDATE,
827
+ rowsPath: 'complianceCandidates',
828
+ // Keyed on `documentRef`: the identity decision the set schema records (locale and jurisdictions
829
+ // are publication-variant concerns and would make a draft's identity change as it matured).
830
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
831
+ edges: [
832
+ /*
833
+ * These rows CITE facts, and a fact is either DERIVED from the application or DECIDED by a
834
+ * person — one registry edge with two targets, which the projection routes by membership, so
835
+ * the spine needs one field per target (#1427).
836
+ */
837
+ { field: 'citesFactIds', targetAspectKey: 'document-facts.declaration', many: true },
838
+ { field: 'citesDecidedFactIds', targetAspectKey: 'document-facts-authored.declaration', many: true },
839
+ ],
840
+ },
841
+ {
842
+ /*
843
+ * One SECTION of a landed candidate (#1465), derived from the set's `candidates` and addressed by
844
+ * the composed `<documentRef>/<sectionRef>` — a section ref alone repeats across documents.
845
+ */
846
+ aspectKey: 'compliance-candidate-set.section',
847
+ resourceType: WorkbenchShared_ResourceType.ASPECT_COMPLIANCE_CANDIDATE_SET_SECTION,
848
+ rowsPath: 'complianceCandidateSetSections',
849
+ identity: WorkbenchAspectIdentityKind.BUSINESS_KEY,
850
+ edges: [
851
+ // Back to the candidate the section belongs to.
852
+ { field: 'complianceCandidateId', targetAspectKey: 'compliance-candidate-set.candidate', many: false },
853
+ /*
854
+ * The section's CLAUSES cite the facts they rest on — derived or decided — one registry edge with
855
+ * two targets, routed by membership, so one spine field per target (#1427).
856
+ */
857
+ { field: 'citesFactIds', targetAspectKey: 'document-facts.declaration', many: true },
858
+ { field: 'citesDecidedFactIds', targetAspectKey: 'document-facts-authored.declaration', many: true },
859
+ ],
860
+ },
861
+ {
862
+ aspectKey: 'document-release-set.release',
863
+ resourceType: WorkbenchShared_ResourceType.ASPECT_DOCUMENT_RELEASE,
864
+ rowsPath: 'documentReleases',
865
+ // A release is keyed by document × locale × jurisdictions — no single-field business key, so the
866
+ // registry falls back to the indexed identity, as the publication variant beside it does.
867
+ identity: WorkbenchAspectIdentityKind.INDEXED,
868
+ edges: [],
869
+ },
430
870
  ];
431
871
  const DESCRIPTOR_BY_ASPECT_KEY = new Map(WORKBENCH_ASPECT_DESCRIPTORS.map((descriptor) => [descriptor.aspectKey, descriptor]));
432
872
  export function getWorkbenchAspectDescriptor(aspectKey) {