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/CHANGELOG.md +16 -0
- package/README.md +15 -2
- package/dashboard/app/api/ontology/route.ts +89 -0
- package/dashboard/app/coevolution/client.tsx +11 -1
- package/dashboard/app/coevolution/page.tsx +3 -2
- package/dashboard/app/commands/page.tsx +23 -0
- package/dashboard/app/guide/page.tsx +438 -185
- package/dashboard/app/ontology/client.tsx +247 -0
- package/dashboard/app/ontology/page.tsx +9 -0
- package/dashboard/app/page.tsx +12 -2
- package/dashboard/app/topology/client.tsx +1 -0
- package/dashboard/components/sidebar-nav.tsx +1 -0
- package/dashboard/lib/data.ts +325 -4
- package/dist/cli.js +614 -1
- package/package.json +1 -1
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("
|
|
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.
|
|
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": {
|