helixevo 0.5.0 → 0.6.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.
- package/CHANGELOG.md +15 -0
- package/README.md +10 -7
- package/dashboard/app/coevolution/client.tsx +21 -1
- package/dashboard/app/commands/page.tsx +63 -9
- package/dashboard/app/guide/page.tsx +68 -9
- package/dashboard/app/ontology/client.tsx +57 -9
- package/dashboard/app/page.tsx +10 -0
- package/dashboard/app/topology/client.tsx +3 -0
- package/dashboard/components/sidebar-nav.tsx +9 -5
- package/dashboard/lib/data.ts +631 -52
- package/dist/cli.js +528 -49
- package/package.json +1 -1
package/dashboard/lib/data.ts
CHANGED
|
@@ -188,6 +188,8 @@ export interface PressureRouteRecommendation {
|
|
|
188
188
|
governanceMode: GovernanceModeName
|
|
189
189
|
triggeredBy: PressureRouteTrigger
|
|
190
190
|
reasons: string[]
|
|
191
|
+
semanticInfluence?: OntologySemanticInfluence
|
|
192
|
+
semanticConceptIds?: string[]
|
|
191
193
|
}
|
|
192
194
|
|
|
193
195
|
export interface PressureIntervention {
|
|
@@ -310,6 +312,8 @@ export interface TopologyReviewDecision {
|
|
|
310
312
|
export interface ResolvedTopologyReviewCandidate extends TopologyReviewCandidate {
|
|
311
313
|
status: TopologyReviewStatus
|
|
312
314
|
latestDecision?: TopologyReviewDecision
|
|
315
|
+
semanticBindings?: ResolvedOntologyBinding[]
|
|
316
|
+
semanticConceptIds?: string[]
|
|
313
317
|
lastActivityAt: string
|
|
314
318
|
}
|
|
315
319
|
|
|
@@ -401,6 +405,8 @@ export interface ResolvedPressureSignal extends PressureSignal {
|
|
|
401
405
|
interventionTypes: PressureInterventionType[]
|
|
402
406
|
motifIds?: string[]
|
|
403
407
|
routeRecommendation?: PressureRouteRecommendation
|
|
408
|
+
semanticBindings?: ResolvedOntologyBinding[]
|
|
409
|
+
semanticConceptIds?: string[]
|
|
404
410
|
lastActivityAt: string
|
|
405
411
|
addressedAt?: string
|
|
406
412
|
responseSummary: string
|
|
@@ -409,6 +415,8 @@ export interface ResolvedPressureSignal extends PressureSignal {
|
|
|
409
415
|
export interface ResolvedPressureMotif extends PressureMotif {
|
|
410
416
|
lifecycle: PressureLifecycleState
|
|
411
417
|
interventionTypes: PressureInterventionType[]
|
|
418
|
+
semanticBindings?: ResolvedOntologyBinding[]
|
|
419
|
+
semanticConceptIds?: string[]
|
|
412
420
|
lastActivityAt: string
|
|
413
421
|
addressedAt?: string
|
|
414
422
|
responseSummary: string
|
|
@@ -432,6 +440,11 @@ export interface ProjectResponseSummary extends ProjectPressureSummary {
|
|
|
432
440
|
recentInterventionTypes: PressureInterventionType[]
|
|
433
441
|
}
|
|
434
442
|
|
|
443
|
+
export interface ResolvedTransferEvent extends TransferEvent {
|
|
444
|
+
semanticBindings?: ResolvedOntologyBinding[]
|
|
445
|
+
semanticConceptIds?: string[]
|
|
446
|
+
}
|
|
447
|
+
|
|
435
448
|
export interface CoEvolutionDashboardSummary {
|
|
436
449
|
pressureSignals: {
|
|
437
450
|
total: number
|
|
@@ -494,7 +507,7 @@ export interface CoEvolutionDashboardSummary {
|
|
|
494
507
|
promoteQueue: ResolvedPressureMotif[]
|
|
495
508
|
recentAddressed: ResolvedPressureSignal[]
|
|
496
509
|
recentInterventions: PressureIntervention[]
|
|
497
|
-
recentTransfers:
|
|
510
|
+
recentTransfers: ResolvedTransferEvent[]
|
|
498
511
|
topologyBacklog: ResolvedTopologyReviewCandidate[]
|
|
499
512
|
recentTopologyDecisions: ResolvedTopologyReviewCandidate[]
|
|
500
513
|
}
|
|
@@ -515,6 +528,9 @@ export type OntologyConceptKind = 'entity' | 'relation-subtype' | 'capability' |
|
|
|
515
528
|
export type OntologyLayer = 'kernel' | 'extension' | 'frontier'
|
|
516
529
|
export type OntologyReviewDecisionStatus = 'promote' | 'reject' | 'defer'
|
|
517
530
|
export type OntologyReviewStatus = 'open' | 'promoted' | 'rejected' | 'deferred'
|
|
531
|
+
export type OntologyBindingTargetType = 'pressure-signal' | 'pressure-motif' | 'route-recommendation' | 'transfer-event' | 'topology-review'
|
|
532
|
+
export type OntologyBindingSourceKind = 'capability-match' | 'pressure-region' | 'motif-match' | 'transfer-motif' | 'topology-descriptor'
|
|
533
|
+
export type OntologySemanticInfluence = 'none' | 'explanatory' | 'weighted'
|
|
518
534
|
|
|
519
535
|
export interface OntologyKernelSnapshot {
|
|
520
536
|
version: string
|
|
@@ -569,6 +585,52 @@ export interface ResolvedOntologyConcept extends OntologyConcept {
|
|
|
569
585
|
lastActivityAt: string
|
|
570
586
|
}
|
|
571
587
|
|
|
588
|
+
export interface ResolvedOntologyBinding {
|
|
589
|
+
id: string
|
|
590
|
+
conceptId: string
|
|
591
|
+
conceptName: string
|
|
592
|
+
conceptKind: OntologyConceptKind
|
|
593
|
+
targetType: OntologyBindingTargetType
|
|
594
|
+
targetId: string
|
|
595
|
+
sourceKind: OntologyBindingSourceKind
|
|
596
|
+
confidence: number
|
|
597
|
+
reasons: string[]
|
|
598
|
+
effectSummary?: string
|
|
599
|
+
}
|
|
600
|
+
|
|
601
|
+
export interface OntologyConsumerSummary {
|
|
602
|
+
conceptId: string
|
|
603
|
+
conceptName: string
|
|
604
|
+
conceptKind: OntologyConceptKind
|
|
605
|
+
status: OntologyConcept['status']
|
|
606
|
+
totalBindings: number
|
|
607
|
+
bindingsByTargetType: Partial<Record<OntologyBindingTargetType, number>>
|
|
608
|
+
activeTargetIds: string[]
|
|
609
|
+
lastActivityAt?: string
|
|
610
|
+
warning?: string
|
|
611
|
+
}
|
|
612
|
+
|
|
613
|
+
export interface ResolvedOntologyExtension extends OntologyConcept {
|
|
614
|
+
semanticBindings: ResolvedOntologyBinding[]
|
|
615
|
+
adoptionCount: number
|
|
616
|
+
bindingsByTargetType: Partial<Record<OntologyBindingTargetType, number>>
|
|
617
|
+
lastActivityAt: string
|
|
618
|
+
warning?: string
|
|
619
|
+
}
|
|
620
|
+
|
|
621
|
+
export interface OntologyAdoptionSummary {
|
|
622
|
+
activeConcepts: number
|
|
623
|
+
unusedExtensions: number
|
|
624
|
+
totalBindings: number
|
|
625
|
+
routesInfluenced: number
|
|
626
|
+
conceptsWithConsumers: number
|
|
627
|
+
conceptsAtDeprecationRisk: number
|
|
628
|
+
bindingsByTargetType: Partial<Record<OntologyBindingTargetType, number>>
|
|
629
|
+
usageByConceptKind: Partial<Record<OntologyConceptKind, number>>
|
|
630
|
+
topActiveConcepts: OntologyConsumerSummary[]
|
|
631
|
+
atRiskConcepts: OntologyConsumerSummary[]
|
|
632
|
+
}
|
|
633
|
+
|
|
572
634
|
export interface OntologyChangeEvent {
|
|
573
635
|
id: string
|
|
574
636
|
timestamp: string
|
|
@@ -646,6 +708,15 @@ export interface OntologyDashboardSummary {
|
|
|
646
708
|
deprecated: number
|
|
647
709
|
changeEvents: number
|
|
648
710
|
byConceptKind: Partial<Record<OntologyConceptKind, number>>
|
|
711
|
+
adoption: {
|
|
712
|
+
activeConcepts: number
|
|
713
|
+
unusedExtensions: number
|
|
714
|
+
totalBindings: number
|
|
715
|
+
routesInfluenced: number
|
|
716
|
+
conceptsAtDeprecationRisk: number
|
|
717
|
+
bindingsByTargetType: Partial<Record<OntologyBindingTargetType, number>>
|
|
718
|
+
usageByConceptKind: Partial<Record<OntologyConceptKind, number>>
|
|
719
|
+
}
|
|
649
720
|
}
|
|
650
721
|
governance: {
|
|
651
722
|
activeMode: GovernanceModeName
|
|
@@ -690,8 +761,9 @@ export interface OntologyControlDashboardSummary {
|
|
|
690
761
|
governance: ActiveGovernanceSummary
|
|
691
762
|
kernel: OntologyKernelSnapshot
|
|
692
763
|
summary: OntologyDashboardSummary['ontologyLoop']
|
|
764
|
+
adoption: OntologyAdoptionSummary
|
|
693
765
|
frontier: ResolvedOntologyConcept[]
|
|
694
|
-
extensions:
|
|
766
|
+
extensions: ResolvedOntologyExtension[]
|
|
695
767
|
recentChanges: OntologyChangeEvent[]
|
|
696
768
|
}
|
|
697
769
|
|
|
@@ -701,6 +773,14 @@ function emptyCounts<T extends string>(values: readonly T[]): Record<T, number>
|
|
|
701
773
|
return Object.fromEntries(values.map((value) => [value, 0])) as Record<T, number>
|
|
702
774
|
}
|
|
703
775
|
|
|
776
|
+
function uniqueStrings(values: Array<string | undefined | null>): string[] {
|
|
777
|
+
return [...new Set(values.filter((value): value is string => Boolean(value && value.trim())))]
|
|
778
|
+
}
|
|
779
|
+
|
|
780
|
+
function slugFragment(value: string): string {
|
|
781
|
+
return value.replace(/[^a-zA-Z0-9_-]/g, '-').replace(/-+/g, '-').slice(0, 80)
|
|
782
|
+
}
|
|
783
|
+
|
|
704
784
|
function inferCognitiveRole(node: SkillNode): CognitiveRole {
|
|
705
785
|
if (node.cognitiveRole) return node.cognitiveRole
|
|
706
786
|
if (node.layer === 'project' || Boolean(node.project)) return 'specialist'
|
|
@@ -952,7 +1032,9 @@ export function getOntologyReviewSummary() {
|
|
|
952
1032
|
const kernel = loadOntologyKernelSnapshot()
|
|
953
1033
|
const frontier = loadResolvedOntologyFrontierConcepts()
|
|
954
1034
|
const extensions = loadOntologyExtensions()
|
|
1035
|
+
const resolvedExtensions = loadResolvedOntologyExtensions()
|
|
955
1036
|
const changes = loadOntologyChangeEvents()
|
|
1037
|
+
const adoption = getOntologyAdoptionSummary()
|
|
956
1038
|
const byConceptKind = ontologyKindCounts()
|
|
957
1039
|
for (const concept of [...frontier, ...extensions]) {
|
|
958
1040
|
byConceptKind[concept.conceptKind] = (byConceptKind[concept.conceptKind] ?? 0) + 1
|
|
@@ -968,9 +1050,10 @@ export function getOntologyReviewSummary() {
|
|
|
968
1050
|
deprecated: extensions.filter((concept) => concept.status === 'deprecated').length,
|
|
969
1051
|
changeEvents: changes.length,
|
|
970
1052
|
byConceptKind,
|
|
1053
|
+
adoption,
|
|
971
1054
|
backlog: frontier.filter((concept) => concept.reviewStatus === 'open' || concept.reviewStatus === 'deferred').slice(0, 10),
|
|
972
1055
|
recentChanges: changes.slice(0, 10),
|
|
973
|
-
recentExtensions:
|
|
1056
|
+
recentExtensions: resolvedExtensions.slice(0, 8),
|
|
974
1057
|
}
|
|
975
1058
|
}
|
|
976
1059
|
|
|
@@ -979,8 +1062,9 @@ export function loadOntologyControlSummary(): OntologyControlDashboardSummary {
|
|
|
979
1062
|
governance: getActiveGovernanceSummary(),
|
|
980
1063
|
kernel: loadOntologyKernelSnapshot(),
|
|
981
1064
|
summary: getOntologyReviewSummary(),
|
|
1065
|
+
adoption: getOntologyAdoptionSummary(),
|
|
982
1066
|
frontier: loadResolvedOntologyFrontierConcepts(),
|
|
983
|
-
extensions:
|
|
1067
|
+
extensions: loadResolvedOntologyExtensions(),
|
|
984
1068
|
recentChanges: loadOntologyChangeEvents().slice(0, 10),
|
|
985
1069
|
}
|
|
986
1070
|
}
|
|
@@ -1133,9 +1217,18 @@ export function loadPressureInterventions(): PressureIntervention[] {
|
|
|
1133
1217
|
.sort((a, b) => (b.completedAt ?? b.createdAt).localeCompare(a.completedAt ?? a.createdAt))
|
|
1134
1218
|
}
|
|
1135
1219
|
|
|
1136
|
-
export function loadTransferEvents():
|
|
1220
|
+
export function loadTransferEvents(): ResolvedTransferEvent[] {
|
|
1221
|
+
const concepts = activeOntologyExtensions()
|
|
1137
1222
|
return readJsonl<TransferEvent>('transfer-events.jsonl')
|
|
1138
1223
|
.slice()
|
|
1224
|
+
.map((event) => {
|
|
1225
|
+
const semanticBindings = matchTransferOntologyBindings(event, concepts)
|
|
1226
|
+
return {
|
|
1227
|
+
...event,
|
|
1228
|
+
semanticBindings,
|
|
1229
|
+
semanticConceptIds: uniqueStrings(semanticBindings.map((binding) => binding.conceptId)),
|
|
1230
|
+
} satisfies ResolvedTransferEvent
|
|
1231
|
+
})
|
|
1139
1232
|
.sort((a, b) => b.timestamp.localeCompare(a.timestamp))
|
|
1140
1233
|
}
|
|
1141
1234
|
|
|
@@ -1193,6 +1286,7 @@ export function loadTopologyReviewDecisions(): TopologyReviewDecision[] {
|
|
|
1193
1286
|
|
|
1194
1287
|
export function loadResolvedTopologyReviewCandidates(): ResolvedTopologyReviewCandidate[] {
|
|
1195
1288
|
const candidates = loadStoredTopologyReviewCandidates()
|
|
1289
|
+
const concepts = activeOntologyExtensions()
|
|
1196
1290
|
const latestDecisionByCandidate = new Map<string, TopologyReviewDecision>()
|
|
1197
1291
|
for (const decision of loadTopologyReviewDecisions()) {
|
|
1198
1292
|
const existing = latestDecisionByCandidate.get(decision.candidateId)
|
|
@@ -1202,10 +1296,18 @@ export function loadResolvedTopologyReviewCandidates(): ResolvedTopologyReviewCa
|
|
|
1202
1296
|
.map((candidate) => {
|
|
1203
1297
|
const latestDecision = latestDecisionByCandidate.get(candidate.id)
|
|
1204
1298
|
const status: TopologyReviewStatus = latestDecision?.decision ?? 'open'
|
|
1299
|
+
const semanticBindings = matchTopologyOntologyBindings({
|
|
1300
|
+
...candidate,
|
|
1301
|
+
status,
|
|
1302
|
+
latestDecision,
|
|
1303
|
+
lastActivityAt: maxTimestamp(candidate.lastObservedAt, latestDecision?.decidedAt),
|
|
1304
|
+
}, concepts)
|
|
1205
1305
|
return {
|
|
1206
1306
|
...candidate,
|
|
1207
1307
|
status,
|
|
1208
1308
|
latestDecision,
|
|
1309
|
+
semanticBindings,
|
|
1310
|
+
semanticConceptIds: uniqueStrings(semanticBindings.map((binding) => binding.conceptId)),
|
|
1209
1311
|
lastActivityAt: maxTimestamp(candidate.lastObservedAt, latestDecision?.decidedAt),
|
|
1210
1312
|
} satisfies ResolvedTopologyReviewCandidate
|
|
1211
1313
|
})
|
|
@@ -1471,22 +1573,356 @@ export function getActiveGovernanceSummary(): ActiveGovernanceSummary {
|
|
|
1471
1573
|
return deriveGovernanceSummary()
|
|
1472
1574
|
}
|
|
1473
1575
|
|
|
1576
|
+
function emptyOntologyBindingCounts(): Partial<Record<OntologyBindingTargetType, number>> {
|
|
1577
|
+
return {}
|
|
1578
|
+
}
|
|
1579
|
+
|
|
1580
|
+
function ontologyReviewKeyBody(reviewKey?: string): string {
|
|
1581
|
+
return normalizePressureValue((reviewKey ?? '').split(':').slice(1).join('-'))
|
|
1582
|
+
}
|
|
1583
|
+
|
|
1584
|
+
function ontologyMotifTerm(motifId: string): string {
|
|
1585
|
+
return normalizePressureValue(motifId.replace(/^motif_/, ''))
|
|
1586
|
+
}
|
|
1587
|
+
|
|
1588
|
+
function ontologyConceptTerms(concept: OntologyConcept): string[] {
|
|
1589
|
+
return uniqueStrings([
|
|
1590
|
+
normalizePressureValue(concept.name),
|
|
1591
|
+
...((concept.aliases ?? []).map((alias) => normalizePressureValue(alias))),
|
|
1592
|
+
normalizePressureValue(concept.reviewKey),
|
|
1593
|
+
ontologyReviewKeyBody(concept.reviewKey),
|
|
1594
|
+
...((concept.relatedMotifIds ?? []).map((id) => ontologyMotifTerm(id))),
|
|
1595
|
+
...((concept.derivedFromKinds ?? []).map((value) => normalizePressureValue(value))),
|
|
1596
|
+
].filter(Boolean))
|
|
1597
|
+
}
|
|
1598
|
+
|
|
1599
|
+
function ontologyBindingId(conceptId: string, targetType: OntologyBindingTargetType, targetId: string, sourceKind: OntologyBindingSourceKind): string {
|
|
1600
|
+
return `ontology_binding_${slugFragment(conceptId)}_${targetType}_${slugFragment(targetId)}_${sourceKind}`
|
|
1601
|
+
}
|
|
1602
|
+
|
|
1603
|
+
function buildOntologyBinding(params: {
|
|
1604
|
+
concept: OntologyConcept
|
|
1605
|
+
targetType: OntologyBindingTargetType
|
|
1606
|
+
targetId: string
|
|
1607
|
+
sourceKind: OntologyBindingSourceKind
|
|
1608
|
+
confidence: number
|
|
1609
|
+
reasons: string[]
|
|
1610
|
+
effectSummary?: string
|
|
1611
|
+
}): ResolvedOntologyBinding {
|
|
1612
|
+
return {
|
|
1613
|
+
id: ontologyBindingId(params.concept.id, params.targetType, params.targetId, params.sourceKind),
|
|
1614
|
+
conceptId: params.concept.id,
|
|
1615
|
+
conceptName: params.concept.name,
|
|
1616
|
+
conceptKind: params.concept.conceptKind,
|
|
1617
|
+
targetType: params.targetType,
|
|
1618
|
+
targetId: params.targetId,
|
|
1619
|
+
sourceKind: params.sourceKind,
|
|
1620
|
+
confidence: Math.min(0.95, Math.max(0.5, params.confidence)),
|
|
1621
|
+
reasons: params.reasons,
|
|
1622
|
+
effectSummary: params.effectSummary,
|
|
1623
|
+
}
|
|
1624
|
+
}
|
|
1625
|
+
|
|
1626
|
+
function dedupeOntologyBindings(bindings: ResolvedOntologyBinding[]): ResolvedOntologyBinding[] {
|
|
1627
|
+
const byKey = new Map<string, ResolvedOntologyBinding>()
|
|
1628
|
+
for (const binding of bindings) {
|
|
1629
|
+
const key = `${binding.conceptId}::${binding.targetType}::${binding.targetId}`
|
|
1630
|
+
const existing = byKey.get(key)
|
|
1631
|
+
if (!existing || binding.confidence > existing.confidence) byKey.set(key, binding)
|
|
1632
|
+
}
|
|
1633
|
+
return [...byKey.values()]
|
|
1634
|
+
.sort((a, b) => b.confidence - a.confidence || a.conceptName.localeCompare(b.conceptName))
|
|
1635
|
+
.slice(0, 4)
|
|
1636
|
+
}
|
|
1637
|
+
|
|
1638
|
+
function activeOntologyExtensions(): OntologyConcept[] {
|
|
1639
|
+
return loadOntologyExtensions().filter((concept) => concept.status === 'active')
|
|
1640
|
+
}
|
|
1641
|
+
|
|
1642
|
+
function matchSignalOntologyBindings(signal: PressureSignal, motifIds: string[], concepts: OntologyConcept[]): ResolvedOntologyBinding[] {
|
|
1643
|
+
const capability = normalizePressureValue(signal.capability)
|
|
1644
|
+
const region = normalizePressureValue(signal.region)
|
|
1645
|
+
const kind = normalizePressureValue(signal.kind)
|
|
1646
|
+
const motifTerms = uniqueStrings(motifIds.map((id) => ontologyMotifTerm(id)).filter(Boolean))
|
|
1647
|
+
const bindings: ResolvedOntologyBinding[] = []
|
|
1648
|
+
|
|
1649
|
+
for (const concept of concepts) {
|
|
1650
|
+
const terms = ontologyConceptTerms(concept)
|
|
1651
|
+
if (concept.conceptKind === 'capability') {
|
|
1652
|
+
if (capability && terms.includes(capability)) {
|
|
1653
|
+
bindings.push(buildOntologyBinding({
|
|
1654
|
+
concept,
|
|
1655
|
+
targetType: 'pressure-signal',
|
|
1656
|
+
targetId: signal.id,
|
|
1657
|
+
sourceKind: 'capability-match',
|
|
1658
|
+
confidence: 0.86,
|
|
1659
|
+
reasons: [`Signal capability ${signal.capability} matches approved capability concept ${concept.name}.`],
|
|
1660
|
+
effectSummary: 'Sharpens project or capability interpretation for this pressure signal.',
|
|
1661
|
+
}))
|
|
1662
|
+
continue
|
|
1663
|
+
}
|
|
1664
|
+
if (region && terms.includes(region)) {
|
|
1665
|
+
bindings.push(buildOntologyBinding({
|
|
1666
|
+
concept,
|
|
1667
|
+
targetType: 'pressure-signal',
|
|
1668
|
+
targetId: signal.id,
|
|
1669
|
+
sourceKind: 'pressure-region',
|
|
1670
|
+
confidence: 0.74,
|
|
1671
|
+
reasons: [`Signal region ${signal.region} aligns with approved capability concept ${concept.name}.`],
|
|
1672
|
+
effectSummary: 'Adds semantic coverage to a recurring pressure region.',
|
|
1673
|
+
}))
|
|
1674
|
+
}
|
|
1675
|
+
continue
|
|
1676
|
+
}
|
|
1677
|
+
|
|
1678
|
+
if (concept.conceptKind === 'pressure-type') {
|
|
1679
|
+
if ((concept.relatedMotifIds ?? []).some((id) => motifIds.includes(id))) {
|
|
1680
|
+
bindings.push(buildOntologyBinding({
|
|
1681
|
+
concept,
|
|
1682
|
+
targetType: 'pressure-signal',
|
|
1683
|
+
targetId: signal.id,
|
|
1684
|
+
sourceKind: 'motif-match',
|
|
1685
|
+
confidence: 0.9,
|
|
1686
|
+
reasons: [`Signal participates in approved motif family ${concept.name}.`],
|
|
1687
|
+
effectSummary: 'Marks this signal as part of an approved recurring pressure family.',
|
|
1688
|
+
}))
|
|
1689
|
+
continue
|
|
1690
|
+
}
|
|
1691
|
+
if (motifTerms.some((term) => terms.includes(term))) {
|
|
1692
|
+
bindings.push(buildOntologyBinding({
|
|
1693
|
+
concept,
|
|
1694
|
+
targetType: 'pressure-signal',
|
|
1695
|
+
targetId: signal.id,
|
|
1696
|
+
sourceKind: 'motif-match',
|
|
1697
|
+
confidence: 0.84,
|
|
1698
|
+
reasons: [`Signal motif region aligns with approved pressure family ${concept.name}.`],
|
|
1699
|
+
effectSummary: 'Carries an approved recurring pressure family into live signal interpretation.',
|
|
1700
|
+
}))
|
|
1701
|
+
continue
|
|
1702
|
+
}
|
|
1703
|
+
if (kind && terms.includes(kind)) {
|
|
1704
|
+
bindings.push(buildOntologyBinding({
|
|
1705
|
+
concept,
|
|
1706
|
+
targetType: 'pressure-signal',
|
|
1707
|
+
targetId: signal.id,
|
|
1708
|
+
sourceKind: 'pressure-region',
|
|
1709
|
+
confidence: 0.78,
|
|
1710
|
+
reasons: [`Signal kind ${signal.kind} matches approved pressure family ${concept.name}.`],
|
|
1711
|
+
effectSummary: 'Adds semantic identity to recurring pressure of the same kind.',
|
|
1712
|
+
}))
|
|
1713
|
+
}
|
|
1714
|
+
}
|
|
1715
|
+
}
|
|
1716
|
+
|
|
1717
|
+
return dedupeOntologyBindings(bindings)
|
|
1718
|
+
}
|
|
1719
|
+
|
|
1720
|
+
function matchMotifOntologyBindings(motif: Pick<ResolvedPressureMotif, 'id' | 'key' | 'kind' | 'capability'>, concepts: OntologyConcept[]): ResolvedOntologyBinding[] {
|
|
1721
|
+
const capability = normalizePressureValue(motif.capability)
|
|
1722
|
+
const key = normalizePressureValue(motif.key)
|
|
1723
|
+
const kind = normalizePressureValue(motif.kind)
|
|
1724
|
+
const bindings: ResolvedOntologyBinding[] = []
|
|
1725
|
+
|
|
1726
|
+
for (const concept of concepts) {
|
|
1727
|
+
const terms = ontologyConceptTerms(concept)
|
|
1728
|
+
if (concept.conceptKind === 'capability' && capability && terms.includes(capability)) {
|
|
1729
|
+
bindings.push(buildOntologyBinding({
|
|
1730
|
+
concept,
|
|
1731
|
+
targetType: 'pressure-motif',
|
|
1732
|
+
targetId: motif.id,
|
|
1733
|
+
sourceKind: 'capability-match',
|
|
1734
|
+
confidence: 0.84,
|
|
1735
|
+
reasons: [`Motif capability ${motif.capability} matches approved capability concept ${concept.name}.`],
|
|
1736
|
+
effectSummary: 'Makes recurring capability pressure visible as semantic adoption.',
|
|
1737
|
+
}))
|
|
1738
|
+
continue
|
|
1739
|
+
}
|
|
1740
|
+
if (concept.conceptKind === 'pressure-type') {
|
|
1741
|
+
if ((concept.relatedMotifIds ?? []).includes(motif.id)) {
|
|
1742
|
+
bindings.push(buildOntologyBinding({
|
|
1743
|
+
concept,
|
|
1744
|
+
targetType: 'pressure-motif',
|
|
1745
|
+
targetId: motif.id,
|
|
1746
|
+
sourceKind: 'motif-match',
|
|
1747
|
+
confidence: 0.92,
|
|
1748
|
+
reasons: [`Motif ${motif.key} directly matches approved pressure family ${concept.name}.`],
|
|
1749
|
+
effectSummary: 'Confirms this recurring motif is an approved semantic family.',
|
|
1750
|
+
}))
|
|
1751
|
+
continue
|
|
1752
|
+
}
|
|
1753
|
+
if ([key, kind].filter(Boolean).some((value) => terms.includes(value))) {
|
|
1754
|
+
bindings.push(buildOntologyBinding({
|
|
1755
|
+
concept,
|
|
1756
|
+
targetType: 'pressure-motif',
|
|
1757
|
+
targetId: motif.id,
|
|
1758
|
+
sourceKind: 'pressure-region',
|
|
1759
|
+
confidence: 0.86,
|
|
1760
|
+
reasons: [`Motif identity aligns with approved pressure family ${concept.name}.`],
|
|
1761
|
+
effectSummary: 'Carries approved semantic family identity into motif-level routing.',
|
|
1762
|
+
}))
|
|
1763
|
+
}
|
|
1764
|
+
}
|
|
1765
|
+
}
|
|
1766
|
+
|
|
1767
|
+
return dedupeOntologyBindings(bindings)
|
|
1768
|
+
}
|
|
1769
|
+
|
|
1770
|
+
function matchTransferOntologyBindings(event: TransferEvent, concepts: OntologyConcept[]): ResolvedOntologyBinding[] {
|
|
1771
|
+
const capabilityTerms = uniqueStrings((event.capabilityIds ?? []).map((value) => normalizePressureValue(value)).filter(Boolean))
|
|
1772
|
+
const motifTerms = uniqueStrings((event.motifIds ?? []).map((value) => ontologyMotifTerm(value)).filter(Boolean))
|
|
1773
|
+
const bindings: ResolvedOntologyBinding[] = []
|
|
1774
|
+
|
|
1775
|
+
for (const concept of concepts) {
|
|
1776
|
+
const terms = ontologyConceptTerms(concept)
|
|
1777
|
+
if (concept.conceptKind === 'capability' && capabilityTerms.some((value) => terms.includes(value))) {
|
|
1778
|
+
bindings.push(buildOntologyBinding({
|
|
1779
|
+
concept,
|
|
1780
|
+
targetType: 'transfer-event',
|
|
1781
|
+
targetId: event.id,
|
|
1782
|
+
sourceKind: 'capability-match',
|
|
1783
|
+
confidence: 0.82,
|
|
1784
|
+
reasons: [`Transfer capabilities align with approved concept ${concept.name}.`],
|
|
1785
|
+
effectSummary: 'Shows semantic adoption through reusable transfer evidence.',
|
|
1786
|
+
}))
|
|
1787
|
+
continue
|
|
1788
|
+
}
|
|
1789
|
+
if (concept.conceptKind === 'pressure-type') {
|
|
1790
|
+
if ((concept.relatedMotifIds ?? []).some((id) => (event.motifIds ?? []).includes(id))) {
|
|
1791
|
+
bindings.push(buildOntologyBinding({
|
|
1792
|
+
concept,
|
|
1793
|
+
targetType: 'transfer-event',
|
|
1794
|
+
targetId: event.id,
|
|
1795
|
+
sourceKind: 'transfer-motif',
|
|
1796
|
+
confidence: 0.88,
|
|
1797
|
+
reasons: [`Transfer realizes approved recurring family ${concept.name}.`],
|
|
1798
|
+
effectSummary: 'Connects realized transfer evidence to an approved recurring pressure family.',
|
|
1799
|
+
}))
|
|
1800
|
+
continue
|
|
1801
|
+
}
|
|
1802
|
+
if (motifTerms.some((value) => terms.includes(value))) {
|
|
1803
|
+
bindings.push(buildOntologyBinding({
|
|
1804
|
+
concept,
|
|
1805
|
+
targetType: 'transfer-event',
|
|
1806
|
+
targetId: event.id,
|
|
1807
|
+
sourceKind: 'transfer-motif',
|
|
1808
|
+
confidence: 0.8,
|
|
1809
|
+
reasons: [`Transfer motif linkage aligns with approved family ${concept.name}.`],
|
|
1810
|
+
effectSummary: 'Carries approved semantic family identity into realized transfer outcomes.',
|
|
1811
|
+
}))
|
|
1812
|
+
}
|
|
1813
|
+
}
|
|
1814
|
+
}
|
|
1815
|
+
|
|
1816
|
+
return dedupeOntologyBindings(bindings)
|
|
1817
|
+
}
|
|
1818
|
+
|
|
1819
|
+
function topologyMotifDescriptor(candidate: ResolvedTopologyReviewCandidate): string | null {
|
|
1820
|
+
if (candidate.changeType === 'split' && candidate.title.startsWith('Split overloaded skill:')) return 'overloaded-skill-split-pressure'
|
|
1821
|
+
if (candidate.changeType === 'rewire' && candidate.title.startsWith('Resolve conflicting pair:')) return 'conflict-rewire-pressure'
|
|
1822
|
+
if (candidate.changeType === 'promote' && candidate.title.startsWith('Promote recurring motif:')) return 'motif-promotion-pressure'
|
|
1823
|
+
if (candidate.changeType === 'merge' && candidate.title.startsWith('Merge overlap:')) return 'overlap-merge-pressure'
|
|
1824
|
+
if (candidate.changeType === 'consolidate') return 'consolidation-pressure'
|
|
1825
|
+
return null
|
|
1826
|
+
}
|
|
1827
|
+
|
|
1828
|
+
function matchTopologyOntologyBindings(candidate: ResolvedTopologyReviewCandidate, concepts: OntologyConcept[]): ResolvedOntologyBinding[] {
|
|
1829
|
+
const descriptor = normalizePressureValue(topologyMotifDescriptor(candidate) ?? candidate.changeType)
|
|
1830
|
+
const bindings: ResolvedOntologyBinding[] = []
|
|
1831
|
+
|
|
1832
|
+
for (const concept of concepts) {
|
|
1833
|
+
const terms = ontologyConceptTerms(concept)
|
|
1834
|
+
if (concept.conceptKind === 'topology-motif' && descriptor && terms.includes(descriptor)) {
|
|
1835
|
+
bindings.push(buildOntologyBinding({
|
|
1836
|
+
concept,
|
|
1837
|
+
targetType: 'topology-review',
|
|
1838
|
+
targetId: candidate.id,
|
|
1839
|
+
sourceKind: 'topology-descriptor',
|
|
1840
|
+
confidence: 0.86,
|
|
1841
|
+
reasons: [`Topology review descriptor ${descriptor} aligns with approved topology concept ${concept.name}.`],
|
|
1842
|
+
effectSummary: 'Adds semantic identity to recurring structural review patterns.',
|
|
1843
|
+
}))
|
|
1844
|
+
continue
|
|
1845
|
+
}
|
|
1846
|
+
if (concept.conceptKind === 'pressure-type' && (concept.relatedMotifIds ?? []).some((id) => (candidate.relatedMotifIds ?? []).includes(id))) {
|
|
1847
|
+
bindings.push(buildOntologyBinding({
|
|
1848
|
+
concept,
|
|
1849
|
+
targetType: 'topology-review',
|
|
1850
|
+
targetId: candidate.id,
|
|
1851
|
+
sourceKind: 'motif-match',
|
|
1852
|
+
confidence: 0.72,
|
|
1853
|
+
reasons: [`Topology candidate inherits recurring pressure motif semantics from approved family ${concept.name}.`],
|
|
1854
|
+
effectSummary: 'Links structural review back to an approved recurring pressure family.',
|
|
1855
|
+
}))
|
|
1856
|
+
}
|
|
1857
|
+
}
|
|
1858
|
+
|
|
1859
|
+
return dedupeOntologyBindings(bindings)
|
|
1860
|
+
}
|
|
1861
|
+
|
|
1862
|
+
function applyOntologyInfluenceToRoute(params: {
|
|
1863
|
+
recommendation: PressureRouteRecommendation
|
|
1864
|
+
signal: PressureSignal
|
|
1865
|
+
semanticBindings: ResolvedOntologyBinding[]
|
|
1866
|
+
governanceProfile: GovernanceProfile
|
|
1867
|
+
}): PressureRouteRecommendation {
|
|
1868
|
+
const { recommendation, signal, semanticBindings, governanceProfile } = params
|
|
1869
|
+
if (semanticBindings.length === 0) return recommendation
|
|
1870
|
+
|
|
1871
|
+
const conceptIds = uniqueStrings(semanticBindings.map((binding) => binding.conceptId))
|
|
1872
|
+
const conceptNames = uniqueStrings(semanticBindings.map((binding) => binding.conceptName))
|
|
1873
|
+
const hasCapability = semanticBindings.some((binding) => binding.conceptKind === 'capability')
|
|
1874
|
+
const hasPressureType = semanticBindings.some((binding) => binding.conceptKind === 'pressure-type')
|
|
1875
|
+
const hasTopologyMotif = semanticBindings.some((binding) => binding.conceptKind === 'topology-motif')
|
|
1876
|
+
const reasons = [...recommendation.reasons]
|
|
1877
|
+
let confidence = recommendation.confidence
|
|
1878
|
+
let semanticInfluence: OntologySemanticInfluence = 'explanatory'
|
|
1879
|
+
|
|
1880
|
+
if (recommendation.route === 'generalize' && hasPressureType && governanceProfile.riskTolerance >= 0.4 && governanceProfile.reviewThreshold <= 0.82) {
|
|
1881
|
+
confidence = Math.min(0.95, confidence + 0.04)
|
|
1882
|
+
semanticInfluence = 'weighted'
|
|
1883
|
+
reasons.push(`Approved semantic family ${conceptNames.join(', ')} reinforces network-level reuse inside the current governance envelope.`)
|
|
1884
|
+
} else if (recommendation.route === 'specialize' && hasCapability && signal.projectId && governanceProfile.reviewThreshold <= 0.86) {
|
|
1885
|
+
confidence = Math.min(0.95, confidence + 0.03)
|
|
1886
|
+
semanticInfluence = 'weighted'
|
|
1887
|
+
reasons.push(`Approved capability concept ${conceptNames.join(', ')} sharpens project-bounded specialization without widening scope.`)
|
|
1888
|
+
} else if (recommendation.route === 'evolve' && hasPressureType && signal.relatedFailureId) {
|
|
1889
|
+
confidence = Math.min(0.95, confidence + 0.02)
|
|
1890
|
+
semanticInfluence = 'weighted'
|
|
1891
|
+
reasons.push(`Approved semantic family ${conceptNames.join(', ')} sharpens this failure-linked repair lane.`)
|
|
1892
|
+
} else if (recommendation.route === 'manual-review' && (hasTopologyMotif || hasPressureType)) {
|
|
1893
|
+
reasons.push(`Approved semantic family ${conceptNames.join(', ')} confirms the pattern is real, but governance still prefers explicit operator review.`)
|
|
1894
|
+
} else {
|
|
1895
|
+
reasons.push(`Approved semantic family ${conceptNames.join(', ')} is visible here, but current governance/evidence still keeps the route bounded to ${recommendation.route}.`)
|
|
1896
|
+
}
|
|
1897
|
+
|
|
1898
|
+
return {
|
|
1899
|
+
...recommendation,
|
|
1900
|
+
confidence,
|
|
1901
|
+
reasons,
|
|
1902
|
+
semanticInfluence,
|
|
1903
|
+
semanticConceptIds: conceptIds,
|
|
1904
|
+
}
|
|
1905
|
+
}
|
|
1906
|
+
|
|
1474
1907
|
function buildRouteRecommendation(params: {
|
|
1475
1908
|
signal: PressureSignal
|
|
1476
1909
|
relatedSignals: PressureSignal[]
|
|
1477
1910
|
linkedInterventions: PressureIntervention[]
|
|
1478
1911
|
governanceMode: GovernanceModeName
|
|
1912
|
+
governanceProfile: GovernanceProfile
|
|
1913
|
+
semanticBindings: ResolvedOntologyBinding[]
|
|
1479
1914
|
}): PressureRouteRecommendation {
|
|
1480
|
-
const { signal, relatedSignals, linkedInterventions, governanceMode } = params
|
|
1915
|
+
const { signal, relatedSignals, linkedInterventions, governanceMode, governanceProfile, semanticBindings } = params
|
|
1481
1916
|
const projectSpread = new Set(relatedSignals.map((entry) => entry.projectId ?? entry.projectPath).filter(Boolean)).size
|
|
1482
1917
|
const recurrenceCount = relatedSignals.length
|
|
1483
1918
|
const activeTypes = [...new Set(linkedInterventions.map((intervention) => intervention.interventionType))]
|
|
1484
1919
|
const reasons: string[] = []
|
|
1920
|
+
let recommendation: PressureRouteRecommendation
|
|
1485
1921
|
|
|
1486
1922
|
if (activeTypes.length >= 3 && !linkedInterventions.some((intervention) => intervention.status === 'completed' && intervention.impact === 'resolving')) {
|
|
1487
1923
|
reasons.push('Multiple intervention lanes already touch this pressure region without clear closure.')
|
|
1488
1924
|
reasons.push('Operator review is safer than blindly piling on another automated response.')
|
|
1489
|
-
|
|
1925
|
+
recommendation = {
|
|
1490
1926
|
route: 'manual-review',
|
|
1491
1927
|
scope: projectSpread >= 2 ? 'network' : signal.projectId ? 'project' : 'local',
|
|
1492
1928
|
confidence: 0.58,
|
|
@@ -1494,27 +1930,21 @@ function buildRouteRecommendation(params: {
|
|
|
1494
1930
|
triggeredBy: 'mixed-signal',
|
|
1495
1931
|
reasons,
|
|
1496
1932
|
}
|
|
1497
|
-
}
|
|
1498
|
-
|
|
1499
|
-
if (projectSpread >= 2 && recurrenceCount >= 2) {
|
|
1933
|
+
} else if (projectSpread >= 2 && recurrenceCount >= 2 && governanceMode !== 'project-critical') {
|
|
1500
1934
|
reasons.push(`This pressure region repeats across ${projectSpread} projects.`)
|
|
1501
1935
|
reasons.push('A network-level abstraction can absorb repeated local demand more efficiently than one-off fixes.')
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
reasons,
|
|
1510
|
-
}
|
|
1936
|
+
recommendation = {
|
|
1937
|
+
route: 'generalize',
|
|
1938
|
+
scope: 'network',
|
|
1939
|
+
confidence: Math.min(0.95, 0.7 + Math.min(0.15, (projectSpread - 1) * 0.08)),
|
|
1940
|
+
governanceMode,
|
|
1941
|
+
triggeredBy: 'recurring-cross-project-gap',
|
|
1942
|
+
reasons,
|
|
1511
1943
|
}
|
|
1512
|
-
}
|
|
1513
|
-
|
|
1514
|
-
if (signal.projectId && signal.priority === 'high' && signal.suggestedAction === 'specialize') {
|
|
1944
|
+
} else if (signal.projectId && signal.priority === 'high' && signal.suggestedAction === 'specialize') {
|
|
1515
1945
|
reasons.push('The pressure is high priority and bounded to a known project context.')
|
|
1516
1946
|
reasons.push('Project-layer adaptation is the shortest truthful path to response.')
|
|
1517
|
-
|
|
1947
|
+
recommendation = {
|
|
1518
1948
|
route: 'specialize',
|
|
1519
1949
|
scope: 'project',
|
|
1520
1950
|
confidence: 0.82,
|
|
@@ -1522,12 +1952,10 @@ function buildRouteRecommendation(params: {
|
|
|
1522
1952
|
triggeredBy: 'high-priority-local-gap',
|
|
1523
1953
|
reasons,
|
|
1524
1954
|
}
|
|
1525
|
-
}
|
|
1526
|
-
|
|
1527
|
-
if (signal.relatedFailureId && signal.severity >= 0.8) {
|
|
1955
|
+
} else if (signal.relatedFailureId && signal.severity >= 0.8) {
|
|
1528
1956
|
reasons.push('This pressure is tied to a concrete failure with strong corrective evidence.')
|
|
1529
1957
|
reasons.push('An evolution proposal can test a direct skill-level response against replay and regression evidence.')
|
|
1530
|
-
|
|
1958
|
+
recommendation = {
|
|
1531
1959
|
route: 'evolve',
|
|
1532
1960
|
scope: signal.projectId ? 'project' : 'local',
|
|
1533
1961
|
confidence: 0.78,
|
|
@@ -1535,12 +1963,10 @@ function buildRouteRecommendation(params: {
|
|
|
1535
1963
|
triggeredBy: 'accepted-iteration-pattern',
|
|
1536
1964
|
reasons,
|
|
1537
1965
|
}
|
|
1538
|
-
}
|
|
1539
|
-
|
|
1540
|
-
if (governanceMode === 'exploration' || signal.suggestedAction === 'research' || !signal.projectId) {
|
|
1966
|
+
} else if (governanceMode === 'exploration' || signal.suggestedAction === 'research' || !signal.projectId) {
|
|
1541
1967
|
reasons.push('The pressure still looks under-specified or capability-oriented.')
|
|
1542
1968
|
reasons.push('Research is the best way to widen evidence before committing to structural change.')
|
|
1543
|
-
|
|
1969
|
+
recommendation = {
|
|
1544
1970
|
route: 'research',
|
|
1545
1971
|
scope: signal.projectId ? 'project' : 'local',
|
|
1546
1972
|
confidence: 0.68,
|
|
@@ -1548,12 +1974,10 @@ function buildRouteRecommendation(params: {
|
|
|
1548
1974
|
triggeredBy: 'insufficient-evidence',
|
|
1549
1975
|
reasons,
|
|
1550
1976
|
}
|
|
1551
|
-
}
|
|
1552
|
-
|
|
1553
|
-
if (signal.projectId) {
|
|
1977
|
+
} else if (signal.projectId) {
|
|
1554
1978
|
reasons.push('The pressure is project-scoped and can likely be handled without network-wide promotion yet.')
|
|
1555
1979
|
reasons.push('Specialization is the most direct bounded intervention available.')
|
|
1556
|
-
|
|
1980
|
+
recommendation = {
|
|
1557
1981
|
route: 'specialize',
|
|
1558
1982
|
scope: 'project',
|
|
1559
1983
|
confidence: 0.7,
|
|
@@ -1561,18 +1985,25 @@ function buildRouteRecommendation(params: {
|
|
|
1561
1985
|
triggeredBy: 'project-bounded-recurrence',
|
|
1562
1986
|
reasons,
|
|
1563
1987
|
}
|
|
1988
|
+
} else {
|
|
1989
|
+
reasons.push('No single route is dominant from the current evidence.')
|
|
1990
|
+
reasons.push('Research keeps the loop moving while preserving optionality.')
|
|
1991
|
+
recommendation = {
|
|
1992
|
+
route: 'research',
|
|
1993
|
+
scope: 'local',
|
|
1994
|
+
confidence: 0.6,
|
|
1995
|
+
governanceMode,
|
|
1996
|
+
triggeredBy: 'insufficient-evidence',
|
|
1997
|
+
reasons,
|
|
1998
|
+
}
|
|
1564
1999
|
}
|
|
1565
2000
|
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
governanceMode,
|
|
1573
|
-
triggeredBy: 'insufficient-evidence',
|
|
1574
|
-
reasons,
|
|
1575
|
-
}
|
|
2001
|
+
return applyOntologyInfluenceToRoute({
|
|
2002
|
+
recommendation,
|
|
2003
|
+
signal,
|
|
2004
|
+
semanticBindings,
|
|
2005
|
+
governanceProfile,
|
|
2006
|
+
})
|
|
1576
2007
|
}
|
|
1577
2008
|
|
|
1578
2009
|
function interventionEvidenceAccepted(
|
|
@@ -1605,6 +2036,7 @@ export function loadResolvedPressureSignals(): ResolvedPressureSignal[] {
|
|
|
1605
2036
|
const history = loadHistory()
|
|
1606
2037
|
const artifacts = loadEvolutionArtifacts()
|
|
1607
2038
|
const governance = deriveGovernanceSummary(pressureSignals, interventions)
|
|
2039
|
+
const concepts = activeOntologyExtensions()
|
|
1608
2040
|
const acceptedProposalIds = new Set(
|
|
1609
2041
|
history.iterations.flatMap((iteration) => iteration.proposals)
|
|
1610
2042
|
.filter((proposal) => proposal.outcome === 'accepted')
|
|
@@ -1642,11 +2074,14 @@ export function loadResolvedPressureSignals(): ResolvedPressureSignal[] {
|
|
|
1642
2074
|
const relatedSignals = signalsByRegion.get(pressureRegionKey(signal)) ?? [signal]
|
|
1643
2075
|
const projectSpread = new Set(relatedSignals.map((entry) => entry.projectId ?? entry.projectPath).filter(Boolean)).size
|
|
1644
2076
|
const motifIds = relatedSignals.length >= 2 || projectSpread >= 2 ? [motifIdForSignal(signal)] : []
|
|
2077
|
+
const semanticBindings = matchSignalOntologyBindings(signal, motifIds, concepts)
|
|
1645
2078
|
const routeRecommendation = buildRouteRecommendation({
|
|
1646
2079
|
signal,
|
|
1647
2080
|
relatedSignals,
|
|
1648
2081
|
linkedInterventions,
|
|
1649
2082
|
governanceMode: governance.activeMode,
|
|
2083
|
+
governanceProfile: governance.profile,
|
|
2084
|
+
semanticBindings,
|
|
1650
2085
|
})
|
|
1651
2086
|
|
|
1652
2087
|
const lifecycle: PressureLifecycleState = addressingInterventions.length > 0 || signal.status === 'addressed'
|
|
@@ -1660,13 +2095,15 @@ export function loadResolvedPressureSignals(): ResolvedPressureSignal[] {
|
|
|
1660
2095
|
const lastActivityAt = [signal.detectedAt, latestInterventionAt].filter(Boolean).sort((a, b) => b.localeCompare(a))[0] ?? signal.detectedAt
|
|
1661
2096
|
const interventionTypes = [...new Set(linkedInterventions.map((intervention) => intervention.interventionType))]
|
|
1662
2097
|
const addressedAt = addressingInterventions.map((intervention) => intervention.completedAt ?? intervention.createdAt).sort((a, b) => b.localeCompare(a))[0]
|
|
2098
|
+
const semanticNames = uniqueStrings(semanticBindings.map((binding) => binding.conceptName))
|
|
2099
|
+
const semanticNote = semanticNames.length > 0 ? ` • semantic family ${semanticNames.join(', ')}` : ''
|
|
1663
2100
|
const responseSummary = lifecycle === 'addressed'
|
|
1664
|
-
? `Addressed by ${interventionTypes.join(', ') || 'linked intervention'} evidence`
|
|
2101
|
+
? `Addressed by ${interventionTypes.join(', ') || 'linked intervention'} evidence${semanticNote}`
|
|
1665
2102
|
: lifecycle === 'in-progress'
|
|
1666
|
-
? `Response underway via ${interventionTypes.join(', ')}`
|
|
2103
|
+
? `Response underway via ${interventionTypes.join(', ')}${semanticNote}`
|
|
1667
2104
|
: lifecycle === 'stale'
|
|
1668
2105
|
? 'Superseded by newer pressure in the same project/capability region'
|
|
1669
|
-
: `No linked response yet • recommend ${routeRecommendation.route}`
|
|
2106
|
+
: `No linked response yet • recommend ${routeRecommendation.route}${semanticNote}`
|
|
1670
2107
|
|
|
1671
2108
|
return {
|
|
1672
2109
|
...signal,
|
|
@@ -1675,6 +2112,8 @@ export function loadResolvedPressureSignals(): ResolvedPressureSignal[] {
|
|
|
1675
2112
|
interventionTypes,
|
|
1676
2113
|
motifIds,
|
|
1677
2114
|
routeRecommendation,
|
|
2115
|
+
semanticBindings,
|
|
2116
|
+
semanticConceptIds: uniqueStrings(semanticBindings.map((binding) => binding.conceptId)),
|
|
1678
2117
|
lastActivityAt,
|
|
1679
2118
|
addressedAt,
|
|
1680
2119
|
responseSummary,
|
|
@@ -1693,6 +2132,7 @@ export function loadPressureMotifs(): ResolvedPressureMotif[] {
|
|
|
1693
2132
|
const interventions = loadPressureInterventions()
|
|
1694
2133
|
const transferEvents = loadTransferEvents()
|
|
1695
2134
|
const governance = deriveGovernanceSummary()
|
|
2135
|
+
const concepts = activeOntologyExtensions()
|
|
1696
2136
|
const groups = new Map<string, ResolvedPressureSignal[]>()
|
|
1697
2137
|
|
|
1698
2138
|
for (const signal of resolvedSignals) {
|
|
@@ -1749,15 +2189,23 @@ export function loadPressureMotifs(): ResolvedPressureMotif[] {
|
|
|
1749
2189
|
const linkedInterventionIds = linkedInterventions.map((intervention) => intervention.id)
|
|
1750
2190
|
const linkedTransferEventIds = linkedTransferEvents.map((event) => event.id)
|
|
1751
2191
|
const highPriorityCount = signals.filter((signal) => signal.priority === 'high').length
|
|
2192
|
+
const semanticBindings = matchMotifOntologyBindings({
|
|
2193
|
+
id: motifId,
|
|
2194
|
+
key: regionKey,
|
|
2195
|
+
kind: signals[0]?.kind ?? regionKey,
|
|
2196
|
+
capability,
|
|
2197
|
+
}, concepts)
|
|
2198
|
+
const semanticNames = uniqueStrings(semanticBindings.map((binding) => binding.conceptName))
|
|
2199
|
+
const semanticNote = semanticNames.length > 0 ? ` • semantic family ${semanticNames.join(', ')}` : ''
|
|
1752
2200
|
const responseSummary = lifecycle === 'addressed'
|
|
1753
2201
|
? realizedTransfers.length > 0
|
|
1754
|
-
?
|
|
1755
|
-
:
|
|
2202
|
+
? `Promoted into reusable transfer evidence${semanticNote}`
|
|
2203
|
+
: `All linked pressure in this recurring region is addressed${semanticNote}`
|
|
1756
2204
|
: lifecycle === 'in-progress'
|
|
1757
|
-
? `Promotion or response underway via ${interventionTypes.join(', ') || 'linked intervention'}`
|
|
2205
|
+
? `Promotion or response underway via ${interventionTypes.join(', ') || 'linked intervention'}${semanticNote}`
|
|
1758
2206
|
: lifecycle === 'stale'
|
|
1759
2207
|
? 'Recurring pressure has been superseded by newer equivalent signals'
|
|
1760
|
-
: `Recurring demand is waiting for ${recommendation.route}`
|
|
2208
|
+
: `Recurring demand is waiting for ${recommendation.route}${semanticNote}`
|
|
1761
2209
|
|
|
1762
2210
|
motifs.push({
|
|
1763
2211
|
id: motifId,
|
|
@@ -1777,6 +2225,8 @@ export function loadPressureMotifs(): ResolvedPressureMotif[] {
|
|
|
1777
2225
|
suggestedRoute: recommendation,
|
|
1778
2226
|
lifecycle,
|
|
1779
2227
|
interventionTypes,
|
|
2228
|
+
semanticBindings,
|
|
2229
|
+
semanticConceptIds: uniqueStrings(semanticBindings.map((binding) => binding.conceptId)),
|
|
1780
2230
|
lastActivityAt,
|
|
1781
2231
|
addressedAt,
|
|
1782
2232
|
responseSummary,
|
|
@@ -1792,6 +2242,126 @@ export function loadPressureMotifs(): ResolvedPressureMotif[] {
|
|
|
1792
2242
|
})
|
|
1793
2243
|
}
|
|
1794
2244
|
|
|
2245
|
+
function collectOntologyAdoptionState(): { summary: OntologyAdoptionSummary, consumerMap: Map<string, OntologyConsumerSummary> } {
|
|
2246
|
+
const extensions = loadOntologyExtensions()
|
|
2247
|
+
const activeExtensions = extensions.filter((concept) => concept.status === 'active')
|
|
2248
|
+
const byConceptKind = ontologyKindCounts()
|
|
2249
|
+
const bindingsByTargetType = emptyOntologyBindingCounts()
|
|
2250
|
+
const consumerMap = new Map<string, OntologyConsumerSummary>()
|
|
2251
|
+
const conceptById = new Map(extensions.map((concept) => [concept.id, concept]))
|
|
2252
|
+
|
|
2253
|
+
function ensureConsumer(concept: OntologyConcept): OntologyConsumerSummary {
|
|
2254
|
+
const existing = consumerMap.get(concept.id)
|
|
2255
|
+
if (existing) return existing
|
|
2256
|
+
const created: OntologyConsumerSummary = {
|
|
2257
|
+
conceptId: concept.id,
|
|
2258
|
+
conceptName: concept.name,
|
|
2259
|
+
conceptKind: concept.conceptKind,
|
|
2260
|
+
status: concept.status,
|
|
2261
|
+
totalBindings: 0,
|
|
2262
|
+
bindingsByTargetType: emptyOntologyBindingCounts(),
|
|
2263
|
+
activeTargetIds: [],
|
|
2264
|
+
lastActivityAt: concept.lastObservedAt ?? concept.createdAt,
|
|
2265
|
+
}
|
|
2266
|
+
consumerMap.set(concept.id, created)
|
|
2267
|
+
return created
|
|
2268
|
+
}
|
|
2269
|
+
|
|
2270
|
+
function registerBinding(binding: ResolvedOntologyBinding) {
|
|
2271
|
+
const concept = conceptById.get(binding.conceptId)
|
|
2272
|
+
if (!concept || concept.status !== 'active') return
|
|
2273
|
+
const consumer = ensureConsumer(concept)
|
|
2274
|
+
consumer.totalBindings += 1
|
|
2275
|
+
consumer.bindingsByTargetType[binding.targetType] = (consumer.bindingsByTargetType[binding.targetType] ?? 0) + 1
|
|
2276
|
+
if (!consumer.activeTargetIds.includes(binding.targetId) && consumer.activeTargetIds.length < 12) {
|
|
2277
|
+
consumer.activeTargetIds.push(binding.targetId)
|
|
2278
|
+
}
|
|
2279
|
+
consumer.lastActivityAt = maxTimestamp(consumer.lastActivityAt, concept.lastObservedAt, concept.createdAt)
|
|
2280
|
+
bindingsByTargetType[binding.targetType] = (bindingsByTargetType[binding.targetType] ?? 0) + 1
|
|
2281
|
+
byConceptKind[concept.conceptKind] = (byConceptKind[concept.conceptKind] ?? 0) + 1
|
|
2282
|
+
}
|
|
2283
|
+
|
|
2284
|
+
const signals = loadResolvedPressureSignals()
|
|
2285
|
+
for (const signal of signals) {
|
|
2286
|
+
for (const binding of signal.semanticBindings ?? []) registerBinding(binding)
|
|
2287
|
+
if (signal.routeRecommendation?.semanticConceptIds?.length) {
|
|
2288
|
+
for (const conceptId of signal.routeRecommendation.semanticConceptIds) {
|
|
2289
|
+
const concept = conceptById.get(conceptId)
|
|
2290
|
+
if (!concept || concept.status !== 'active') continue
|
|
2291
|
+
registerBinding(buildOntologyBinding({
|
|
2292
|
+
concept,
|
|
2293
|
+
targetType: 'route-recommendation',
|
|
2294
|
+
targetId: `${signal.id}:route`,
|
|
2295
|
+
sourceKind: 'pressure-region',
|
|
2296
|
+
confidence: signal.routeRecommendation.semanticInfluence === 'weighted' ? 0.82 : 0.72,
|
|
2297
|
+
reasons: signal.routeRecommendation.reasons.slice(0, 2),
|
|
2298
|
+
effectSummary: `Route recommendation ${signal.routeRecommendation.route} is semantically informed by ${concept.name}.`,
|
|
2299
|
+
}))
|
|
2300
|
+
}
|
|
2301
|
+
}
|
|
2302
|
+
}
|
|
2303
|
+
|
|
2304
|
+
for (const motif of loadPressureMotifs()) {
|
|
2305
|
+
for (const binding of motif.semanticBindings ?? []) registerBinding(binding)
|
|
2306
|
+
}
|
|
2307
|
+
|
|
2308
|
+
for (const event of loadTransferEvents()) {
|
|
2309
|
+
for (const binding of event.semanticBindings ?? []) registerBinding(binding)
|
|
2310
|
+
}
|
|
2311
|
+
|
|
2312
|
+
for (const candidate of loadResolvedTopologyReviewCandidates()) {
|
|
2313
|
+
for (const binding of candidate.semanticBindings ?? []) registerBinding(binding)
|
|
2314
|
+
}
|
|
2315
|
+
|
|
2316
|
+
const activeConsumers = [...consumerMap.values()]
|
|
2317
|
+
.sort((a, b) => b.totalBindings - a.totalBindings || (b.lastActivityAt ?? '').localeCompare(a.lastActivityAt ?? ''))
|
|
2318
|
+
|
|
2319
|
+
const atRiskConcepts = activeConsumers
|
|
2320
|
+
.filter((consumer) => consumer.totalBindings > 0)
|
|
2321
|
+
.map((consumer) => ({
|
|
2322
|
+
...consumer,
|
|
2323
|
+
warning: `${consumer.totalBindings} active consumer${consumer.totalBindings === 1 ? '' : 's'} would be affected by deprecating ${consumer.conceptName}.`,
|
|
2324
|
+
}))
|
|
2325
|
+
|
|
2326
|
+
return {
|
|
2327
|
+
summary: {
|
|
2328
|
+
activeConcepts: activeConsumers.length,
|
|
2329
|
+
unusedExtensions: activeExtensions.filter((concept) => !consumerMap.has(concept.id)).length,
|
|
2330
|
+
totalBindings: activeConsumers.reduce((sum, consumer) => sum + consumer.totalBindings, 0),
|
|
2331
|
+
routesInfluenced: signals.filter((signal) => signal.routeRecommendation?.semanticInfluence && signal.routeRecommendation.semanticInfluence !== 'none').length,
|
|
2332
|
+
conceptsWithConsumers: activeConsumers.length,
|
|
2333
|
+
conceptsAtDeprecationRisk: atRiskConcepts.length,
|
|
2334
|
+
bindingsByTargetType,
|
|
2335
|
+
usageByConceptKind: byConceptKind,
|
|
2336
|
+
topActiveConcepts: activeConsumers.slice(0, 8),
|
|
2337
|
+
atRiskConcepts: atRiskConcepts.slice(0, 8),
|
|
2338
|
+
},
|
|
2339
|
+
consumerMap: new Map(activeConsumers.map((consumer) => [consumer.conceptId, consumer])),
|
|
2340
|
+
}
|
|
2341
|
+
}
|
|
2342
|
+
|
|
2343
|
+
export function getOntologyAdoptionSummary(): OntologyAdoptionSummary {
|
|
2344
|
+
return collectOntologyAdoptionState().summary
|
|
2345
|
+
}
|
|
2346
|
+
|
|
2347
|
+
export function loadResolvedOntologyExtensions(): ResolvedOntologyExtension[] {
|
|
2348
|
+
const extensions = loadOntologyExtensions()
|
|
2349
|
+
const { consumerMap } = collectOntologyAdoptionState()
|
|
2350
|
+
return extensions
|
|
2351
|
+
.map((concept) => {
|
|
2352
|
+
const consumer = consumerMap.get(concept.id)
|
|
2353
|
+
return {
|
|
2354
|
+
...concept,
|
|
2355
|
+
semanticBindings: [],
|
|
2356
|
+
adoptionCount: consumer?.totalBindings ?? 0,
|
|
2357
|
+
bindingsByTargetType: consumer?.bindingsByTargetType ?? emptyOntologyBindingCounts(),
|
|
2358
|
+
lastActivityAt: maxTimestamp(concept.lastObservedAt, consumer?.lastActivityAt, concept.createdAt),
|
|
2359
|
+
warning: consumer?.warning,
|
|
2360
|
+
} satisfies ResolvedOntologyExtension
|
|
2361
|
+
})
|
|
2362
|
+
.sort((a, b) => b.adoptionCount - a.adoptionCount || b.lastActivityAt.localeCompare(a.lastActivityAt) || a.name.localeCompare(b.name))
|
|
2363
|
+
}
|
|
2364
|
+
|
|
1795
2365
|
export function loadCoEvolutionSummary(): CoEvolutionDashboardSummary {
|
|
1796
2366
|
const graph = loadGraph()
|
|
1797
2367
|
const pressureSignals = loadPressureSignals()
|
|
@@ -2193,6 +2763,15 @@ export function loadOntologySummary(): OntologyDashboardSummary {
|
|
|
2193
2763
|
deprecated: ontologyReview.deprecated,
|
|
2194
2764
|
changeEvents: ontologyReview.changeEvents,
|
|
2195
2765
|
byConceptKind: ontologyReview.byConceptKind,
|
|
2766
|
+
adoption: {
|
|
2767
|
+
activeConcepts: ontologyReview.adoption.activeConcepts,
|
|
2768
|
+
unusedExtensions: ontologyReview.adoption.unusedExtensions,
|
|
2769
|
+
totalBindings: ontologyReview.adoption.totalBindings,
|
|
2770
|
+
routesInfluenced: ontologyReview.adoption.routesInfluenced,
|
|
2771
|
+
conceptsAtDeprecationRisk: ontologyReview.adoption.conceptsAtDeprecationRisk,
|
|
2772
|
+
bindingsByTargetType: ontologyReview.adoption.bindingsByTargetType,
|
|
2773
|
+
usageByConceptKind: ontologyReview.adoption.usageByConceptKind,
|
|
2774
|
+
},
|
|
2196
2775
|
},
|
|
2197
2776
|
governance: {
|
|
2198
2777
|
activeMode: governance.activeMode,
|