helixevo 0.4.1 → 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/dist/cli.js CHANGED
@@ -9489,6 +9489,48 @@ function writeJson(filename, data) {
9489
9489
  ensureDir(dirname(path));
9490
9490
  writeFileSync3(path, JSON.stringify(data, null, 2));
9491
9491
  }
9492
+ function getOntologyPath(filename) {
9493
+ return join3(getHelixDir(), "ontology", filename);
9494
+ }
9495
+ function readOntologyJson(filename, fallback) {
9496
+ const path = getOntologyPath(filename);
9497
+ if (!existsSync3(path))
9498
+ return fallback;
9499
+ return JSON.parse(readFileSync3(path, "utf-8"));
9500
+ }
9501
+ function writeOntologyJson(filename, data) {
9502
+ const path = getOntologyPath(filename);
9503
+ ensureDir(dirname(path));
9504
+ writeFileSync3(path, JSON.stringify(data, null, 2));
9505
+ }
9506
+ function readOntologyJsonl(filename) {
9507
+ const path = getOntologyPath(filename);
9508
+ if (!existsSync3(path))
9509
+ return [];
9510
+ const raw = readFileSync3(path, "utf-8").trim();
9511
+ if (!raw)
9512
+ return [];
9513
+ return raw.split(`
9514
+ `).filter(Boolean).map((line) => JSON.parse(line));
9515
+ }
9516
+ function appendOntologyJsonl(filename, record) {
9517
+ const path = getOntologyPath(filename);
9518
+ ensureDir(dirname(path));
9519
+ appendFileSync(path, JSON.stringify(record) + `
9520
+ `);
9521
+ }
9522
+ function kernelSnapshot() {
9523
+ return {
9524
+ version: ONTOLOGY_V0_SPEC.version,
9525
+ updatedAt: new Date().toISOString(),
9526
+ pillars: [...ONTOLOGY_V0_SPEC.pillars],
9527
+ layers: [...ONTOLOGY_V0_SPEC.layers],
9528
+ entityNames: [...ONTOLOGY_V0_SPEC.entityNames],
9529
+ relationFamilies: [...ONTOLOGY_V0_SPEC.relationFamilies],
9530
+ mutationOperations: [...ONTOLOGY_V0_SPEC.mutationOperations],
9531
+ invariantIds: [...ONTOLOGY_V0_SPEC.invariantIds]
9532
+ };
9533
+ }
9492
9534
  function loadFailures() {
9493
9535
  return readJsonl("failures.jsonl");
9494
9536
  }
@@ -9982,6 +10024,7 @@ function refreshTopologyReviewCandidates() {
9982
10024
  }
9983
10025
  function loadResolvedTopologyReviewCandidates() {
9984
10026
  const candidates = loadStoredTopologyReviewCandidates();
10027
+ const concepts = activeOntologyExtensions();
9985
10028
  const latestDecisionByCandidate = new Map;
9986
10029
  for (const decision of loadTopologyReviewDecisions()) {
9987
10030
  const existing = latestDecisionByCandidate.get(decision.candidateId);
@@ -9992,10 +10035,18 @@ function loadResolvedTopologyReviewCandidates() {
9992
10035
  return candidates.map((candidate) => {
9993
10036
  const latestDecision = latestDecisionByCandidate.get(candidate.id);
9994
10037
  const status = latestDecision?.decision ?? "open";
10038
+ const semanticBindings = matchTopologyOntologyBindings({
10039
+ ...candidate,
10040
+ status,
10041
+ latestDecision,
10042
+ lastActivityAt: maxTimestamp(candidate.lastObservedAt, latestDecision?.decidedAt)
10043
+ }, concepts);
9995
10044
  return {
9996
10045
  ...candidate,
9997
10046
  status,
9998
10047
  latestDecision,
10048
+ semanticBindings,
10049
+ semanticConceptIds: uniqueStrings(semanticBindings.map((binding) => binding.conceptId)),
9999
10050
  lastActivityAt: maxTimestamp(candidate.lastObservedAt, latestDecision?.decidedAt)
10000
10051
  };
10001
10052
  }).sort((a, b) => topologyStatusRank(b.status) - topologyStatusRank(a.status) || priorityRank(b.priority) - priorityRank(a.priority) || b.confidence - a.confidence || b.lastActivityAt.localeCompare(a.lastActivityAt) || a.title.localeCompare(b.title));
@@ -10019,6 +10070,472 @@ function getTopologyReviewSummary() {
10019
10070
  recentDecisions: candidates.filter((candidate) => candidate.status !== "open").slice(0, 8)
10020
10071
  };
10021
10072
  }
10073
+ function slugFragment(value) {
10074
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 72) || "concept";
10075
+ }
10076
+ function ontologyKindCounts() {
10077
+ return {};
10078
+ }
10079
+ function ontologyStatusRank(status) {
10080
+ return status === "open" ? 4 : status === "deferred" ? 3 : status === "rejected" ? 2 : 1;
10081
+ }
10082
+ function mergeConcept(existing, next) {
10083
+ return {
10084
+ ...existing,
10085
+ ...next,
10086
+ createdAt: existing.createdAt,
10087
+ aliases: uniqueStrings([...existing.aliases ?? [], ...next.aliases ?? []]),
10088
+ promotionCriteria: uniqueStrings([...existing.promotionCriteria ?? [], ...next.promotionCriteria ?? []]),
10089
+ projectIds: uniqueStrings([...existing.projectIds ?? [], ...next.projectIds ?? []]),
10090
+ evidenceIds: uniqueStrings([...existing.evidenceIds ?? [], ...next.evidenceIds ?? []]),
10091
+ relatedSignalIds: uniqueStrings([...existing.relatedSignalIds ?? [], ...next.relatedSignalIds ?? []]),
10092
+ relatedMotifIds: uniqueStrings([...existing.relatedMotifIds ?? [], ...next.relatedMotifIds ?? []]),
10093
+ relatedReviewIds: uniqueStrings([...existing.relatedReviewIds ?? [], ...next.relatedReviewIds ?? []]),
10094
+ relatedTransferEventIds: uniqueStrings([...existing.relatedTransferEventIds ?? [], ...next.relatedTransferEventIds ?? []]),
10095
+ derivedFromKinds: uniqueStrings([...existing.derivedFromKinds ?? [], ...next.derivedFromKinds ?? []]),
10096
+ observationCount: Math.max(existing.observationCount ?? 0, next.observationCount ?? 0),
10097
+ confidence: Math.max(existing.confidence ?? 0, next.confidence ?? 0),
10098
+ lastObservedAt: maxTimestamp(existing.lastObservedAt, next.lastObservedAt, existing.createdAt, next.createdAt),
10099
+ sourceSummary: next.sourceSummary ?? existing.sourceSummary,
10100
+ migrationMap: { ...existing.migrationMap ?? {}, ...next.migrationMap ?? {} }
10101
+ };
10102
+ }
10103
+ function conceptArtifactSummary(action, concept) {
10104
+ return `${action} ontology ${concept.conceptKind} concept ${concept.name} using ${concept.observationCount ?? 0} supporting observations.`;
10105
+ }
10106
+ function appendOntologyArtifact(params) {
10107
+ const artifact = {
10108
+ id: `ontology_artifact_${slugFragment(params.concept.id)}_${Date.now()}`,
10109
+ createdAt: new Date().toISOString(),
10110
+ artifactType: "ontology-review",
10111
+ provenance: "native-ontology",
10112
+ title: `${params.concept.name} · ontology ${params.operation}`,
10113
+ summary: params.summary,
10114
+ sourceId: params.sourceId,
10115
+ operation: `ontology:${params.operation}`,
10116
+ metrics: params.metrics
10117
+ };
10118
+ appendEvolutionArtifact(artifact);
10119
+ return artifact.id;
10120
+ }
10121
+ function loadOntologyKernelSnapshot() {
10122
+ return readOntologyJson("kernel.json", kernelSnapshot());
10123
+ }
10124
+ function materializeOntologyKernelSnapshot() {
10125
+ const snapshot = { ...kernelSnapshot(), updatedAt: new Date().toISOString() };
10126
+ writeOntologyJson("kernel.json", snapshot);
10127
+ return snapshot;
10128
+ }
10129
+ function loadOntologyExtensions() {
10130
+ return readOntologyJson("extensions.json", []).slice().sort((a, b) => (b.lastObservedAt ?? b.createdAt).localeCompare(a.lastObservedAt ?? a.createdAt) || a.name.localeCompare(b.name));
10131
+ }
10132
+ function saveOntologyExtensions(concepts) {
10133
+ writeOntologyJson("extensions.json", concepts);
10134
+ }
10135
+ function loadOntologyFrontier() {
10136
+ return readOntologyJson("frontier.json", []).slice().sort((a, b) => (b.lastObservedAt ?? b.createdAt).localeCompare(a.lastObservedAt ?? a.createdAt) || a.name.localeCompare(b.name));
10137
+ }
10138
+ function saveOntologyFrontier(concepts) {
10139
+ writeOntologyJson("frontier.json", concepts);
10140
+ }
10141
+ function loadOntologyReviewDecisions() {
10142
+ return readOntologyJsonl("reviews.jsonl").slice().sort((a, b) => b.decidedAt.localeCompare(a.decidedAt));
10143
+ }
10144
+ function appendOntologyReviewDecision(decision) {
10145
+ appendOntologyJsonl("reviews.jsonl", decision);
10146
+ }
10147
+ function loadOntologyChangeEvents() {
10148
+ return readOntologyJsonl("change-log.jsonl").slice().sort((a, b) => b.timestamp.localeCompare(a.timestamp));
10149
+ }
10150
+ function appendOntologyChangeEvent(event) {
10151
+ appendOntologyJsonl("change-log.jsonl", event);
10152
+ }
10153
+ function buildCapabilityFrontierConcept() {
10154
+ const signalsByCapability = new Map;
10155
+ for (const signal of loadPressureSignals()) {
10156
+ const key = signal.capability?.trim() || signal.region?.trim();
10157
+ if (!key)
10158
+ continue;
10159
+ const normalized = slugFragment(key);
10160
+ const list = signalsByCapability.get(normalized) ?? [];
10161
+ list.push(signal);
10162
+ signalsByCapability.set(normalized, list);
10163
+ }
10164
+ return [...signalsByCapability.entries()].flatMap(([normalized, signals]) => {
10165
+ const distinctProjects = uniqueStrings(signals.map((signal) => signal.projectId ?? signal.projectPath));
10166
+ if (signals.length < 3 && distinctProjects.length < 2)
10167
+ return [];
10168
+ const first = signals[0];
10169
+ const capabilityName = first.capability?.trim() || first.region?.trim() || normalized;
10170
+ return [{
10171
+ id: `ontology_capability_${normalized}`,
10172
+ name: capabilityName.replace(/-/g, " "),
10173
+ conceptKind: "capability",
10174
+ layer: "frontier",
10175
+ status: "provisional",
10176
+ createdAt: new Date().toISOString(),
10177
+ description: `Recurring capability pressure around ${capabilityName}.`,
10178
+ promotionCriteria: [
10179
+ "Recurs across time or more than one meaningful project context",
10180
+ "Improves pressure explanation or project-gap attribution",
10181
+ "Cannot be expressed cleanly by the current capability vocabulary alone"
10182
+ ],
10183
+ reviewKey: `capability:${normalized}`,
10184
+ lastObservedAt: signals.map((signal) => signal.detectedAt).sort().at(-1),
10185
+ sourceSummary: `Derived from ${signals.length} pressure signals across ${Math.max(1, distinctProjects.length)} contexts.`,
10186
+ projectIds: distinctProjects,
10187
+ evidenceIds: uniqueStrings(signals.map((signal) => signal.id)),
10188
+ observationCount: signals.length,
10189
+ confidence: Math.min(0.95, 0.52 + Math.min(0.28, signals.length * 0.05) + Math.min(0.12, distinctProjects.length * 0.08)),
10190
+ relatedSignalIds: uniqueStrings(signals.map((signal) => signal.id)),
10191
+ derivedFromKinds: ["project-analysis-pressure", "capability-gap"],
10192
+ aliases: [capabilityName],
10193
+ migrationMap: {}
10194
+ }];
10195
+ });
10196
+ }
10197
+ function buildPressureTypeFrontierConcepts() {
10198
+ return loadPressureMotifs().flatMap((motif) => {
10199
+ if (motif.recurrenceCount < 3 && motif.projectIds.length < 2)
10200
+ return [];
10201
+ const capabilityPart = motif.capability ? ` ${motif.capability}` : "";
10202
+ const normalized = slugFragment(`${motif.kind}-${motif.capability ?? motif.key}`);
10203
+ return [{
10204
+ id: `ontology_pressure_${normalized}`,
10205
+ name: `${motif.kind}${capabilityPart}`.trim().replace(/-/g, " "),
10206
+ conceptKind: "pressure-type",
10207
+ layer: "frontier",
10208
+ status: "provisional",
10209
+ createdAt: new Date().toISOString(),
10210
+ description: motif.description,
10211
+ promotionCriteria: [
10212
+ "Recurs above isolated single-signal behavior",
10213
+ "Improves route recommendation explanation",
10214
+ "Supports at least one operational use-case in co-evolution control"
10215
+ ],
10216
+ reviewKey: `pressure:${normalized}`,
10217
+ lastObservedAt: motif.lastActivityAt,
10218
+ sourceSummary: `Derived from motif ${motif.key} with ${motif.recurrenceCount} observations across ${motif.projectIds.length} projects.`,
10219
+ projectIds: motif.projectIds,
10220
+ evidenceIds: uniqueStrings([...motif.pressureSignalIds, ...motif.linkedInterventionIds, ...motif.linkedTransferEventIds]),
10221
+ observationCount: motif.recurrenceCount,
10222
+ confidence: Math.min(0.95, 0.56 + Math.min(0.2, motif.recurrenceCount * 0.04) + Math.min(0.12, motif.projectIds.length * 0.06)),
10223
+ relatedSignalIds: motif.pressureSignalIds,
10224
+ relatedMotifIds: [motif.id],
10225
+ relatedTransferEventIds: motif.linkedTransferEventIds,
10226
+ derivedFromKinds: ["pressure-motif"],
10227
+ aliases: [motif.kind, motif.key],
10228
+ migrationMap: {}
10229
+ }];
10230
+ });
10231
+ }
10232
+ function topologyMotifDescriptor(candidate) {
10233
+ if (candidate.changeType === "split" && candidate.title.startsWith("Split overloaded skill:"))
10234
+ return "overloaded-skill-split-pressure";
10235
+ if (candidate.changeType === "rewire" && candidate.title.startsWith("Resolve conflicting pair:"))
10236
+ return "conflict-rewire-pressure";
10237
+ if (candidate.changeType === "promote" && candidate.title.startsWith("Promote recurring motif:"))
10238
+ return "motif-promotion-pressure";
10239
+ if (candidate.changeType === "merge" && candidate.title.startsWith("Merge overlap:"))
10240
+ return "overlap-merge-pressure";
10241
+ if (candidate.changeType === "consolidate")
10242
+ return "consolidation-pressure";
10243
+ return null;
10244
+ }
10245
+ function buildTopologyMotifFrontierConcepts() {
10246
+ const grouped = new Map;
10247
+ for (const candidate of loadResolvedTopologyReviewCandidates()) {
10248
+ const descriptor = topologyMotifDescriptor(candidate);
10249
+ if (!descriptor)
10250
+ continue;
10251
+ const list = grouped.get(descriptor) ?? [];
10252
+ list.push(candidate);
10253
+ grouped.set(descriptor, list);
10254
+ }
10255
+ return [...grouped.entries()].flatMap(([descriptor, candidates]) => {
10256
+ if (candidates.length < 2)
10257
+ return [];
10258
+ const first = candidates[0];
10259
+ return [{
10260
+ id: `ontology_topology_${slugFragment(descriptor)}`,
10261
+ name: descriptor.replace(/-/g, " "),
10262
+ conceptKind: "topology-motif",
10263
+ layer: "frontier",
10264
+ status: "provisional",
10265
+ createdAt: new Date().toISOString(),
10266
+ description: `Recurring structural review pattern: ${descriptor.replace(/-/g, " ")}.`,
10267
+ promotionCriteria: [
10268
+ "Recurs across multiple structural review candidates",
10269
+ "Improves topology review explanation or triage",
10270
+ "Supports a bounded operational use-case in the topology control surface"
10271
+ ],
10272
+ reviewKey: `topology:${slugFragment(descriptor)}`,
10273
+ lastObservedAt: candidates.map((candidate) => candidate.lastActivityAt).sort().at(-1),
10274
+ sourceSummary: `Derived from ${candidates.length} reviewed topology candidates of type ${first.changeType}.`,
10275
+ projectIds: uniqueStrings(candidates.flatMap((candidate) => candidate.projectIds ?? [])),
10276
+ evidenceIds: uniqueStrings(candidates.flatMap((candidate) => [...candidate.evidence, candidate.id])),
10277
+ observationCount: candidates.length,
10278
+ confidence: Math.min(0.92, 0.55 + Math.min(0.24, candidates.length * 0.08)),
10279
+ relatedReviewIds: candidates.map((candidate) => candidate.id),
10280
+ relatedMotifIds: uniqueStrings(candidates.flatMap((candidate) => candidate.relatedMotifIds ?? [])),
10281
+ relatedTransferEventIds: uniqueStrings(candidates.flatMap((candidate) => candidate.relatedTransferEventIds ?? [])),
10282
+ derivedFromKinds: ["topology-review"],
10283
+ aliases: [first.changeType, descriptor],
10284
+ migrationMap: {}
10285
+ }];
10286
+ });
10287
+ }
10288
+ function deriveOntologyFrontierConcepts() {
10289
+ const byKey = new Map;
10290
+ for (const concept of [
10291
+ ...buildCapabilityFrontierConcept(),
10292
+ ...buildPressureTypeFrontierConcepts(),
10293
+ ...buildTopologyMotifFrontierConcepts()
10294
+ ]) {
10295
+ const key = concept.reviewKey ?? concept.id;
10296
+ const current = byKey.get(key);
10297
+ byKey.set(key, current ? mergeConcept(current, concept) : concept);
10298
+ }
10299
+ return [...byKey.values()].filter((concept) => (concept.evidenceIds?.length ?? 0) > 0).sort((a, b) => (b.confidence ?? 0) - (a.confidence ?? 0) || (b.observationCount ?? 0) - (a.observationCount ?? 0) || (b.lastObservedAt ?? "").localeCompare(a.lastObservedAt ?? "")).slice(0, 10);
10300
+ }
10301
+ function refreshOntologyFrontier() {
10302
+ materializeOntologyKernelSnapshot();
10303
+ const existing = loadOntologyFrontier();
10304
+ const extensions = loadOntologyExtensions();
10305
+ const promotedKeys = new Set(extensions.map((concept) => concept.reviewKey ?? concept.id));
10306
+ const existingByKey = new Map(existing.map((concept) => [concept.reviewKey ?? concept.id, concept]));
10307
+ const derived = deriveOntologyFrontierConcepts().filter((concept) => !promotedKeys.has(concept.reviewKey ?? concept.id));
10308
+ let created = 0;
10309
+ let updated = 0;
10310
+ for (const concept of derived) {
10311
+ const key = concept.reviewKey ?? concept.id;
10312
+ const current = existingByKey.get(key);
10313
+ if (!current) {
10314
+ existingByKey.set(key, concept);
10315
+ created += 1;
10316
+ appendOntologyChangeEvent({
10317
+ id: `ontology_change_hypothesized_${slugFragment(concept.id)}_${Date.now()}`,
10318
+ timestamp: new Date().toISOString(),
10319
+ changeType: "concept-hypothesized",
10320
+ conceptIds: [concept.id],
10321
+ reason: concept.sourceSummary ?? conceptArtifactSummary("Hypothesized", concept),
10322
+ evidenceIds: concept.evidenceIds,
10323
+ ontologyVersion: ONTOLOGY_SPEC_VERSION
10324
+ });
10325
+ appendOntologyArtifact({
10326
+ concept,
10327
+ operation: "refresh",
10328
+ sourceId: concept.id,
10329
+ summary: conceptArtifactSummary("Hypothesized", concept),
10330
+ metrics: {
10331
+ observations: concept.observationCount ?? 0,
10332
+ confidence: concept.confidence ?? 0,
10333
+ evidence: concept.evidenceIds?.length ?? 0
10334
+ }
10335
+ });
10336
+ continue;
10337
+ }
10338
+ existingByKey.set(key, mergeConcept(current, concept));
10339
+ updated += 1;
10340
+ }
10341
+ const frontier = [...existingByKey.values()].filter((concept) => !promotedKeys.has(concept.reviewKey ?? concept.id)).sort((a, b) => (b.lastObservedAt ?? b.createdAt).localeCompare(a.lastObservedAt ?? a.createdAt) || a.name.localeCompare(b.name));
10342
+ saveOntologyFrontier(frontier);
10343
+ const resolved = loadResolvedOntologyFrontierConcepts();
10344
+ return {
10345
+ created,
10346
+ updated,
10347
+ total: resolved.length,
10348
+ open: resolved.filter((concept) => concept.reviewStatus === "open").length,
10349
+ concepts: resolved
10350
+ };
10351
+ }
10352
+ function loadResolvedOntologyFrontierConcepts() {
10353
+ const concepts = loadOntologyFrontier();
10354
+ const latestDecisionByConcept = new Map;
10355
+ for (const decision of loadOntologyReviewDecisions()) {
10356
+ const existing = latestDecisionByConcept.get(decision.conceptId);
10357
+ if (!existing || decision.decidedAt > existing.decidedAt)
10358
+ latestDecisionByConcept.set(decision.conceptId, decision);
10359
+ }
10360
+ return concepts.map((concept) => {
10361
+ const latestReview = latestDecisionByConcept.get(concept.id);
10362
+ const reviewStatus = latestReview?.decision === "promote" ? "promoted" : latestReview?.decision === "reject" ? "rejected" : latestReview?.decision === "defer" ? "deferred" : "open";
10363
+ return {
10364
+ ...concept,
10365
+ reviewStatus,
10366
+ latestReview,
10367
+ lastActivityAt: maxTimestamp(concept.lastObservedAt, latestReview?.decidedAt, concept.createdAt)
10368
+ };
10369
+ }).sort((a, b) => ontologyStatusRank(b.reviewStatus) - ontologyStatusRank(a.reviewStatus) || (b.confidence ?? 0) - (a.confidence ?? 0) || (b.observationCount ?? 0) - (a.observationCount ?? 0) || b.lastActivityAt.localeCompare(a.lastActivityAt) || a.name.localeCompare(b.name));
10370
+ }
10371
+ function reviewOntologyConcept(params) {
10372
+ const frontier = loadOntologyFrontier();
10373
+ const concept = frontier.find((entry) => entry.id === params.conceptId);
10374
+ if (!concept)
10375
+ return { concept: null, extension: null };
10376
+ const governance = getActiveGovernanceSummary();
10377
+ const decision = {
10378
+ id: `ontology_review_${params.decision}_${slugFragment(params.conceptId)}_${Date.now()}`,
10379
+ conceptId: params.conceptId,
10380
+ decision: params.decision,
10381
+ decidedAt: new Date().toISOString(),
10382
+ governanceMode: governance.activeMode,
10383
+ rationale: params.rationale.trim() || `${params.decision} ontology concept via operator review`,
10384
+ decidedBy: "operator"
10385
+ };
10386
+ appendOntologyReviewDecision(decision);
10387
+ if (params.decision === "promote") {
10388
+ const extensions = loadOntologyExtensions();
10389
+ const promotedConcept = {
10390
+ ...concept,
10391
+ layer: "extension",
10392
+ status: "active",
10393
+ migrationMap: { ...concept.migrationMap ?? {}, [concept.id]: concept.id },
10394
+ aliases: uniqueStrings([...concept.aliases ?? [], concept.name]),
10395
+ lastObservedAt: maxTimestamp(concept.lastObservedAt, decision.decidedAt)
10396
+ };
10397
+ saveOntologyExtensions([
10398
+ promotedConcept,
10399
+ ...extensions.filter((entry) => entry.id !== concept.id)
10400
+ ]);
10401
+ saveOntologyFrontier(frontier.filter((entry) => entry.id !== concept.id));
10402
+ appendOntologyChangeEvent({
10403
+ id: `ontology_change_promoted_${slugFragment(concept.id)}_${Date.now()}`,
10404
+ timestamp: decision.decidedAt,
10405
+ changeType: "concept-promoted",
10406
+ conceptIds: [concept.id],
10407
+ reason: decision.rationale,
10408
+ evidenceIds: concept.evidenceIds,
10409
+ approvedBy: decision.decidedBy,
10410
+ ontologyVersion: ONTOLOGY_SPEC_VERSION
10411
+ });
10412
+ appendOntologyArtifact({
10413
+ concept: promotedConcept,
10414
+ operation: "promote",
10415
+ sourceId: decision.id,
10416
+ summary: conceptArtifactSummary("Promoted", promotedConcept),
10417
+ metrics: {
10418
+ observations: promotedConcept.observationCount ?? 0,
10419
+ confidence: promotedConcept.confidence ?? 0,
10420
+ evidence: promotedConcept.evidenceIds?.length ?? 0
10421
+ }
10422
+ });
10423
+ return {
10424
+ concept: null,
10425
+ extension: promotedConcept
10426
+ };
10427
+ }
10428
+ if (params.decision === "reject") {
10429
+ appendOntologyChangeEvent({
10430
+ id: `ontology_change_rejected_${slugFragment(concept.id)}_${Date.now()}`,
10431
+ timestamp: decision.decidedAt,
10432
+ changeType: "concept-rejected",
10433
+ conceptIds: [concept.id],
10434
+ reason: decision.rationale,
10435
+ evidenceIds: concept.evidenceIds,
10436
+ approvedBy: decision.decidedBy,
10437
+ ontologyVersion: ONTOLOGY_SPEC_VERSION
10438
+ });
10439
+ appendOntologyArtifact({
10440
+ concept,
10441
+ operation: "reject",
10442
+ sourceId: decision.id,
10443
+ summary: conceptArtifactSummary("Rejected", concept),
10444
+ metrics: {
10445
+ observations: concept.observationCount ?? 0,
10446
+ confidence: concept.confidence ?? 0,
10447
+ evidence: concept.evidenceIds?.length ?? 0
10448
+ }
10449
+ });
10450
+ }
10451
+ return {
10452
+ concept: loadResolvedOntologyFrontierConcepts().find((entry) => entry.id === params.conceptId) ?? null,
10453
+ extension: null
10454
+ };
10455
+ }
10456
+ function deprecateOntologyExtension(params) {
10457
+ const extensions = loadOntologyExtensions();
10458
+ const current = extensions.find((entry) => entry.id === params.conceptId);
10459
+ if (!current)
10460
+ return null;
10461
+ const governance = getActiveGovernanceSummary();
10462
+ const adoption = loadResolvedOntologyExtensions().find((entry) => entry.id === params.conceptId);
10463
+ const deprecated = {
10464
+ ...current,
10465
+ status: "deprecated",
10466
+ lastObservedAt: maxTimestamp(current.lastObservedAt, new Date().toISOString())
10467
+ };
10468
+ saveOntologyExtensions([
10469
+ deprecated,
10470
+ ...extensions.filter((entry) => entry.id !== params.conceptId)
10471
+ ]);
10472
+ const consumerWarning = adoption && adoption.adoptionCount > 0 ? ` Warning: ${adoption.adoptionCount} active semantic consumer${adoption.adoptionCount === 1 ? "" : "s"} were visible before deprecation.` : "";
10473
+ appendOntologyChangeEvent({
10474
+ id: `ontology_change_deprecated_${slugFragment(current.id)}_${Date.now()}`,
10475
+ timestamp: new Date().toISOString(),
10476
+ changeType: "concept-deprecated",
10477
+ conceptIds: [current.id],
10478
+ reason: (params.rationale.trim() || `Deprecated ontology extension ${current.name}`) + consumerWarning,
10479
+ evidenceIds: current.evidenceIds,
10480
+ approvedBy: "operator",
10481
+ ontologyVersion: ONTOLOGY_SPEC_VERSION
10482
+ });
10483
+ appendOntologyArtifact({
10484
+ concept: deprecated,
10485
+ operation: "deprecate",
10486
+ sourceId: `${current.id}:deprecate`,
10487
+ summary: `Deprecated ontology extension ${deprecated.name} under ${governance.activeMode} governance.${consumerWarning}`,
10488
+ metrics: {
10489
+ observations: deprecated.observationCount ?? 0,
10490
+ confidence: deprecated.confidence ?? 0,
10491
+ adoptionCount: adoption?.adoptionCount ?? 0
10492
+ }
10493
+ });
10494
+ const resolved = loadResolvedOntologyExtensions().find((entry) => entry.id === params.conceptId);
10495
+ if (resolved) {
10496
+ return {
10497
+ ...resolved,
10498
+ warning: consumerWarning.trim() || resolved.warning,
10499
+ adoptionCount: adoption?.adoptionCount ?? resolved.adoptionCount
10500
+ };
10501
+ }
10502
+ return {
10503
+ ...deprecated,
10504
+ semanticBindings: [],
10505
+ adoptionCount: adoption?.adoptionCount ?? 0,
10506
+ bindingsByTargetType: adoption?.bindingsByTargetType ?? emptyOntologyBindingCounts(),
10507
+ lastActivityAt: deprecated.lastObservedAt ?? deprecated.createdAt,
10508
+ warning: consumerWarning.trim() || undefined
10509
+ };
10510
+ }
10511
+ function getOntologyReviewSummary() {
10512
+ const kernel = loadOntologyKernelSnapshot();
10513
+ const extensions = loadOntologyExtensions();
10514
+ const resolvedExtensions = loadResolvedOntologyExtensions();
10515
+ const frontier = loadResolvedOntologyFrontierConcepts();
10516
+ const changeEvents = loadOntologyChangeEvents();
10517
+ const adoption = getOntologyAdoptionSummary();
10518
+ const byConceptKind = ontologyKindCounts();
10519
+ for (const concept of [...extensions, ...frontier]) {
10520
+ byConceptKind[concept.conceptKind] = (byConceptKind[concept.conceptKind] ?? 0) + 1;
10521
+ }
10522
+ return {
10523
+ kernelConcepts: kernel.entityNames.length,
10524
+ extensions: extensions.length,
10525
+ frontier: frontier.length,
10526
+ reviewOpen: frontier.filter((concept) => concept.reviewStatus === "open").length,
10527
+ promoted: extensions.filter((concept) => concept.status === "active").length,
10528
+ rejected: frontier.filter((concept) => concept.reviewStatus === "rejected").length,
10529
+ deferred: frontier.filter((concept) => concept.reviewStatus === "deferred").length,
10530
+ deprecated: extensions.filter((concept) => concept.status === "deprecated").length,
10531
+ changeEvents: changeEvents.length,
10532
+ byConceptKind,
10533
+ adoption,
10534
+ backlog: frontier.filter((concept) => concept.reviewStatus === "open" || concept.reviewStatus === "deferred").slice(0, 10),
10535
+ recentChanges: changeEvents.slice(0, 10),
10536
+ recentExtensions: resolvedExtensions.slice(0, 8)
10537
+ };
10538
+ }
10022
10539
  function buildDerivedEvolutionArtifact(iteration, proposal) {
10023
10540
  return {
10024
10541
  id: `artifact_derived_${iteration.id}_${proposal.id}`,
@@ -10142,7 +10659,15 @@ function loadPressureInterventions() {
10142
10659
  return loadNativePressureInterventions().slice().sort((a, b) => (b.completedAt ?? b.createdAt).localeCompare(a.completedAt ?? a.createdAt));
10143
10660
  }
10144
10661
  function loadTransferEvents() {
10145
- return loadNativeTransferEvents().slice().sort((a, b) => b.timestamp.localeCompare(a.timestamp));
10662
+ const concepts = activeOntologyExtensions();
10663
+ return loadNativeTransferEvents().slice().map((event) => {
10664
+ const semanticBindings = matchTransferOntologyBindings(event, concepts);
10665
+ return {
10666
+ ...event,
10667
+ semanticBindings,
10668
+ semanticConceptIds: uniqueStrings(semanticBindings.map((binding) => binding.conceptId))
10669
+ };
10670
+ }).sort((a, b) => b.timestamp.localeCompare(a.timestamp));
10146
10671
  }
10147
10672
  function normalizePressureValue(value) {
10148
10673
  return (value ?? "").trim().toLowerCase().replace(/[\s_/]+/g, "-");
@@ -10277,16 +10802,301 @@ function deriveGovernanceSummary(pressureSignals = loadPressureSignals(), interv
10277
10802
  function getActiveGovernanceSummary() {
10278
10803
  return deriveGovernanceSummary();
10279
10804
  }
10805
+ function emptyOntologyBindingCounts() {
10806
+ return {};
10807
+ }
10808
+ function ontologyReviewKeyBody(reviewKey) {
10809
+ return normalizePressureValue((reviewKey ?? "").split(":").slice(1).join("-"));
10810
+ }
10811
+ function ontologyMotifTerm(motifId) {
10812
+ return normalizePressureValue(motifId.replace(/^motif_/, ""));
10813
+ }
10814
+ function ontologyConceptTerms(concept) {
10815
+ return uniqueStrings([
10816
+ normalizePressureValue(concept.name),
10817
+ ...(concept.aliases ?? []).map((alias) => normalizePressureValue(alias)),
10818
+ normalizePressureValue(concept.reviewKey),
10819
+ ontologyReviewKeyBody(concept.reviewKey),
10820
+ ...(concept.relatedMotifIds ?? []).map((id) => ontologyMotifTerm(id)),
10821
+ ...(concept.derivedFromKinds ?? []).map((value) => normalizePressureValue(value))
10822
+ ].filter(Boolean));
10823
+ }
10824
+ function ontologyBindingId(conceptId, targetType, targetId, sourceKind) {
10825
+ return `ontology_binding_${slugFragment(conceptId)}_${targetType}_${slugFragment(targetId)}_${sourceKind}`;
10826
+ }
10827
+ function buildOntologyBinding(params) {
10828
+ return {
10829
+ id: ontologyBindingId(params.concept.id, params.targetType, params.targetId, params.sourceKind),
10830
+ conceptId: params.concept.id,
10831
+ conceptName: params.concept.name,
10832
+ conceptKind: params.concept.conceptKind,
10833
+ targetType: params.targetType,
10834
+ targetId: params.targetId,
10835
+ sourceKind: params.sourceKind,
10836
+ confidence: Math.min(0.95, Math.max(0.5, params.confidence)),
10837
+ reasons: params.reasons,
10838
+ effectSummary: params.effectSummary
10839
+ };
10840
+ }
10841
+ function dedupeOntologyBindings(bindings) {
10842
+ const byKey = new Map;
10843
+ for (const binding of bindings) {
10844
+ const key = `${binding.conceptId}::${binding.targetType}::${binding.targetId}`;
10845
+ const existing = byKey.get(key);
10846
+ if (!existing || binding.confidence > existing.confidence)
10847
+ byKey.set(key, binding);
10848
+ }
10849
+ return [...byKey.values()].sort((a, b) => b.confidence - a.confidence || a.conceptName.localeCompare(b.conceptName)).slice(0, 4);
10850
+ }
10851
+ function activeOntologyExtensions() {
10852
+ return loadOntologyExtensions().filter((concept) => concept.status === "active");
10853
+ }
10854
+ function matchSignalOntologyBindings(signal, motifIds, concepts) {
10855
+ const capability = normalizePressureValue(signal.capability);
10856
+ const region = normalizePressureValue(signal.region);
10857
+ const kind = normalizePressureValue(signal.kind);
10858
+ const motifTerms = uniqueStrings(motifIds.map((id) => ontologyMotifTerm(id)).filter(Boolean));
10859
+ const bindings = [];
10860
+ for (const concept of concepts) {
10861
+ const terms = ontologyConceptTerms(concept);
10862
+ if (concept.conceptKind === "capability") {
10863
+ if (capability && terms.includes(capability)) {
10864
+ bindings.push(buildOntologyBinding({
10865
+ concept,
10866
+ targetType: "pressure-signal",
10867
+ targetId: signal.id,
10868
+ sourceKind: "capability-match",
10869
+ confidence: 0.86,
10870
+ reasons: [`Signal capability ${signal.capability} matches approved capability concept ${concept.name}.`],
10871
+ effectSummary: "Sharpens project or capability interpretation for this pressure signal."
10872
+ }));
10873
+ continue;
10874
+ }
10875
+ if (region && terms.includes(region)) {
10876
+ bindings.push(buildOntologyBinding({
10877
+ concept,
10878
+ targetType: "pressure-signal",
10879
+ targetId: signal.id,
10880
+ sourceKind: "pressure-region",
10881
+ confidence: 0.74,
10882
+ reasons: [`Signal region ${signal.region} aligns with approved capability concept ${concept.name}.`],
10883
+ effectSummary: "Adds semantic coverage to a recurring pressure region."
10884
+ }));
10885
+ }
10886
+ continue;
10887
+ }
10888
+ if (concept.conceptKind === "pressure-type") {
10889
+ if ((concept.relatedMotifIds ?? []).some((id) => motifIds.includes(id))) {
10890
+ bindings.push(buildOntologyBinding({
10891
+ concept,
10892
+ targetType: "pressure-signal",
10893
+ targetId: signal.id,
10894
+ sourceKind: "motif-match",
10895
+ confidence: 0.9,
10896
+ reasons: [`Signal participates in approved motif family ${concept.name}.`],
10897
+ effectSummary: "Marks this signal as part of an approved recurring pressure family."
10898
+ }));
10899
+ continue;
10900
+ }
10901
+ if (motifTerms.some((term) => terms.includes(term))) {
10902
+ bindings.push(buildOntologyBinding({
10903
+ concept,
10904
+ targetType: "pressure-signal",
10905
+ targetId: signal.id,
10906
+ sourceKind: "motif-match",
10907
+ confidence: 0.84,
10908
+ reasons: [`Signal motif region aligns with approved pressure family ${concept.name}.`],
10909
+ effectSummary: "Carries an approved recurring pressure family into live signal interpretation."
10910
+ }));
10911
+ continue;
10912
+ }
10913
+ if (kind && terms.includes(kind)) {
10914
+ bindings.push(buildOntologyBinding({
10915
+ concept,
10916
+ targetType: "pressure-signal",
10917
+ targetId: signal.id,
10918
+ sourceKind: "pressure-region",
10919
+ confidence: 0.78,
10920
+ reasons: [`Signal kind ${signal.kind} matches approved pressure family ${concept.name}.`],
10921
+ effectSummary: "Adds semantic identity to recurring pressure of the same kind."
10922
+ }));
10923
+ }
10924
+ }
10925
+ }
10926
+ return dedupeOntologyBindings(bindings);
10927
+ }
10928
+ function matchMotifOntologyBindings(motif, concepts) {
10929
+ const capability = normalizePressureValue(motif.capability);
10930
+ const key = normalizePressureValue(motif.key);
10931
+ const kind = normalizePressureValue(motif.kind);
10932
+ const bindings = [];
10933
+ for (const concept of concepts) {
10934
+ const terms = ontologyConceptTerms(concept);
10935
+ if (concept.conceptKind === "capability" && capability && terms.includes(capability)) {
10936
+ bindings.push(buildOntologyBinding({
10937
+ concept,
10938
+ targetType: "pressure-motif",
10939
+ targetId: motif.id,
10940
+ sourceKind: "capability-match",
10941
+ confidence: 0.84,
10942
+ reasons: [`Motif capability ${motif.capability} matches approved capability concept ${concept.name}.`],
10943
+ effectSummary: "Makes recurring capability pressure visible as semantic adoption."
10944
+ }));
10945
+ continue;
10946
+ }
10947
+ if (concept.conceptKind === "pressure-type") {
10948
+ if ((concept.relatedMotifIds ?? []).includes(motif.id)) {
10949
+ bindings.push(buildOntologyBinding({
10950
+ concept,
10951
+ targetType: "pressure-motif",
10952
+ targetId: motif.id,
10953
+ sourceKind: "motif-match",
10954
+ confidence: 0.92,
10955
+ reasons: [`Motif ${motif.key} directly matches approved pressure family ${concept.name}.`],
10956
+ effectSummary: "Confirms this recurring motif is an approved semantic family."
10957
+ }));
10958
+ continue;
10959
+ }
10960
+ if ([key, kind].filter(Boolean).some((value) => terms.includes(value))) {
10961
+ bindings.push(buildOntologyBinding({
10962
+ concept,
10963
+ targetType: "pressure-motif",
10964
+ targetId: motif.id,
10965
+ sourceKind: "pressure-region",
10966
+ confidence: 0.86,
10967
+ reasons: [`Motif identity aligns with approved pressure family ${concept.name}.`],
10968
+ effectSummary: "Carries approved semantic family identity into motif-level routing."
10969
+ }));
10970
+ }
10971
+ }
10972
+ }
10973
+ return dedupeOntologyBindings(bindings);
10974
+ }
10975
+ function matchTransferOntologyBindings(event, concepts) {
10976
+ const capabilityTerms = uniqueStrings((event.capabilityIds ?? []).map((value) => normalizePressureValue(value)).filter(Boolean));
10977
+ const motifTerms = uniqueStrings((event.motifIds ?? []).map((value) => ontologyMotifTerm(value)).filter(Boolean));
10978
+ const bindings = [];
10979
+ for (const concept of concepts) {
10980
+ const terms = ontologyConceptTerms(concept);
10981
+ if (concept.conceptKind === "capability" && capabilityTerms.some((value) => terms.includes(value))) {
10982
+ bindings.push(buildOntologyBinding({
10983
+ concept,
10984
+ targetType: "transfer-event",
10985
+ targetId: event.id,
10986
+ sourceKind: "capability-match",
10987
+ confidence: 0.82,
10988
+ reasons: [`Transfer capabilities align with approved concept ${concept.name}.`],
10989
+ effectSummary: "Shows semantic adoption through reusable transfer evidence."
10990
+ }));
10991
+ continue;
10992
+ }
10993
+ if (concept.conceptKind === "pressure-type") {
10994
+ if ((concept.relatedMotifIds ?? []).some((id) => (event.motifIds ?? []).includes(id))) {
10995
+ bindings.push(buildOntologyBinding({
10996
+ concept,
10997
+ targetType: "transfer-event",
10998
+ targetId: event.id,
10999
+ sourceKind: "transfer-motif",
11000
+ confidence: 0.88,
11001
+ reasons: [`Transfer realizes approved recurring family ${concept.name}.`],
11002
+ effectSummary: "Connects realized transfer evidence to an approved recurring pressure family."
11003
+ }));
11004
+ continue;
11005
+ }
11006
+ if (motifTerms.some((value) => terms.includes(value))) {
11007
+ bindings.push(buildOntologyBinding({
11008
+ concept,
11009
+ targetType: "transfer-event",
11010
+ targetId: event.id,
11011
+ sourceKind: "transfer-motif",
11012
+ confidence: 0.8,
11013
+ reasons: [`Transfer motif linkage aligns with approved family ${concept.name}.`],
11014
+ effectSummary: "Carries approved semantic family identity into realized transfer outcomes."
11015
+ }));
11016
+ }
11017
+ }
11018
+ }
11019
+ return dedupeOntologyBindings(bindings);
11020
+ }
11021
+ function matchTopologyOntologyBindings(candidate, concepts) {
11022
+ const descriptor = normalizePressureValue(topologyMotifDescriptor(candidate) ?? candidate.changeType);
11023
+ const bindings = [];
11024
+ for (const concept of concepts) {
11025
+ const terms = ontologyConceptTerms(concept);
11026
+ if (concept.conceptKind === "topology-motif" && descriptor && terms.includes(descriptor)) {
11027
+ bindings.push(buildOntologyBinding({
11028
+ concept,
11029
+ targetType: "topology-review",
11030
+ targetId: candidate.id,
11031
+ sourceKind: "topology-descriptor",
11032
+ confidence: 0.86,
11033
+ reasons: [`Topology review descriptor ${descriptor} aligns with approved topology concept ${concept.name}.`],
11034
+ effectSummary: "Adds semantic identity to recurring structural review patterns."
11035
+ }));
11036
+ continue;
11037
+ }
11038
+ if (concept.conceptKind === "pressure-type" && (concept.relatedMotifIds ?? []).some((id) => (candidate.relatedMotifIds ?? []).includes(id))) {
11039
+ bindings.push(buildOntologyBinding({
11040
+ concept,
11041
+ targetType: "topology-review",
11042
+ targetId: candidate.id,
11043
+ sourceKind: "motif-match",
11044
+ confidence: 0.72,
11045
+ reasons: [`Topology candidate inherits recurring pressure motif semantics from approved family ${concept.name}.`],
11046
+ effectSummary: "Links structural review back to an approved recurring pressure family."
11047
+ }));
11048
+ }
11049
+ }
11050
+ return dedupeOntologyBindings(bindings);
11051
+ }
11052
+ function applyOntologyInfluenceToRoute(params) {
11053
+ const { recommendation, signal, semanticBindings, governanceProfile } = params;
11054
+ if (semanticBindings.length === 0)
11055
+ return recommendation;
11056
+ const conceptIds = uniqueStrings(semanticBindings.map((binding) => binding.conceptId));
11057
+ const conceptNames = uniqueStrings(semanticBindings.map((binding) => binding.conceptName));
11058
+ const hasCapability = semanticBindings.some((binding) => binding.conceptKind === "capability");
11059
+ const hasPressureType = semanticBindings.some((binding) => binding.conceptKind === "pressure-type");
11060
+ const hasTopologyMotif = semanticBindings.some((binding) => binding.conceptKind === "topology-motif");
11061
+ const reasons = [...recommendation.reasons];
11062
+ let confidence = recommendation.confidence;
11063
+ let semanticInfluence = "explanatory";
11064
+ if (recommendation.route === "generalize" && hasPressureType && governanceProfile.riskTolerance >= 0.4 && governanceProfile.reviewThreshold <= 0.82) {
11065
+ confidence = Math.min(0.95, confidence + 0.04);
11066
+ semanticInfluence = "weighted";
11067
+ reasons.push(`Approved semantic family ${conceptNames.join(", ")} reinforces network-level reuse inside the current governance envelope.`);
11068
+ } else if (recommendation.route === "specialize" && hasCapability && signal.projectId && governanceProfile.reviewThreshold <= 0.86) {
11069
+ confidence = Math.min(0.95, confidence + 0.03);
11070
+ semanticInfluence = "weighted";
11071
+ reasons.push(`Approved capability concept ${conceptNames.join(", ")} sharpens project-bounded specialization without widening scope.`);
11072
+ } else if (recommendation.route === "evolve" && hasPressureType && signal.relatedFailureId) {
11073
+ confidence = Math.min(0.95, confidence + 0.02);
11074
+ semanticInfluence = "weighted";
11075
+ reasons.push(`Approved semantic family ${conceptNames.join(", ")} sharpens this failure-linked repair lane.`);
11076
+ } else if (recommendation.route === "manual-review" && (hasTopologyMotif || hasPressureType)) {
11077
+ reasons.push(`Approved semantic family ${conceptNames.join(", ")} confirms the pattern is real, but governance still prefers explicit operator review.`);
11078
+ } else {
11079
+ reasons.push(`Approved semantic family ${conceptNames.join(", ")} is visible here, but current governance/evidence still keeps the route bounded to ${recommendation.route}.`);
11080
+ }
11081
+ return {
11082
+ ...recommendation,
11083
+ confidence,
11084
+ reasons,
11085
+ semanticInfluence,
11086
+ semanticConceptIds: conceptIds
11087
+ };
11088
+ }
10280
11089
  function buildRouteRecommendation(params) {
10281
- const { signal, relatedSignals, linkedInterventions, governanceMode } = params;
11090
+ const { signal, relatedSignals, linkedInterventions, governanceMode, governanceProfile, semanticBindings } = params;
10282
11091
  const projectSpread = new Set(relatedSignals.map((entry) => entry.projectId ?? entry.projectPath).filter(Boolean)).size;
10283
11092
  const recurrenceCount = relatedSignals.length;
10284
11093
  const activeTypes = [...new Set(linkedInterventions.map((intervention) => intervention.interventionType))];
10285
11094
  const reasons = [];
11095
+ let recommendation;
10286
11096
  if (activeTypes.length >= 3 && !linkedInterventions.some((intervention) => intervention.status === "completed" && intervention.impact === "resolving")) {
10287
11097
  reasons.push("Multiple intervention lanes already touch this pressure region without clear closure.");
10288
11098
  reasons.push("Operator review is safer than blindly piling on another automated response.");
10289
- return {
11099
+ recommendation = {
10290
11100
  route: "manual-review",
10291
11101
  scope: projectSpread >= 2 ? "network" : signal.projectId ? "project" : "local",
10292
11102
  confidence: 0.58,
@@ -10294,25 +11104,21 @@ function buildRouteRecommendation(params) {
10294
11104
  triggeredBy: "mixed-signal",
10295
11105
  reasons
10296
11106
  };
10297
- }
10298
- if (projectSpread >= 2 && recurrenceCount >= 2) {
11107
+ } else if (projectSpread >= 2 && recurrenceCount >= 2 && governanceMode !== "project-critical") {
10299
11108
  reasons.push(`This pressure region repeats across ${projectSpread} projects.`);
10300
11109
  reasons.push("A network-level abstraction can absorb repeated local demand more efficiently than one-off fixes.");
10301
- if (governanceMode !== "project-critical") {
10302
- return {
10303
- route: "generalize",
10304
- scope: "network",
10305
- confidence: Math.min(0.95, 0.7 + Math.min(0.15, (projectSpread - 1) * 0.08)),
10306
- governanceMode,
10307
- triggeredBy: "recurring-cross-project-gap",
10308
- reasons
10309
- };
10310
- }
10311
- }
10312
- if (signal.projectId && signal.priority === "high" && signal.suggestedAction === "specialize") {
11110
+ recommendation = {
11111
+ route: "generalize",
11112
+ scope: "network",
11113
+ confidence: Math.min(0.95, 0.7 + Math.min(0.15, (projectSpread - 1) * 0.08)),
11114
+ governanceMode,
11115
+ triggeredBy: "recurring-cross-project-gap",
11116
+ reasons
11117
+ };
11118
+ } else if (signal.projectId && signal.priority === "high" && signal.suggestedAction === "specialize") {
10313
11119
  reasons.push("The pressure is high priority and bounded to a known project context.");
10314
11120
  reasons.push("Project-layer adaptation is the shortest truthful path to response.");
10315
- return {
11121
+ recommendation = {
10316
11122
  route: "specialize",
10317
11123
  scope: "project",
10318
11124
  confidence: 0.82,
@@ -10320,11 +11126,10 @@ function buildRouteRecommendation(params) {
10320
11126
  triggeredBy: "high-priority-local-gap",
10321
11127
  reasons
10322
11128
  };
10323
- }
10324
- if (signal.relatedFailureId && signal.severity >= 0.8) {
11129
+ } else if (signal.relatedFailureId && signal.severity >= 0.8) {
10325
11130
  reasons.push("This pressure is tied to a concrete failure with strong corrective evidence.");
10326
11131
  reasons.push("An evolution proposal can test a direct skill-level response against replay and regression evidence.");
10327
- return {
11132
+ recommendation = {
10328
11133
  route: "evolve",
10329
11134
  scope: signal.projectId ? "project" : "local",
10330
11135
  confidence: 0.78,
@@ -10332,11 +11137,10 @@ function buildRouteRecommendation(params) {
10332
11137
  triggeredBy: "accepted-iteration-pattern",
10333
11138
  reasons
10334
11139
  };
10335
- }
10336
- if (governanceMode === "exploration" || signal.suggestedAction === "research" || !signal.projectId) {
11140
+ } else if (governanceMode === "exploration" || signal.suggestedAction === "research" || !signal.projectId) {
10337
11141
  reasons.push("The pressure still looks under-specified or capability-oriented.");
10338
11142
  reasons.push("Research is the best way to widen evidence before committing to structural change.");
10339
- return {
11143
+ recommendation = {
10340
11144
  route: "research",
10341
11145
  scope: signal.projectId ? "project" : "local",
10342
11146
  confidence: 0.68,
@@ -10344,11 +11148,10 @@ function buildRouteRecommendation(params) {
10344
11148
  triggeredBy: "insufficient-evidence",
10345
11149
  reasons
10346
11150
  };
10347
- }
10348
- if (signal.projectId) {
11151
+ } else if (signal.projectId) {
10349
11152
  reasons.push("The pressure is project-scoped and can likely be handled without network-wide promotion yet.");
10350
11153
  reasons.push("Specialization is the most direct bounded intervention available.");
10351
- return {
11154
+ recommendation = {
10352
11155
  route: "specialize",
10353
11156
  scope: "project",
10354
11157
  confidence: 0.7,
@@ -10356,17 +11159,24 @@ function buildRouteRecommendation(params) {
10356
11159
  triggeredBy: "project-bounded-recurrence",
10357
11160
  reasons
10358
11161
  };
11162
+ } else {
11163
+ reasons.push("No single route is dominant from the current evidence.");
11164
+ reasons.push("Research keeps the loop moving while preserving optionality.");
11165
+ recommendation = {
11166
+ route: "research",
11167
+ scope: "local",
11168
+ confidence: 0.6,
11169
+ governanceMode,
11170
+ triggeredBy: "insufficient-evidence",
11171
+ reasons
11172
+ };
10359
11173
  }
10360
- reasons.push("No single route is dominant from the current evidence.");
10361
- reasons.push("Research keeps the loop moving while preserving optionality.");
10362
- return {
10363
- route: "research",
10364
- scope: "local",
10365
- confidence: 0.6,
10366
- governanceMode,
10367
- triggeredBy: "insufficient-evidence",
10368
- reasons
10369
- };
11174
+ return applyOntologyInfluenceToRoute({
11175
+ recommendation,
11176
+ signal,
11177
+ semanticBindings,
11178
+ governanceProfile
11179
+ });
10370
11180
  }
10371
11181
  function interventionEvidenceAccepted(intervention, acceptedProposalIds, acceptedArtifactIds, realizedTransferEventIds) {
10372
11182
  if (intervention.status !== "completed" || intervention.impact !== "resolving")
@@ -10395,6 +11205,7 @@ function loadResolvedPressureSignals() {
10395
11205
  const history = loadHistory();
10396
11206
  const artifacts = loadEvolutionArtifacts();
10397
11207
  const governance = deriveGovernanceSummary(pressureSignals, interventions);
11208
+ const concepts = activeOntologyExtensions();
10398
11209
  const acceptedProposalIds = new Set(history.iterations.flatMap((iteration) => iteration.proposals).filter((proposal) => proposal.outcome === "accepted").map((proposal) => proposal.id));
10399
11210
  const acceptedArtifactIds = new Set(artifacts.filter((artifact) => artifact.outcome === "accepted").map((artifact) => artifact.id));
10400
11211
  const realizedTransferEventIds = new Set(transferEvents.filter((event) => event.status === "realized").map((event) => event.id));
@@ -10421,17 +11232,22 @@ function loadResolvedPressureSignals() {
10421
11232
  const relatedSignals = signalsByRegion.get(pressureRegionKey(signal)) ?? [signal];
10422
11233
  const projectSpread = new Set(relatedSignals.map((entry) => entry.projectId ?? entry.projectPath).filter(Boolean)).size;
10423
11234
  const motifIds = relatedSignals.length >= 2 || projectSpread >= 2 ? [motifIdForSignal(signal)] : [];
11235
+ const semanticBindings = matchSignalOntologyBindings(signal, motifIds, concepts);
10424
11236
  const routeRecommendation = buildRouteRecommendation({
10425
11237
  signal,
10426
11238
  relatedSignals,
10427
11239
  linkedInterventions,
10428
- governanceMode: governance.activeMode
11240
+ governanceMode: governance.activeMode,
11241
+ governanceProfile: governance.profile,
11242
+ semanticBindings
10429
11243
  });
10430
11244
  const lifecycle = addressingInterventions.length > 0 || signal.status === "addressed" ? "addressed" : activeInterventions.length > 0 ? "in-progress" : hasNewerEquivalentSignal ? "stale" : "open";
10431
11245
  const lastActivityAt = [signal.detectedAt, latestInterventionAt].filter(Boolean).sort((a, b) => b.localeCompare(a))[0] ?? signal.detectedAt;
10432
11246
  const interventionTypes = [...new Set(linkedInterventions.map((intervention) => intervention.interventionType))];
10433
11247
  const addressedAt = addressingInterventions.map((intervention) => intervention.completedAt ?? intervention.createdAt).sort((a, b) => b.localeCompare(a))[0];
10434
- const responseSummary = lifecycle === "addressed" ? `Addressed by ${interventionTypes.join(", ") || "linked intervention"} evidence` : lifecycle === "in-progress" ? `Response underway via ${interventionTypes.join(", ")}` : lifecycle === "stale" ? "Superseded by newer pressure in the same project/capability region" : `No linked response yet • recommend ${routeRecommendation.route}`;
11248
+ const semanticNames = uniqueStrings(semanticBindings.map((binding) => binding.conceptName));
11249
+ const semanticNote = semanticNames.length > 0 ? ` • semantic family ${semanticNames.join(", ")}` : "";
11250
+ const responseSummary = lifecycle === "addressed" ? `Addressed by ${interventionTypes.join(", ") || "linked intervention"} evidence${semanticNote}` : lifecycle === "in-progress" ? `Response underway via ${interventionTypes.join(", ")}${semanticNote}` : lifecycle === "stale" ? "Superseded by newer pressure in the same project/capability region" : `No linked response yet • recommend ${routeRecommendation.route}${semanticNote}`;
10435
11251
  return {
10436
11252
  ...signal,
10437
11253
  lifecycle,
@@ -10439,6 +11255,8 @@ function loadResolvedPressureSignals() {
10439
11255
  interventionTypes,
10440
11256
  motifIds,
10441
11257
  routeRecommendation,
11258
+ semanticBindings,
11259
+ semanticConceptIds: uniqueStrings(semanticBindings.map((binding) => binding.conceptId)),
10442
11260
  lastActivityAt,
10443
11261
  addressedAt,
10444
11262
  responseSummary
@@ -10449,6 +11267,7 @@ function loadPressureMotifs() {
10449
11267
  const resolvedSignals = loadResolvedPressureSignals();
10450
11268
  const interventions = loadPressureInterventions();
10451
11269
  const transferEvents = loadTransferEvents();
11270
+ const concepts = activeOntologyExtensions();
10452
11271
  const groups = new Map;
10453
11272
  for (const signal of resolvedSignals) {
10454
11273
  const regionKey = pressureRegionKey(signal);
@@ -10493,7 +11312,15 @@ function loadPressureMotifs() {
10493
11312
  const linkedInterventionIds = linkedInterventions.map((intervention) => intervention.id);
10494
11313
  const linkedTransferEventIds = linkedTransferEvents.map((event) => event.id);
10495
11314
  const highPriorityCount = signals.filter((signal) => signal.priority === "high").length;
10496
- const responseSummary = lifecycle === "addressed" ? realizedTransfers.length > 0 ? "Promoted into reusable transfer evidence" : "All linked pressure in this recurring region is addressed" : lifecycle === "in-progress" ? `Promotion or response underway via ${interventionTypes.join(", ") || "linked intervention"}` : lifecycle === "stale" ? "Recurring pressure has been superseded by newer equivalent signals" : `Recurring demand is waiting for ${recommendation.route}`;
11315
+ const semanticBindings = matchMotifOntologyBindings({
11316
+ id: motifId,
11317
+ key: regionKey,
11318
+ kind: signals[0]?.kind ?? regionKey,
11319
+ capability
11320
+ }, concepts);
11321
+ const semanticNames = uniqueStrings(semanticBindings.map((binding) => binding.conceptName));
11322
+ const semanticNote = semanticNames.length > 0 ? ` • semantic family ${semanticNames.join(", ")}` : "";
11323
+ const responseSummary = lifecycle === "addressed" ? realizedTransfers.length > 0 ? `Promoted into reusable transfer evidence${semanticNote}` : `All linked pressure in this recurring region is addressed${semanticNote}` : lifecycle === "in-progress" ? `Promotion or response underway via ${interventionTypes.join(", ") || "linked intervention"}${semanticNote}` : lifecycle === "stale" ? "Recurring pressure has been superseded by newer equivalent signals" : `Recurring demand is waiting for ${recommendation.route}${semanticNote}`;
10497
11324
  motifs.push({
10498
11325
  id: motifId,
10499
11326
  key: regionKey,
@@ -10512,6 +11339,8 @@ function loadPressureMotifs() {
10512
11339
  suggestedRoute: recommendation,
10513
11340
  lifecycle,
10514
11341
  interventionTypes,
11342
+ semanticBindings,
11343
+ semanticConceptIds: uniqueStrings(semanticBindings.map((binding) => binding.conceptId)),
10515
11344
  lastActivityAt,
10516
11345
  addressedAt,
10517
11346
  responseSummary
@@ -10522,6 +11351,116 @@ function loadPressureMotifs() {
10522
11351
  return lifecycleWeight(b.lifecycle) - lifecycleWeight(a.lifecycle) || b.highPriorityCount - a.highPriorityCount || b.recurrenceCount - a.recurrenceCount || b.lastActivityAt.localeCompare(a.lastActivityAt);
10523
11352
  });
10524
11353
  }
11354
+ function collectOntologyAdoptionState() {
11355
+ const extensions = loadOntologyExtensions();
11356
+ const activeExtensions = extensions.filter((concept) => concept.status === "active");
11357
+ const byConceptKind = ontologyKindCounts();
11358
+ const bindingsByTargetType = emptyOntologyBindingCounts();
11359
+ const consumerMap = new Map;
11360
+ const conceptById = new Map(extensions.map((concept) => [concept.id, concept]));
11361
+ function ensureConsumer(concept) {
11362
+ const existing = consumerMap.get(concept.id);
11363
+ if (existing)
11364
+ return existing;
11365
+ const created = {
11366
+ conceptId: concept.id,
11367
+ conceptName: concept.name,
11368
+ conceptKind: concept.conceptKind,
11369
+ status: concept.status,
11370
+ totalBindings: 0,
11371
+ bindingsByTargetType: emptyOntologyBindingCounts(),
11372
+ activeTargetIds: [],
11373
+ lastActivityAt: concept.lastObservedAt ?? concept.createdAt
11374
+ };
11375
+ consumerMap.set(concept.id, created);
11376
+ return created;
11377
+ }
11378
+ function registerBinding(binding) {
11379
+ const concept = conceptById.get(binding.conceptId);
11380
+ if (!concept || concept.status !== "active")
11381
+ return;
11382
+ const consumer = ensureConsumer(concept);
11383
+ consumer.totalBindings += 1;
11384
+ consumer.bindingsByTargetType[binding.targetType] = (consumer.bindingsByTargetType[binding.targetType] ?? 0) + 1;
11385
+ if (!consumer.activeTargetIds.includes(binding.targetId) && consumer.activeTargetIds.length < 12) {
11386
+ consumer.activeTargetIds.push(binding.targetId);
11387
+ }
11388
+ consumer.lastActivityAt = maxTimestamp(consumer.lastActivityAt, concept.lastObservedAt, concept.createdAt);
11389
+ bindingsByTargetType[binding.targetType] = (bindingsByTargetType[binding.targetType] ?? 0) + 1;
11390
+ byConceptKind[concept.conceptKind] = (byConceptKind[concept.conceptKind] ?? 0) + 1;
11391
+ }
11392
+ const signals = loadResolvedPressureSignals();
11393
+ for (const signal of signals) {
11394
+ for (const binding of signal.semanticBindings ?? [])
11395
+ registerBinding(binding);
11396
+ if (signal.routeRecommendation?.semanticConceptIds?.length) {
11397
+ for (const conceptId of signal.routeRecommendation.semanticConceptIds) {
11398
+ const concept = conceptById.get(conceptId);
11399
+ if (!concept || concept.status !== "active")
11400
+ continue;
11401
+ registerBinding(buildOntologyBinding({
11402
+ concept,
11403
+ targetType: "route-recommendation",
11404
+ targetId: `${signal.id}:route`,
11405
+ sourceKind: "pressure-region",
11406
+ confidence: signal.routeRecommendation.semanticInfluence === "weighted" ? 0.82 : 0.72,
11407
+ reasons: signal.routeRecommendation.reasons.slice(0, 2),
11408
+ effectSummary: `Route recommendation ${signal.routeRecommendation.route} is semantically informed by ${concept.name}.`
11409
+ }));
11410
+ }
11411
+ }
11412
+ }
11413
+ for (const motif of loadPressureMotifs()) {
11414
+ for (const binding of motif.semanticBindings ?? [])
11415
+ registerBinding(binding);
11416
+ }
11417
+ for (const event of loadTransferEvents()) {
11418
+ for (const binding of event.semanticBindings ?? [])
11419
+ registerBinding(binding);
11420
+ }
11421
+ for (const candidate of loadResolvedTopologyReviewCandidates()) {
11422
+ for (const binding of candidate.semanticBindings ?? [])
11423
+ registerBinding(binding);
11424
+ }
11425
+ const activeConsumers = [...consumerMap.values()].sort((a, b) => b.totalBindings - a.totalBindings || (b.lastActivityAt ?? "").localeCompare(a.lastActivityAt ?? ""));
11426
+ const atRiskConcepts = activeConsumers.filter((consumer) => consumer.totalBindings > 0).map((consumer) => ({
11427
+ ...consumer,
11428
+ warning: `${consumer.totalBindings} active consumer${consumer.totalBindings === 1 ? "" : "s"} would be affected by deprecating ${consumer.conceptName}.`
11429
+ }));
11430
+ return {
11431
+ summary: {
11432
+ activeConcepts: activeConsumers.length,
11433
+ unusedExtensions: activeExtensions.filter((concept) => !consumerMap.has(concept.id)).length,
11434
+ totalBindings: activeConsumers.reduce((sum, consumer) => sum + consumer.totalBindings, 0),
11435
+ routesInfluenced: signals.filter((signal) => signal.routeRecommendation?.semanticInfluence && signal.routeRecommendation.semanticInfluence !== "none").length,
11436
+ conceptsWithConsumers: activeConsumers.length,
11437
+ conceptsAtDeprecationRisk: atRiskConcepts.length,
11438
+ bindingsByTargetType,
11439
+ usageByConceptKind: byConceptKind,
11440
+ topActiveConcepts: activeConsumers.slice(0, 8),
11441
+ atRiskConcepts: atRiskConcepts.slice(0, 8)
11442
+ },
11443
+ consumerMap: new Map(activeConsumers.map((consumer) => [consumer.conceptId, consumer]))
11444
+ };
11445
+ }
11446
+ function getOntologyAdoptionSummary() {
11447
+ return collectOntologyAdoptionState().summary;
11448
+ }
11449
+ function loadResolvedOntologyExtensions() {
11450
+ const extensions = loadOntologyExtensions();
11451
+ const { consumerMap } = collectOntologyAdoptionState();
11452
+ return extensions.map((concept) => {
11453
+ const consumer = consumerMap.get(concept.id);
11454
+ return {
11455
+ ...concept,
11456
+ semanticBindings: [],
11457
+ adoptionCount: consumer?.totalBindings ?? 0,
11458
+ bindingsByTargetType: consumer?.bindingsByTargetType ?? emptyOntologyBindingCounts(),
11459
+ lastActivityAt: maxTimestamp(concept.lastObservedAt, consumer?.lastActivityAt, concept.createdAt),
11460
+ warning: consumer?.warning
11461
+ };
11462
+ }).sort((a, b) => b.adoptionCount - a.adoptionCount || b.lastActivityAt.localeCompare(a.lastActivityAt) || a.name.localeCompare(b.name));
11463
+ }
10525
11464
  function matchPressureSignals(filters) {
10526
11465
  const capabilities = new Set((filters.capabilities ?? []).map((value) => normalizePressureValue(value)));
10527
11466
  const relatedFailureIds = new Set(filters.relatedFailureIds ?? []);
@@ -15875,9 +16814,157 @@ async function topologyCommand(options) {
15875
16814
  }
15876
16815
  }
15877
16816
 
16817
+ // src/commands/ontology.ts
16818
+ init_data();
16819
+ function printStatus2() {
16820
+ const summary = getOntologyReviewSummary();
16821
+ console.log(`
16822
+ Ontology Frontier`);
16823
+ console.log(" ────────────────────────────────────────");
16824
+ console.log(` Kernel: ${summary.kernelConcepts} canonical entities`);
16825
+ console.log(` Extensions: ${summary.extensions} active • ${summary.deprecated} deprecated`);
16826
+ console.log(` Frontier: ${summary.frontier} total • ${summary.reviewOpen} open • ${summary.deferred} deferred • ${summary.rejected} rejected`);
16827
+ console.log(` Change log: ${summary.changeEvents} events`);
16828
+ console.log(` Adoption: ${summary.adoption.activeConcepts} active concepts • ${summary.adoption.totalBindings} bindings • ${summary.adoption.routesInfluenced} routes influenced`);
16829
+ console.log(` Coverage: ${summary.adoption.unusedExtensions} unused extensions • ${summary.adoption.conceptsAtDeprecationRisk} deprecation-sensitive concepts`);
16830
+ if (summary.backlog.length > 0) {
16831
+ console.log(`
16832
+ Frontier concepts ready for review`);
16833
+ for (const concept of summary.backlog.slice(0, 5)) {
16834
+ console.log(` • ${concept.id} (${concept.conceptKind}) • ${concept.reviewStatus}`);
16835
+ }
16836
+ }
16837
+ if (summary.recentExtensions.length > 0) {
16838
+ console.log(`
16839
+ Approved extensions`);
16840
+ for (const concept of summary.recentExtensions.slice(0, 5)) {
16841
+ const warning = concept.warning ? " • deprecation risk" : "";
16842
+ console.log(` • ${concept.id} (${concept.conceptKind}) • ${concept.status} • ${concept.adoptionCount} bindings${warning}`);
16843
+ }
16844
+ }
16845
+ console.log();
16846
+ }
16847
+ async function ontologyCommand(options) {
16848
+ materializeOntologyKernelSnapshot();
16849
+ const actions = [
16850
+ options.refresh ? "refresh" : null,
16851
+ options.review ? "review" : null,
16852
+ options.deprecate ? "deprecate" : null
16853
+ ].filter(Boolean);
16854
+ if (actions.length > 1) {
16855
+ throw new Error("Use only one of --refresh, --review, or --deprecate at a time");
16856
+ }
16857
+ if (options.refresh) {
16858
+ const result = refreshOntologyFrontier();
16859
+ console.log(`
16860
+ ✓ Refreshed ontology frontier`);
16861
+ console.log(` created: ${result.created}`);
16862
+ console.log(` updated: ${result.updated}`);
16863
+ console.log(` frontier total: ${result.total}`);
16864
+ console.log(` open review: ${result.open}`);
16865
+ if (options.verbose && result.concepts.length > 0) {
16866
+ for (const concept of result.concepts.slice(0, 8)) {
16867
+ console.log(` • ${concept.id} (${concept.conceptKind}) • ${concept.observationCount ?? 0} obs • ${(concept.confidence ?? 0).toFixed(2)} conf`);
16868
+ }
16869
+ }
16870
+ console.log();
16871
+ return;
16872
+ }
16873
+ if (options.review) {
16874
+ if (!options.decision) {
16875
+ throw new Error("--decision <promote|reject|defer> is required with --review");
16876
+ }
16877
+ const result = reviewOntologyConcept({
16878
+ conceptId: options.review,
16879
+ decision: options.decision,
16880
+ rationale: options.rationale ?? `${options.decision} ontology concept via CLI review`
16881
+ });
16882
+ if (!result.concept && !result.extension) {
16883
+ throw new Error(`Unknown ontology frontier concept: ${options.review}`);
16884
+ }
16885
+ console.log(`
16886
+ ✓ Recorded ontology review: ${options.review}`);
16887
+ console.log(` decision: ${options.decision}`);
16888
+ if (result.extension) {
16889
+ console.log(` promoted extension: ${result.extension.id}`);
16890
+ console.log(` status: ${result.extension.status}`);
16891
+ } else if (result.concept) {
16892
+ console.log(` review status: ${result.concept.reviewStatus}`);
16893
+ console.log(` concept kind: ${result.concept.conceptKind}`);
16894
+ }
16895
+ console.log();
16896
+ return;
16897
+ }
16898
+ if (options.deprecate) {
16899
+ const concept = deprecateOntologyExtension({
16900
+ conceptId: options.deprecate,
16901
+ rationale: options.rationale ?? `Deprecated ontology extension ${options.deprecate} via CLI`
16902
+ });
16903
+ if (!concept) {
16904
+ throw new Error(`Unknown ontology extension: ${options.deprecate}`);
16905
+ }
16906
+ console.log(`
16907
+ ✓ Deprecated ontology extension: ${concept.id}`);
16908
+ console.log(` kind: ${concept.conceptKind}`);
16909
+ console.log(` status: ${concept.status}`);
16910
+ if (concept.warning) {
16911
+ console.log(` warning: ${concept.warning}`);
16912
+ }
16913
+ console.log();
16914
+ return;
16915
+ }
16916
+ printStatus2();
16917
+ if (options.verbose) {
16918
+ const summary = getOntologyReviewSummary();
16919
+ const frontier = loadResolvedOntologyFrontierConcepts().slice(0, 8);
16920
+ const changes = loadOntologyChangeEvents().slice(0, 8);
16921
+ const extensions = loadResolvedOntologyExtensions().slice(0, 8);
16922
+ if (frontier.length > 0) {
16923
+ console.log(" Recent frontier concepts");
16924
+ for (const concept of frontier) {
16925
+ console.log(` • ${concept.id} • ${concept.conceptKind} • ${concept.reviewStatus} • ${concept.observationCount ?? 0} obs`);
16926
+ }
16927
+ console.log();
16928
+ }
16929
+ if (extensions.length > 0) {
16930
+ console.log(" Recent approved extensions");
16931
+ for (const concept of extensions) {
16932
+ const warning = concept.warning ? ` • ${concept.warning}` : "";
16933
+ console.log(` • ${concept.id} • ${concept.conceptKind} • ${concept.status} • ${concept.adoptionCount} bindings${warning}`);
16934
+ }
16935
+ console.log();
16936
+ }
16937
+ if (summary.adoption.topActiveConcepts.length > 0) {
16938
+ console.log(" Top active concepts");
16939
+ for (const concept of summary.adoption.topActiveConcepts) {
16940
+ console.log(` • ${concept.conceptId} • ${concept.totalBindings} bindings • ${Object.entries(concept.bindingsByTargetType).map(([target, count]) => `${target}:${count}`).join(" • ")}`);
16941
+ }
16942
+ console.log();
16943
+ }
16944
+ if (summary.adoption.unusedExtensions > 0) {
16945
+ console.log(` Unused approved extensions: ${summary.adoption.unusedExtensions}`);
16946
+ console.log();
16947
+ }
16948
+ if (summary.adoption.atRiskConcepts.length > 0) {
16949
+ console.log(" Deprecation-sensitive concepts");
16950
+ for (const concept of summary.adoption.atRiskConcepts) {
16951
+ console.log(` • ${concept.conceptId} • ${concept.warning}`);
16952
+ }
16953
+ console.log();
16954
+ }
16955
+ if (changes.length > 0) {
16956
+ console.log(" Recent ontology changes");
16957
+ for (const event of changes) {
16958
+ console.log(` • ${event.changeType} • ${event.conceptIds.join(", ")} • ${event.timestamp}`);
16959
+ }
16960
+ console.log();
16961
+ }
16962
+ }
16963
+ }
16964
+
15878
16965
  // src/cli.ts
15879
16966
  var program2 = new Command;
15880
- program2.name("helixevo").description("Self-evolving skill ecosystem for AI agents").version(VERSION).addHelpText("after", `
16967
+ program2.name("helixevo").description("Co-evolving skill and project brain for AI agents").version(VERSION).addHelpText("after", `
15881
16968
  Examples:
15882
16969
  $ helixevo watch Always-on learning (auto-capture + auto-evolve)
15883
16970
  $ helixevo watch --project myapp Watch with project context
@@ -15898,6 +16985,10 @@ Examples:
15898
16985
  $ helixevo topology --prepare <id> Prepare an accepted topology candidate
15899
16986
  $ helixevo topology --apply <planId> Apply a safe prepared topology plan
15900
16987
  $ helixevo topology --rollback <planId> Roll back an applied topology plan
16988
+ $ helixevo ontology --status --verbose Show frontier, extension, adoption, and deprecation-risk visibility
16989
+ $ helixevo ontology --refresh Derive frontier concepts from recurring evidence
16990
+ $ helixevo ontology --review <id> --decision promote
16991
+ Promote a reviewed frontier concept into approved extensions
15901
16992
  $ helixevo report --days 7 Weekly evolution report
15902
16993
  $ helixevo capture <session.json> Extract failures from session`);
15903
16994
  program2.command("init").description("Import existing skills + generate skill tests").option("--skills-paths <paths...>", "Paths to scan for existing skills").option("--skip-tests", "Skip skill test generation").action(initCommand);
@@ -15921,6 +17012,7 @@ program2.command("health").description("Assess network health: cohesion, coverag
15921
17012
  program2.command("metrics").description("Show correction rates, skill trends, and evolution impact").option("--verbose", "Show detailed per-skill breakdown").action(metricsCommand);
15922
17013
  program2.command("project-setup").description("Analyze a project folder and match it against your skill set").argument("<path>", "Path to the project folder").option("--verbose", "Show detailed analysis").option("--dry-run", "Analyze without saving project profile").action(projectSetupCommand);
15923
17014
  program2.command("topology").description("Reviewed topology execution control [--status] [--prepare <candidateId>] [--apply <planId>] [--rollback <planId>]").option("--status", "Show topology review and execution state").option("--prepare <candidateId>", "Prepare an accepted topology review candidate").option("--apply <planId>", "Apply a safe prepared topology plan").option("--rollback <planId>", "Roll back an applied topology plan").option("--verbose", "Show detailed topology plan state").action(topologyCommand);
17015
+ program2.command("ontology").description("Governed ontology frontier and semantic adoption control [--status] [--refresh] [--review <conceptId>] [--decision <promote|reject|defer>] [--deprecate <conceptId>]").option("--status", "Show ontology kernel, frontier, and extension state").option("--refresh", "Derive frontier concepts from recurring runtime evidence").option("--review <conceptId>", "Review a frontier concept").option("--decision <decision>", "Decision for --review: promote, reject, or defer").option("--rationale <text>", "Optional rationale for review or deprecate actions").option("--deprecate <conceptId>", "Deprecate an approved ontology extension").option("--verbose", "Show detailed ontology frontier state").action(ontologyCommand);
15924
17016
  program2.hook("postAction", () => {
15925
17017
  checkForUpdates().catch(() => {});
15926
17018
  });