helixevo 0.4.0 → 0.5.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
  }
@@ -10019,6 +10061,451 @@ function getTopologyReviewSummary() {
10019
10061
  recentDecisions: candidates.filter((candidate) => candidate.status !== "open").slice(0, 8)
10020
10062
  };
10021
10063
  }
10064
+ function slugFragment(value) {
10065
+ return value.toLowerCase().replace(/[^a-z0-9]+/g, "-").replace(/^-+|-+$/g, "").slice(0, 72) || "concept";
10066
+ }
10067
+ function ontologyKindCounts() {
10068
+ return {};
10069
+ }
10070
+ function ontologyStatusRank(status) {
10071
+ return status === "open" ? 4 : status === "deferred" ? 3 : status === "rejected" ? 2 : 1;
10072
+ }
10073
+ function mergeConcept(existing, next) {
10074
+ return {
10075
+ ...existing,
10076
+ ...next,
10077
+ createdAt: existing.createdAt,
10078
+ aliases: uniqueStrings([...existing.aliases ?? [], ...next.aliases ?? []]),
10079
+ promotionCriteria: uniqueStrings([...existing.promotionCriteria ?? [], ...next.promotionCriteria ?? []]),
10080
+ projectIds: uniqueStrings([...existing.projectIds ?? [], ...next.projectIds ?? []]),
10081
+ evidenceIds: uniqueStrings([...existing.evidenceIds ?? [], ...next.evidenceIds ?? []]),
10082
+ relatedSignalIds: uniqueStrings([...existing.relatedSignalIds ?? [], ...next.relatedSignalIds ?? []]),
10083
+ relatedMotifIds: uniqueStrings([...existing.relatedMotifIds ?? [], ...next.relatedMotifIds ?? []]),
10084
+ relatedReviewIds: uniqueStrings([...existing.relatedReviewIds ?? [], ...next.relatedReviewIds ?? []]),
10085
+ relatedTransferEventIds: uniqueStrings([...existing.relatedTransferEventIds ?? [], ...next.relatedTransferEventIds ?? []]),
10086
+ derivedFromKinds: uniqueStrings([...existing.derivedFromKinds ?? [], ...next.derivedFromKinds ?? []]),
10087
+ observationCount: Math.max(existing.observationCount ?? 0, next.observationCount ?? 0),
10088
+ confidence: Math.max(existing.confidence ?? 0, next.confidence ?? 0),
10089
+ lastObservedAt: maxTimestamp(existing.lastObservedAt, next.lastObservedAt, existing.createdAt, next.createdAt),
10090
+ sourceSummary: next.sourceSummary ?? existing.sourceSummary,
10091
+ migrationMap: { ...existing.migrationMap ?? {}, ...next.migrationMap ?? {} }
10092
+ };
10093
+ }
10094
+ function conceptArtifactSummary(action, concept) {
10095
+ return `${action} ontology ${concept.conceptKind} concept ${concept.name} using ${concept.observationCount ?? 0} supporting observations.`;
10096
+ }
10097
+ function appendOntologyArtifact(params) {
10098
+ const artifact = {
10099
+ id: `ontology_artifact_${slugFragment(params.concept.id)}_${Date.now()}`,
10100
+ createdAt: new Date().toISOString(),
10101
+ artifactType: "ontology-review",
10102
+ provenance: "native-ontology",
10103
+ title: `${params.concept.name} · ontology ${params.operation}`,
10104
+ summary: params.summary,
10105
+ sourceId: params.sourceId,
10106
+ operation: `ontology:${params.operation}`,
10107
+ metrics: params.metrics
10108
+ };
10109
+ appendEvolutionArtifact(artifact);
10110
+ return artifact.id;
10111
+ }
10112
+ function loadOntologyKernelSnapshot() {
10113
+ return readOntologyJson("kernel.json", kernelSnapshot());
10114
+ }
10115
+ function materializeOntologyKernelSnapshot() {
10116
+ const snapshot = { ...kernelSnapshot(), updatedAt: new Date().toISOString() };
10117
+ writeOntologyJson("kernel.json", snapshot);
10118
+ return snapshot;
10119
+ }
10120
+ function loadOntologyExtensions() {
10121
+ return readOntologyJson("extensions.json", []).slice().sort((a, b) => (b.lastObservedAt ?? b.createdAt).localeCompare(a.lastObservedAt ?? a.createdAt) || a.name.localeCompare(b.name));
10122
+ }
10123
+ function saveOntologyExtensions(concepts) {
10124
+ writeOntologyJson("extensions.json", concepts);
10125
+ }
10126
+ function loadOntologyFrontier() {
10127
+ return readOntologyJson("frontier.json", []).slice().sort((a, b) => (b.lastObservedAt ?? b.createdAt).localeCompare(a.lastObservedAt ?? a.createdAt) || a.name.localeCompare(b.name));
10128
+ }
10129
+ function saveOntologyFrontier(concepts) {
10130
+ writeOntologyJson("frontier.json", concepts);
10131
+ }
10132
+ function loadOntologyReviewDecisions() {
10133
+ return readOntologyJsonl("reviews.jsonl").slice().sort((a, b) => b.decidedAt.localeCompare(a.decidedAt));
10134
+ }
10135
+ function appendOntologyReviewDecision(decision) {
10136
+ appendOntologyJsonl("reviews.jsonl", decision);
10137
+ }
10138
+ function loadOntologyChangeEvents() {
10139
+ return readOntologyJsonl("change-log.jsonl").slice().sort((a, b) => b.timestamp.localeCompare(a.timestamp));
10140
+ }
10141
+ function appendOntologyChangeEvent(event) {
10142
+ appendOntologyJsonl("change-log.jsonl", event);
10143
+ }
10144
+ function buildCapabilityFrontierConcept() {
10145
+ const signalsByCapability = new Map;
10146
+ for (const signal of loadPressureSignals()) {
10147
+ const key = signal.capability?.trim() || signal.region?.trim();
10148
+ if (!key)
10149
+ continue;
10150
+ const normalized = slugFragment(key);
10151
+ const list = signalsByCapability.get(normalized) ?? [];
10152
+ list.push(signal);
10153
+ signalsByCapability.set(normalized, list);
10154
+ }
10155
+ return [...signalsByCapability.entries()].flatMap(([normalized, signals]) => {
10156
+ const distinctProjects = uniqueStrings(signals.map((signal) => signal.projectId ?? signal.projectPath));
10157
+ if (signals.length < 3 && distinctProjects.length < 2)
10158
+ return [];
10159
+ const first = signals[0];
10160
+ const capabilityName = first.capability?.trim() || first.region?.trim() || normalized;
10161
+ return [{
10162
+ id: `ontology_capability_${normalized}`,
10163
+ name: capabilityName.replace(/-/g, " "),
10164
+ conceptKind: "capability",
10165
+ layer: "frontier",
10166
+ status: "provisional",
10167
+ createdAt: new Date().toISOString(),
10168
+ description: `Recurring capability pressure around ${capabilityName}.`,
10169
+ promotionCriteria: [
10170
+ "Recurs across time or more than one meaningful project context",
10171
+ "Improves pressure explanation or project-gap attribution",
10172
+ "Cannot be expressed cleanly by the current capability vocabulary alone"
10173
+ ],
10174
+ reviewKey: `capability:${normalized}`,
10175
+ lastObservedAt: signals.map((signal) => signal.detectedAt).sort().at(-1),
10176
+ sourceSummary: `Derived from ${signals.length} pressure signals across ${Math.max(1, distinctProjects.length)} contexts.`,
10177
+ projectIds: distinctProjects,
10178
+ evidenceIds: uniqueStrings(signals.map((signal) => signal.id)),
10179
+ observationCount: signals.length,
10180
+ confidence: Math.min(0.95, 0.52 + Math.min(0.28, signals.length * 0.05) + Math.min(0.12, distinctProjects.length * 0.08)),
10181
+ relatedSignalIds: uniqueStrings(signals.map((signal) => signal.id)),
10182
+ derivedFromKinds: ["project-analysis-pressure", "capability-gap"],
10183
+ aliases: [capabilityName],
10184
+ migrationMap: {}
10185
+ }];
10186
+ });
10187
+ }
10188
+ function buildPressureTypeFrontierConcepts() {
10189
+ return loadPressureMotifs().flatMap((motif) => {
10190
+ if (motif.recurrenceCount < 3 && motif.projectIds.length < 2)
10191
+ return [];
10192
+ const capabilityPart = motif.capability ? ` ${motif.capability}` : "";
10193
+ const normalized = slugFragment(`${motif.kind}-${motif.capability ?? motif.key}`);
10194
+ return [{
10195
+ id: `ontology_pressure_${normalized}`,
10196
+ name: `${motif.kind}${capabilityPart}`.trim().replace(/-/g, " "),
10197
+ conceptKind: "pressure-type",
10198
+ layer: "frontier",
10199
+ status: "provisional",
10200
+ createdAt: new Date().toISOString(),
10201
+ description: motif.description,
10202
+ promotionCriteria: [
10203
+ "Recurs above isolated single-signal behavior",
10204
+ "Improves route recommendation explanation",
10205
+ "Supports at least one operational use-case in co-evolution control"
10206
+ ],
10207
+ reviewKey: `pressure:${normalized}`,
10208
+ lastObservedAt: motif.lastActivityAt,
10209
+ sourceSummary: `Derived from motif ${motif.key} with ${motif.recurrenceCount} observations across ${motif.projectIds.length} projects.`,
10210
+ projectIds: motif.projectIds,
10211
+ evidenceIds: uniqueStrings([...motif.pressureSignalIds, ...motif.linkedInterventionIds, ...motif.linkedTransferEventIds]),
10212
+ observationCount: motif.recurrenceCount,
10213
+ confidence: Math.min(0.95, 0.56 + Math.min(0.2, motif.recurrenceCount * 0.04) + Math.min(0.12, motif.projectIds.length * 0.06)),
10214
+ relatedSignalIds: motif.pressureSignalIds,
10215
+ relatedMotifIds: [motif.id],
10216
+ relatedTransferEventIds: motif.linkedTransferEventIds,
10217
+ derivedFromKinds: ["pressure-motif"],
10218
+ aliases: [motif.kind, motif.key],
10219
+ migrationMap: {}
10220
+ }];
10221
+ });
10222
+ }
10223
+ function topologyMotifDescriptor(candidate) {
10224
+ if (candidate.changeType === "split" && candidate.title.startsWith("Split overloaded skill:"))
10225
+ return "overloaded-skill-split-pressure";
10226
+ if (candidate.changeType === "rewire" && candidate.title.startsWith("Resolve conflicting pair:"))
10227
+ return "conflict-rewire-pressure";
10228
+ if (candidate.changeType === "promote" && candidate.title.startsWith("Promote recurring motif:"))
10229
+ return "motif-promotion-pressure";
10230
+ if (candidate.changeType === "merge" && candidate.title.startsWith("Merge overlap:"))
10231
+ return "overlap-merge-pressure";
10232
+ if (candidate.changeType === "consolidate")
10233
+ return "consolidation-pressure";
10234
+ return null;
10235
+ }
10236
+ function buildTopologyMotifFrontierConcepts() {
10237
+ const grouped = new Map;
10238
+ for (const candidate of loadResolvedTopologyReviewCandidates()) {
10239
+ const descriptor = topologyMotifDescriptor(candidate);
10240
+ if (!descriptor)
10241
+ continue;
10242
+ const list = grouped.get(descriptor) ?? [];
10243
+ list.push(candidate);
10244
+ grouped.set(descriptor, list);
10245
+ }
10246
+ return [...grouped.entries()].flatMap(([descriptor, candidates]) => {
10247
+ if (candidates.length < 2)
10248
+ return [];
10249
+ const first = candidates[0];
10250
+ return [{
10251
+ id: `ontology_topology_${slugFragment(descriptor)}`,
10252
+ name: descriptor.replace(/-/g, " "),
10253
+ conceptKind: "topology-motif",
10254
+ layer: "frontier",
10255
+ status: "provisional",
10256
+ createdAt: new Date().toISOString(),
10257
+ description: `Recurring structural review pattern: ${descriptor.replace(/-/g, " ")}.`,
10258
+ promotionCriteria: [
10259
+ "Recurs across multiple structural review candidates",
10260
+ "Improves topology review explanation or triage",
10261
+ "Supports a bounded operational use-case in the topology control surface"
10262
+ ],
10263
+ reviewKey: `topology:${slugFragment(descriptor)}`,
10264
+ lastObservedAt: candidates.map((candidate) => candidate.lastActivityAt).sort().at(-1),
10265
+ sourceSummary: `Derived from ${candidates.length} reviewed topology candidates of type ${first.changeType}.`,
10266
+ projectIds: uniqueStrings(candidates.flatMap((candidate) => candidate.projectIds ?? [])),
10267
+ evidenceIds: uniqueStrings(candidates.flatMap((candidate) => [...candidate.evidence, candidate.id])),
10268
+ observationCount: candidates.length,
10269
+ confidence: Math.min(0.92, 0.55 + Math.min(0.24, candidates.length * 0.08)),
10270
+ relatedReviewIds: candidates.map((candidate) => candidate.id),
10271
+ relatedMotifIds: uniqueStrings(candidates.flatMap((candidate) => candidate.relatedMotifIds ?? [])),
10272
+ relatedTransferEventIds: uniqueStrings(candidates.flatMap((candidate) => candidate.relatedTransferEventIds ?? [])),
10273
+ derivedFromKinds: ["topology-review"],
10274
+ aliases: [first.changeType, descriptor],
10275
+ migrationMap: {}
10276
+ }];
10277
+ });
10278
+ }
10279
+ function deriveOntologyFrontierConcepts() {
10280
+ const byKey = new Map;
10281
+ for (const concept of [
10282
+ ...buildCapabilityFrontierConcept(),
10283
+ ...buildPressureTypeFrontierConcepts(),
10284
+ ...buildTopologyMotifFrontierConcepts()
10285
+ ]) {
10286
+ const key = concept.reviewKey ?? concept.id;
10287
+ const current = byKey.get(key);
10288
+ byKey.set(key, current ? mergeConcept(current, concept) : concept);
10289
+ }
10290
+ 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);
10291
+ }
10292
+ function refreshOntologyFrontier() {
10293
+ materializeOntologyKernelSnapshot();
10294
+ const existing = loadOntologyFrontier();
10295
+ const extensions = loadOntologyExtensions();
10296
+ const promotedKeys = new Set(extensions.map((concept) => concept.reviewKey ?? concept.id));
10297
+ const existingByKey = new Map(existing.map((concept) => [concept.reviewKey ?? concept.id, concept]));
10298
+ const derived = deriveOntologyFrontierConcepts().filter((concept) => !promotedKeys.has(concept.reviewKey ?? concept.id));
10299
+ let created = 0;
10300
+ let updated = 0;
10301
+ for (const concept of derived) {
10302
+ const key = concept.reviewKey ?? concept.id;
10303
+ const current = existingByKey.get(key);
10304
+ if (!current) {
10305
+ existingByKey.set(key, concept);
10306
+ created += 1;
10307
+ appendOntologyChangeEvent({
10308
+ id: `ontology_change_hypothesized_${slugFragment(concept.id)}_${Date.now()}`,
10309
+ timestamp: new Date().toISOString(),
10310
+ changeType: "concept-hypothesized",
10311
+ conceptIds: [concept.id],
10312
+ reason: concept.sourceSummary ?? conceptArtifactSummary("Hypothesized", concept),
10313
+ evidenceIds: concept.evidenceIds,
10314
+ ontologyVersion: ONTOLOGY_SPEC_VERSION
10315
+ });
10316
+ appendOntologyArtifact({
10317
+ concept,
10318
+ operation: "refresh",
10319
+ sourceId: concept.id,
10320
+ summary: conceptArtifactSummary("Hypothesized", concept),
10321
+ metrics: {
10322
+ observations: concept.observationCount ?? 0,
10323
+ confidence: concept.confidence ?? 0,
10324
+ evidence: concept.evidenceIds?.length ?? 0
10325
+ }
10326
+ });
10327
+ continue;
10328
+ }
10329
+ existingByKey.set(key, mergeConcept(current, concept));
10330
+ updated += 1;
10331
+ }
10332
+ 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));
10333
+ saveOntologyFrontier(frontier);
10334
+ const resolved = loadResolvedOntologyFrontierConcepts();
10335
+ return {
10336
+ created,
10337
+ updated,
10338
+ total: resolved.length,
10339
+ open: resolved.filter((concept) => concept.reviewStatus === "open").length,
10340
+ concepts: resolved
10341
+ };
10342
+ }
10343
+ function loadResolvedOntologyFrontierConcepts() {
10344
+ const concepts = loadOntologyFrontier();
10345
+ const latestDecisionByConcept = new Map;
10346
+ for (const decision of loadOntologyReviewDecisions()) {
10347
+ const existing = latestDecisionByConcept.get(decision.conceptId);
10348
+ if (!existing || decision.decidedAt > existing.decidedAt)
10349
+ latestDecisionByConcept.set(decision.conceptId, decision);
10350
+ }
10351
+ return concepts.map((concept) => {
10352
+ const latestReview = latestDecisionByConcept.get(concept.id);
10353
+ const reviewStatus = latestReview?.decision === "promote" ? "promoted" : latestReview?.decision === "reject" ? "rejected" : latestReview?.decision === "defer" ? "deferred" : "open";
10354
+ return {
10355
+ ...concept,
10356
+ reviewStatus,
10357
+ latestReview,
10358
+ lastActivityAt: maxTimestamp(concept.lastObservedAt, latestReview?.decidedAt, concept.createdAt)
10359
+ };
10360
+ }).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));
10361
+ }
10362
+ function reviewOntologyConcept(params) {
10363
+ const frontier = loadOntologyFrontier();
10364
+ const concept = frontier.find((entry) => entry.id === params.conceptId);
10365
+ if (!concept)
10366
+ return { concept: null, extension: null };
10367
+ const governance = getActiveGovernanceSummary();
10368
+ const decision = {
10369
+ id: `ontology_review_${params.decision}_${slugFragment(params.conceptId)}_${Date.now()}`,
10370
+ conceptId: params.conceptId,
10371
+ decision: params.decision,
10372
+ decidedAt: new Date().toISOString(),
10373
+ governanceMode: governance.activeMode,
10374
+ rationale: params.rationale.trim() || `${params.decision} ontology concept via operator review`,
10375
+ decidedBy: "operator"
10376
+ };
10377
+ appendOntologyReviewDecision(decision);
10378
+ if (params.decision === "promote") {
10379
+ const extensions = loadOntologyExtensions();
10380
+ const promotedConcept = {
10381
+ ...concept,
10382
+ layer: "extension",
10383
+ status: "active",
10384
+ migrationMap: { ...concept.migrationMap ?? {}, [concept.id]: concept.id },
10385
+ aliases: uniqueStrings([...concept.aliases ?? [], concept.name]),
10386
+ lastObservedAt: maxTimestamp(concept.lastObservedAt, decision.decidedAt)
10387
+ };
10388
+ saveOntologyExtensions([
10389
+ promotedConcept,
10390
+ ...extensions.filter((entry) => entry.id !== concept.id)
10391
+ ]);
10392
+ saveOntologyFrontier(frontier.filter((entry) => entry.id !== concept.id));
10393
+ appendOntologyChangeEvent({
10394
+ id: `ontology_change_promoted_${slugFragment(concept.id)}_${Date.now()}`,
10395
+ timestamp: decision.decidedAt,
10396
+ changeType: "concept-promoted",
10397
+ conceptIds: [concept.id],
10398
+ reason: decision.rationale,
10399
+ evidenceIds: concept.evidenceIds,
10400
+ approvedBy: decision.decidedBy,
10401
+ ontologyVersion: ONTOLOGY_SPEC_VERSION
10402
+ });
10403
+ appendOntologyArtifact({
10404
+ concept: promotedConcept,
10405
+ operation: "promote",
10406
+ sourceId: decision.id,
10407
+ summary: conceptArtifactSummary("Promoted", promotedConcept),
10408
+ metrics: {
10409
+ observations: promotedConcept.observationCount ?? 0,
10410
+ confidence: promotedConcept.confidence ?? 0,
10411
+ evidence: promotedConcept.evidenceIds?.length ?? 0
10412
+ }
10413
+ });
10414
+ return {
10415
+ concept: null,
10416
+ extension: promotedConcept
10417
+ };
10418
+ }
10419
+ if (params.decision === "reject") {
10420
+ appendOntologyChangeEvent({
10421
+ id: `ontology_change_rejected_${slugFragment(concept.id)}_${Date.now()}`,
10422
+ timestamp: decision.decidedAt,
10423
+ changeType: "concept-rejected",
10424
+ conceptIds: [concept.id],
10425
+ reason: decision.rationale,
10426
+ evidenceIds: concept.evidenceIds,
10427
+ approvedBy: decision.decidedBy,
10428
+ ontologyVersion: ONTOLOGY_SPEC_VERSION
10429
+ });
10430
+ appendOntologyArtifact({
10431
+ concept,
10432
+ operation: "reject",
10433
+ sourceId: decision.id,
10434
+ summary: conceptArtifactSummary("Rejected", concept),
10435
+ metrics: {
10436
+ observations: concept.observationCount ?? 0,
10437
+ confidence: concept.confidence ?? 0,
10438
+ evidence: concept.evidenceIds?.length ?? 0
10439
+ }
10440
+ });
10441
+ }
10442
+ return {
10443
+ concept: loadResolvedOntologyFrontierConcepts().find((entry) => entry.id === params.conceptId) ?? null,
10444
+ extension: null
10445
+ };
10446
+ }
10447
+ function deprecateOntologyExtension(params) {
10448
+ const extensions = loadOntologyExtensions();
10449
+ const current = extensions.find((entry) => entry.id === params.conceptId);
10450
+ if (!current)
10451
+ return null;
10452
+ const governance = getActiveGovernanceSummary();
10453
+ const deprecated = {
10454
+ ...current,
10455
+ status: "deprecated",
10456
+ lastObservedAt: maxTimestamp(current.lastObservedAt, new Date().toISOString())
10457
+ };
10458
+ saveOntologyExtensions([
10459
+ deprecated,
10460
+ ...extensions.filter((entry) => entry.id !== params.conceptId)
10461
+ ]);
10462
+ appendOntologyChangeEvent({
10463
+ id: `ontology_change_deprecated_${slugFragment(current.id)}_${Date.now()}`,
10464
+ timestamp: new Date().toISOString(),
10465
+ changeType: "concept-deprecated",
10466
+ conceptIds: [current.id],
10467
+ reason: params.rationale.trim() || `Deprecated ontology extension ${current.name}`,
10468
+ evidenceIds: current.evidenceIds,
10469
+ approvedBy: "operator",
10470
+ ontologyVersion: ONTOLOGY_SPEC_VERSION
10471
+ });
10472
+ appendOntologyArtifact({
10473
+ concept: deprecated,
10474
+ operation: "deprecate",
10475
+ sourceId: `${current.id}:deprecate`,
10476
+ summary: `Deprecated ontology extension ${deprecated.name} under ${governance.activeMode} governance.`,
10477
+ metrics: {
10478
+ observations: deprecated.observationCount ?? 0,
10479
+ confidence: deprecated.confidence ?? 0
10480
+ }
10481
+ });
10482
+ return deprecated;
10483
+ }
10484
+ function getOntologyReviewSummary() {
10485
+ const kernel = loadOntologyKernelSnapshot();
10486
+ const extensions = loadOntologyExtensions();
10487
+ const frontier = loadResolvedOntologyFrontierConcepts();
10488
+ const changeEvents = loadOntologyChangeEvents();
10489
+ const byConceptKind = ontologyKindCounts();
10490
+ for (const concept of [...extensions, ...frontier]) {
10491
+ byConceptKind[concept.conceptKind] = (byConceptKind[concept.conceptKind] ?? 0) + 1;
10492
+ }
10493
+ return {
10494
+ kernelConcepts: kernel.entityNames.length,
10495
+ extensions: extensions.length,
10496
+ frontier: frontier.length,
10497
+ reviewOpen: frontier.filter((concept) => concept.reviewStatus === "open").length,
10498
+ promoted: extensions.filter((concept) => concept.status === "active").length,
10499
+ rejected: frontier.filter((concept) => concept.reviewStatus === "rejected").length,
10500
+ deferred: frontier.filter((concept) => concept.reviewStatus === "deferred").length,
10501
+ deprecated: extensions.filter((concept) => concept.status === "deprecated").length,
10502
+ changeEvents: changeEvents.length,
10503
+ byConceptKind,
10504
+ backlog: frontier.filter((concept) => concept.reviewStatus === "open" || concept.reviewStatus === "deferred").slice(0, 10),
10505
+ recentChanges: changeEvents.slice(0, 10),
10506
+ recentExtensions: extensions.slice(0, 8)
10507
+ };
10508
+ }
10022
10509
  function buildDerivedEvolutionArtifact(iteration, proposal) {
10023
10510
  return {
10024
10511
  id: `artifact_derived_${iteration.id}_${proposal.id}`,
@@ -15875,9 +16362,131 @@ async function topologyCommand(options) {
15875
16362
  }
15876
16363
  }
15877
16364
 
16365
+ // src/commands/ontology.ts
16366
+ init_data();
16367
+ function printStatus2() {
16368
+ const summary = getOntologyReviewSummary();
16369
+ console.log(`
16370
+ Ontology Frontier`);
16371
+ console.log(" ────────────────────────────────────────");
16372
+ console.log(` Kernel: ${summary.kernelConcepts} canonical entities`);
16373
+ console.log(` Extensions: ${summary.extensions} active • ${summary.deprecated} deprecated`);
16374
+ console.log(` Frontier: ${summary.frontier} total • ${summary.reviewOpen} open • ${summary.deferred} deferred • ${summary.rejected} rejected`);
16375
+ console.log(` Change log: ${summary.changeEvents} events`);
16376
+ if (summary.backlog.length > 0) {
16377
+ console.log(`
16378
+ Frontier concepts ready for review`);
16379
+ for (const concept of summary.backlog.slice(0, 5)) {
16380
+ console.log(` • ${concept.id} (${concept.conceptKind}) • ${concept.reviewStatus}`);
16381
+ }
16382
+ }
16383
+ if (summary.recentExtensions.length > 0) {
16384
+ console.log(`
16385
+ Approved extensions`);
16386
+ for (const concept of summary.recentExtensions.slice(0, 5)) {
16387
+ console.log(` • ${concept.id} (${concept.conceptKind}) • ${concept.status}`);
16388
+ }
16389
+ }
16390
+ console.log();
16391
+ }
16392
+ async function ontologyCommand(options) {
16393
+ materializeOntologyKernelSnapshot();
16394
+ const actions = [
16395
+ options.refresh ? "refresh" : null,
16396
+ options.review ? "review" : null,
16397
+ options.deprecate ? "deprecate" : null
16398
+ ].filter(Boolean);
16399
+ if (actions.length > 1) {
16400
+ throw new Error("Use only one of --refresh, --review, or --deprecate at a time");
16401
+ }
16402
+ if (options.refresh) {
16403
+ const result = refreshOntologyFrontier();
16404
+ console.log(`
16405
+ ✓ Refreshed ontology frontier`);
16406
+ console.log(` created: ${result.created}`);
16407
+ console.log(` updated: ${result.updated}`);
16408
+ console.log(` frontier total: ${result.total}`);
16409
+ console.log(` open review: ${result.open}`);
16410
+ if (options.verbose && result.concepts.length > 0) {
16411
+ for (const concept of result.concepts.slice(0, 8)) {
16412
+ console.log(` • ${concept.id} (${concept.conceptKind}) • ${concept.observationCount ?? 0} obs • ${(concept.confidence ?? 0).toFixed(2)} conf`);
16413
+ }
16414
+ }
16415
+ console.log();
16416
+ return;
16417
+ }
16418
+ if (options.review) {
16419
+ if (!options.decision) {
16420
+ throw new Error("--decision <promote|reject|defer> is required with --review");
16421
+ }
16422
+ const result = reviewOntologyConcept({
16423
+ conceptId: options.review,
16424
+ decision: options.decision,
16425
+ rationale: options.rationale ?? `${options.decision} ontology concept via CLI review`
16426
+ });
16427
+ if (!result.concept && !result.extension) {
16428
+ throw new Error(`Unknown ontology frontier concept: ${options.review}`);
16429
+ }
16430
+ console.log(`
16431
+ ✓ Recorded ontology review: ${options.review}`);
16432
+ console.log(` decision: ${options.decision}`);
16433
+ if (result.extension) {
16434
+ console.log(` promoted extension: ${result.extension.id}`);
16435
+ console.log(` status: ${result.extension.status}`);
16436
+ } else if (result.concept) {
16437
+ console.log(` review status: ${result.concept.reviewStatus}`);
16438
+ console.log(` concept kind: ${result.concept.conceptKind}`);
16439
+ }
16440
+ console.log();
16441
+ return;
16442
+ }
16443
+ if (options.deprecate) {
16444
+ const concept = deprecateOntologyExtension({
16445
+ conceptId: options.deprecate,
16446
+ rationale: options.rationale ?? `Deprecated ontology extension ${options.deprecate} via CLI`
16447
+ });
16448
+ if (!concept) {
16449
+ throw new Error(`Unknown ontology extension: ${options.deprecate}`);
16450
+ }
16451
+ console.log(`
16452
+ ✓ Deprecated ontology extension: ${concept.id}`);
16453
+ console.log(` kind: ${concept.conceptKind}`);
16454
+ console.log(` status: ${concept.status}`);
16455
+ console.log();
16456
+ return;
16457
+ }
16458
+ printStatus2();
16459
+ if (options.verbose) {
16460
+ const frontier = loadResolvedOntologyFrontierConcepts().slice(0, 8);
16461
+ const changes = loadOntologyChangeEvents().slice(0, 8);
16462
+ const extensions = loadOntologyExtensions().slice(0, 8);
16463
+ if (frontier.length > 0) {
16464
+ console.log(" Recent frontier concepts");
16465
+ for (const concept of frontier) {
16466
+ console.log(` • ${concept.id} • ${concept.conceptKind} • ${concept.reviewStatus} • ${concept.observationCount ?? 0} obs`);
16467
+ }
16468
+ console.log();
16469
+ }
16470
+ if (extensions.length > 0) {
16471
+ console.log(" Recent approved extensions");
16472
+ for (const concept of extensions) {
16473
+ console.log(` • ${concept.id} • ${concept.conceptKind} • ${concept.status}`);
16474
+ }
16475
+ console.log();
16476
+ }
16477
+ if (changes.length > 0) {
16478
+ console.log(" Recent ontology changes");
16479
+ for (const event of changes) {
16480
+ console.log(` • ${event.changeType} • ${event.conceptIds.join(", ")} • ${event.timestamp}`);
16481
+ }
16482
+ console.log();
16483
+ }
16484
+ }
16485
+ }
16486
+
15878
16487
  // src/cli.ts
15879
16488
  var program2 = new Command;
15880
- program2.name("helixevo").description("Self-evolving skill ecosystem for AI agents").version(VERSION).addHelpText("after", `
16489
+ program2.name("helixevo").description("Co-evolving skill and project brain for AI agents").version(VERSION).addHelpText("after", `
15881
16490
  Examples:
15882
16491
  $ helixevo watch Always-on learning (auto-capture + auto-evolve)
15883
16492
  $ helixevo watch --project myapp Watch with project context
@@ -15898,6 +16507,9 @@ Examples:
15898
16507
  $ helixevo topology --prepare <id> Prepare an accepted topology candidate
15899
16508
  $ helixevo topology --apply <planId> Apply a safe prepared topology plan
15900
16509
  $ helixevo topology --rollback <planId> Roll back an applied topology plan
16510
+ $ helixevo ontology --refresh Derive frontier concepts from recurring evidence
16511
+ $ helixevo ontology --review <id> --decision promote
16512
+ Promote a reviewed frontier concept into approved extensions
15901
16513
  $ helixevo report --days 7 Weekly evolution report
15902
16514
  $ helixevo capture <session.json> Extract failures from session`);
15903
16515
  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 +16533,7 @@ program2.command("health").description("Assess network health: cohesion, coverag
15921
16533
  program2.command("metrics").description("Show correction rates, skill trends, and evolution impact").option("--verbose", "Show detailed per-skill breakdown").action(metricsCommand);
15922
16534
  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
16535
  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);
16536
+ program2.command("ontology").description("Governed ontology frontier 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
16537
  program2.hook("postAction", () => {
15925
16538
  checkForUpdates().catch(() => {});
15926
16539
  });
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "helixevo",
3
- "version": "0.4.0",
3
+ "version": "0.5.0",
4
4
  "description": "Co-evolving skill and project brain for AI agents, with ontology-aware learning, governed response, rollbackable topology control, and a premium dashboard.",
5
5
  "type": "module",
6
6
  "bin": {