helixevo 0.2.40 → 0.3.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.
@@ -18,10 +18,16 @@ function readJsonl<T>(filename: string): T[] {
18
18
 
19
19
  // ─── Types ──────────────────────────────────────────────────────
20
20
 
21
+ export type CognitiveRole = 'generalist' | 'specialist' | 'hybrid'
22
+ export type StabilityState = 'stable' | 'adaptive' | 'experimental'
23
+ export type PlasticityState = 'consolidated' | 'volatile' | 'candidate'
24
+
21
25
  export interface SkillNode {
22
26
  id: string; name: string; layer: string; score: number
23
27
  generation: number; status: string; tags: string[]
24
28
  failureCount: number; lastEvolved: string; project?: string
29
+ cognitiveRole?: CognitiveRole; stabilityState?: StabilityState; plasticityState?: PlasticityState
30
+ capabilities?: string[]; lineageId?: string
25
31
  }
26
32
 
27
33
  export interface SkillEdge {
@@ -31,7 +37,7 @@ export interface SkillEdge {
31
37
  }
32
38
 
33
39
  export interface SkillGraph {
34
- updated: string; nodes: SkillNode[]; edges: SkillEdge[]
40
+ updated: string; ontologyVersion?: string; nodes: SkillNode[]; edges: SkillEdge[]
35
41
  clusters: { id: string; name: string; skills: string[]; cohesion: number }[]
36
42
  }
37
43
 
@@ -39,6 +45,7 @@ export interface Failure {
39
45
  id: string; sessionId: string; timestamp: string; project: string | null
40
46
  userRequest: string; agentAction: string; correction: string
41
47
  correctionType: string; skillsActive: string[]; resolved?: boolean
48
+ pressureSignals?: string[]; capabilityGaps?: string[]; activationTraceId?: string
42
49
  }
43
50
 
44
51
  export interface Frontier {
@@ -71,6 +78,159 @@ export interface CanaryEntry {
71
78
  skillSlug: string; version: string; deployedAt: string; expiresAt: string
72
79
  }
73
80
 
81
+ export type ArtifactProvenance = 'native-evolution' | 'derived-history'
82
+ export type ActivationProvenance = 'capture-derived' | 'project-analysis' | 'derived-failure'
83
+ export type PressureProvenance = 'failure-native' | 'project-analysis-native' | 'failure-derived' | 'profile-gap-derived'
84
+
85
+ export interface EvidenceArtifact {
86
+ id: string
87
+ createdAt: string
88
+ artifactType: 'evolution' | 'replay' | 'regression' | 'canary' | 'project-analysis' | 'ontology-review'
89
+ provenance: ArtifactProvenance
90
+ title: string
91
+ summary: string
92
+ sourceId?: string
93
+ iterationId?: string
94
+ proposalId?: string
95
+ projectId?: string
96
+ targetSkill?: string
97
+ operation?: string
98
+ outcome?: 'accepted' | 'rejected' | 'canary'
99
+ clusterType?: string
100
+ trigger?: string
101
+ judgeScores?: {
102
+ taskCompletion: number
103
+ correctionAlignment: number
104
+ sideEffectCheck: number
105
+ }
106
+ regression?: {
107
+ total: number
108
+ passed: number
109
+ passRate: number
110
+ }
111
+ metrics?: Record<string, number>
112
+ }
113
+
114
+ export interface ActivationTrace {
115
+ id: string
116
+ timestamp: string
117
+ provenance: ActivationProvenance
118
+ sourceId: string
119
+ projectId?: string
120
+ projectPath?: string
121
+ sessionId?: string
122
+ activatedSkillIds: string[]
123
+ relevantSkillIds?: string[]
124
+ reasonSummary: string
125
+ contextSummary?: string
126
+ gapAreas?: string[]
127
+ recommendations?: string[]
128
+ relatedFailureId?: string
129
+ traceVersion?: string
130
+ }
131
+
132
+ export interface PressureSignal {
133
+ id: string
134
+ kind: string
135
+ provenance: PressureProvenance
136
+ sourceType: 'failure' | 'correction' | 'replay' | 'regression' | 'project-analysis' | 'research' | 'manual'
137
+ sourceId: string
138
+ projectId?: string
139
+ projectPath?: string
140
+ detectedAt: string
141
+ severity: number
142
+ priority?: 'high' | 'medium' | 'low'
143
+ capability?: string
144
+ region?: string
145
+ description?: string
146
+ suggestedAction?: 'research' | 'specialize' | 'create'
147
+ relatedFailureId?: string
148
+ relatedActivationTraceId?: string
149
+ skillIds?: string[]
150
+ status?: 'open' | 'addressed'
151
+ }
152
+
153
+ export interface ProjectPressureSummary {
154
+ projectId: string
155
+ totalSignals: number
156
+ nativeSignals: number
157
+ derivedSignals: number
158
+ highPrioritySignals: number
159
+ topCapabilities: string[]
160
+ }
161
+
162
+ export interface CoEvolutionDashboardSummary {
163
+ pressureSignals: {
164
+ total: number
165
+ native: number
166
+ derived: number
167
+ }
168
+ topProjects: ProjectPressureSummary[]
169
+ rolePressure: Record<CognitiveRole, number>
170
+ crossProjectGapAreas: { area: string; projects: string[]; count: number }[]
171
+ activeRecommendations: { action: 'research' | 'specialize' | 'create'; count: number }[]
172
+ }
173
+
174
+ export interface OntologyDashboardSummary {
175
+ specVersion: string
176
+ source: 'graph' | 'compat-derived'
177
+ skillRoles: Record<CognitiveRole, number>
178
+ stabilityStates: Record<StabilityState, number>
179
+ plasticityStates: Record<PlasticityState, number>
180
+ enrichedSkillNodes: number
181
+ relationFamiliesObserved: number
182
+ mutationOperationsObserved: number
183
+ observedMutationActions: string[]
184
+ evidenceBackedProposals: number
185
+ artifacts: {
186
+ total: number
187
+ native: number
188
+ derived: number
189
+ }
190
+ activationTraces: {
191
+ total: number
192
+ native: number
193
+ derived: number
194
+ }
195
+ pressureSignals: {
196
+ total: number
197
+ native: number
198
+ derived: number
199
+ }
200
+ annotatedFailures: {
201
+ pressureSignals: number
202
+ capabilityGaps: number
203
+ activationTraces: number
204
+ }
205
+ }
206
+
207
+ const DASHBOARD_ONTOLOGY_SPEC_VERSION = '0.1.0'
208
+
209
+ function emptyCounts<T extends string>(values: readonly T[]): Record<T, number> {
210
+ return Object.fromEntries(values.map((value) => [value, 0])) as Record<T, number>
211
+ }
212
+
213
+ function inferCognitiveRole(node: SkillNode): CognitiveRole {
214
+ if (node.cognitiveRole) return node.cognitiveRole
215
+ if (node.layer === 'project' || Boolean(node.project)) return 'specialist'
216
+ if (node.generation > 0 && node.failureCount > 0) return 'hybrid'
217
+ return 'generalist'
218
+ }
219
+
220
+ function inferStabilityState(node: SkillNode): StabilityState {
221
+ if (node.stabilityState) return node.stabilityState
222
+ if (node.status === 'canary' || node.score < 0.55) return 'experimental'
223
+ if (inferCognitiveRole(node) === 'generalist' && node.generation > 0 && node.score >= 0.7) return 'stable'
224
+ return 'adaptive'
225
+ }
226
+
227
+ function inferPlasticityState(node: SkillNode): PlasticityState {
228
+ if (node.plasticityState) return node.plasticityState
229
+ if (node.status === 'canary' || node.generation === 0) return 'candidate'
230
+ if (node.generation >= 2 || node.score >= 0.8) return 'consolidated'
231
+ return 'volatile'
232
+ }
233
+
74
234
  // ─── Loaders ────────────────────────────────────────────────────
75
235
 
76
236
  export function loadGraph(): SkillGraph {
@@ -109,6 +269,218 @@ export function loadSkillContent(slug: string): string {
109
269
  return readFileSync(path, 'utf-8')
110
270
  }
111
271
 
272
+ function buildDerivedEvolutionArtifact(iteration: Iteration, proposal: Proposal): EvidenceArtifact {
273
+ return {
274
+ id: `artifact_derived_${iteration.id}_${proposal.id}`,
275
+ createdAt: iteration.timestamp,
276
+ artifactType: 'evolution',
277
+ provenance: 'derived-history',
278
+ sourceId: iteration.id,
279
+ iterationId: iteration.id,
280
+ proposalId: proposal.id,
281
+ title: `${proposal.targetSkill} · ${proposal.action}`,
282
+ summary: proposal.description,
283
+ targetSkill: proposal.targetSkill,
284
+ operation: proposal.action,
285
+ outcome: proposal.outcome as 'accepted' | 'rejected' | 'canary',
286
+ trigger: iteration.trigger,
287
+ judgeScores: {
288
+ taskCompletion: proposal.judges.taskCompletion.score,
289
+ correctionAlignment: proposal.judges.correctionAlignment.score,
290
+ sideEffectCheck: proposal.judges.sideEffectCheck.score,
291
+ },
292
+ regression: proposal.regressionResult,
293
+ metrics: {
294
+ regressionPassRate: proposal.regressionResult.passRate,
295
+ proposalConsensus: proposal.consensus ? 1 : 0,
296
+ },
297
+ }
298
+ }
299
+
300
+ function buildDerivedActivationTraceFromFailure(failure: Failure): ActivationTrace {
301
+ return {
302
+ id: failure.activationTraceId ?? `activation_derived_${failure.id}`,
303
+ timestamp: failure.timestamp,
304
+ provenance: 'derived-failure',
305
+ sourceId: failure.id,
306
+ projectId: failure.project ?? undefined,
307
+ sessionId: failure.sessionId,
308
+ activatedSkillIds: failure.skillsActive,
309
+ relevantSkillIds: failure.skillsActive,
310
+ reasonSummary: `${failure.correctionType} correction captured from failure`,
311
+ contextSummary: failure.correction,
312
+ relatedFailureId: failure.id,
313
+ traceVersion: '0.1.0',
314
+ }
315
+ }
316
+
317
+ export function loadEvolutionArtifacts(): EvidenceArtifact[] {
318
+ const nativeArtifacts = readJsonl<EvidenceArtifact>('evolution-artifacts.jsonl')
319
+ const history = loadHistory()
320
+ const derivedArtifacts = history.iterations.flatMap((iteration) => iteration.proposals.map((proposal) => buildDerivedEvolutionArtifact(iteration, proposal)))
321
+ const nativeProposalIds = new Set(nativeArtifacts.map((artifact) => artifact.proposalId).filter(Boolean))
322
+ return [
323
+ ...nativeArtifacts,
324
+ ...derivedArtifacts.filter((artifact) => !artifact.proposalId || !nativeProposalIds.has(artifact.proposalId)),
325
+ ].sort((a, b) => b.createdAt.localeCompare(a.createdAt))
326
+ }
327
+
328
+ export function loadActivationTraces(): ActivationTrace[] {
329
+ const nativeTraces = readJsonl<ActivationTrace>('activation-traces.jsonl')
330
+ const failures = loadFailures()
331
+ const derivedTraces = failures
332
+ .filter((failure) => failure.skillsActive.length > 0)
333
+ .map(buildDerivedActivationTraceFromFailure)
334
+ const nativeSourceIds = new Set(nativeTraces.map((trace) => trace.sourceId))
335
+ return [
336
+ ...nativeTraces,
337
+ ...derivedTraces.filter((trace) => !nativeSourceIds.has(trace.sourceId)),
338
+ ].sort((a, b) => b.timestamp.localeCompare(a.timestamp))
339
+ }
340
+
341
+ function inferFailurePressurePriority(failure: Failure): 'high' | 'medium' | 'low' {
342
+ if (failure.correctionType === 'manual_edit' || failure.correctionType === 'mode_switch') return 'high'
343
+ if (failure.skillsActive.length === 0) return 'high'
344
+ if (failure.correctionType === 'retry') return 'medium'
345
+ return 'low'
346
+ }
347
+
348
+ function inferFailurePressureSeverity(failure: Failure): number {
349
+ const base = failure.correctionType === 'manual_edit' ? 0.95
350
+ : failure.correctionType === 'mode_switch' ? 0.9
351
+ : failure.correctionType === 'retry' ? 0.72
352
+ : 0.65
353
+ return failure.skillsActive.length === 0 ? Math.min(1, base + 0.08) : base
354
+ }
355
+
356
+ function buildDerivedPressureSignalFromFailure(failure: Failure): PressureSignal {
357
+ return {
358
+ id: `pressure_derived_${failure.id}`,
359
+ kind: `correction:${failure.correctionType}`,
360
+ provenance: 'failure-derived',
361
+ sourceType: 'failure',
362
+ sourceId: failure.id,
363
+ projectId: failure.project ?? undefined,
364
+ detectedAt: failure.timestamp,
365
+ severity: inferFailurePressureSeverity(failure),
366
+ priority: inferFailurePressurePriority(failure),
367
+ description: failure.correction,
368
+ relatedFailureId: failure.id,
369
+ relatedActivationTraceId: failure.activationTraceId,
370
+ skillIds: failure.skillsActive,
371
+ status: failure.resolved ? 'addressed' : 'open',
372
+ }
373
+ }
374
+
375
+ function buildDerivedProfileGapPressureSignals(profile: ProjectProfile): PressureSignal[] {
376
+ return profile.gaps.map((gap, index) => ({
377
+ id: `pressure_profile_${profile.name}_${index}`,
378
+ kind: `gap:${gap.area}`,
379
+ provenance: 'profile-gap-derived',
380
+ sourceType: 'project-analysis',
381
+ sourceId: profile.path,
382
+ projectId: profile.name,
383
+ projectPath: profile.path,
384
+ detectedAt: profile.analyzedAt,
385
+ severity: gap.priority === 'high' ? 0.95 : gap.priority === 'medium' ? 0.75 : 0.55,
386
+ priority: gap.priority,
387
+ capability: gap.area,
388
+ description: gap.description,
389
+ suggestedAction: gap.suggestedAction,
390
+ status: 'open',
391
+ }))
392
+ }
393
+
394
+ export function loadPressureSignals(): PressureSignal[] {
395
+ const nativeSignals = readJsonl<PressureSignal>('pressure-signals.jsonl')
396
+ const failures = loadFailures()
397
+ const profiles = loadAllProjectProfiles()
398
+ const derivedFromFailures = failures.map(buildDerivedPressureSignalFromFailure)
399
+ const derivedFromProfiles = profiles.flatMap(buildDerivedProfileGapPressureSignals)
400
+ const nativeKeys = new Set(nativeSignals.map((signal) => `${signal.sourceId}:${signal.kind}`))
401
+ return [
402
+ ...nativeSignals,
403
+ ...derivedFromFailures.filter((signal) => !nativeKeys.has(`${signal.sourceId}:${signal.kind}`)),
404
+ ...derivedFromProfiles.filter((signal) => !nativeKeys.has(`${signal.sourceId}:${signal.kind}`)),
405
+ ].sort((a, b) => b.detectedAt.localeCompare(a.detectedAt))
406
+ }
407
+
408
+ function projectRoleForSkill(skillId: string, graph: SkillGraph): CognitiveRole {
409
+ const node = graph.nodes.find((entry) => entry.id === skillId)
410
+ if (!node) return 'hybrid'
411
+ return inferCognitiveRole(node)
412
+ }
413
+
414
+ export function loadCoEvolutionSummary(): CoEvolutionDashboardSummary {
415
+ const graph = loadGraph()
416
+ const pressureSignals = loadPressureSignals()
417
+ const rolePressure = emptyCounts(['generalist', 'specialist', 'hybrid'] as const)
418
+ const projectBuckets = new Map<string, ProjectPressureSummary>()
419
+ const areaProjects = new Map<string, Set<string>>()
420
+ const recommendationCounts = new Map<'research' | 'specialize' | 'create', number>()
421
+
422
+ for (const signal of pressureSignals) {
423
+ for (const skillId of signal.skillIds ?? []) {
424
+ rolePressure[projectRoleForSkill(skillId, graph)] += 1
425
+ }
426
+
427
+ if (signal.projectId) {
428
+ const existing = projectBuckets.get(signal.projectId) ?? {
429
+ projectId: signal.projectId,
430
+ totalSignals: 0,
431
+ nativeSignals: 0,
432
+ derivedSignals: 0,
433
+ highPrioritySignals: 0,
434
+ topCapabilities: [],
435
+ }
436
+ existing.totalSignals += 1
437
+ if (signal.provenance === 'failure-native' || signal.provenance === 'project-analysis-native') existing.nativeSignals += 1
438
+ else existing.derivedSignals += 1
439
+ if (signal.priority === 'high') existing.highPrioritySignals += 1
440
+ if (signal.capability && !existing.topCapabilities.includes(signal.capability) && existing.topCapabilities.length < 3) {
441
+ existing.topCapabilities.push(signal.capability)
442
+ }
443
+ projectBuckets.set(signal.projectId, existing)
444
+ }
445
+
446
+ if (signal.capability && signal.projectId) {
447
+ const projects = areaProjects.get(signal.capability) ?? new Set<string>()
448
+ projects.add(signal.projectId)
449
+ areaProjects.set(signal.capability, projects)
450
+ }
451
+
452
+ if (signal.suggestedAction) {
453
+ recommendationCounts.set(signal.suggestedAction, (recommendationCounts.get(signal.suggestedAction) ?? 0) + 1)
454
+ }
455
+ }
456
+
457
+ const topProjects = [...projectBuckets.values()]
458
+ .sort((a, b) => b.totalSignals - a.totalSignals || b.highPrioritySignals - a.highPrioritySignals)
459
+ .slice(0, 6)
460
+
461
+ const crossProjectGapAreas = [...areaProjects.entries()]
462
+ .filter(([, projects]) => projects.size >= 2)
463
+ .map(([area, projects]) => ({ area, projects: [...projects], count: projects.size }))
464
+ .sort((a, b) => b.count - a.count || a.area.localeCompare(b.area))
465
+ .slice(0, 6)
466
+
467
+ const activeRecommendations = [...recommendationCounts.entries()]
468
+ .map(([action, count]) => ({ action, count }))
469
+ .sort((a, b) => b.count - a.count)
470
+
471
+ return {
472
+ pressureSignals: {
473
+ total: pressureSignals.length,
474
+ native: pressureSignals.filter((signal) => signal.provenance === 'failure-native' || signal.provenance === 'project-analysis-native').length,
475
+ derived: pressureSignals.filter((signal) => signal.provenance === 'failure-derived' || signal.provenance === 'profile-gap-derived').length,
476
+ },
477
+ topProjects,
478
+ rolePressure,
479
+ crossProjectGapAreas,
480
+ activeRecommendations,
481
+ }
482
+ }
483
+
112
484
  export function listProjects(): string[] {
113
485
  const failures = loadFailures()
114
486
  const projects = [...new Set(failures.map(f => f.project).filter(Boolean))] as string[]
@@ -176,3 +548,73 @@ export function getDashboardSummary() {
176
548
  skillTests: skillTests.length,
177
549
  }
178
550
  }
551
+
552
+ export function loadOntologySummary(): OntologyDashboardSummary {
553
+ const graph = loadGraph()
554
+ const history = loadHistory()
555
+ const failures = loadFailures()
556
+ const artifacts = loadEvolutionArtifacts()
557
+ const activationTraces = loadActivationTraces()
558
+ const pressureSignals = loadPressureSignals()
559
+ const skillRoles = emptyCounts(['generalist', 'specialist', 'hybrid'] as const)
560
+ const stabilityStates = emptyCounts(['stable', 'adaptive', 'experimental'] as const)
561
+ const plasticityStates = emptyCounts(['consolidated', 'volatile', 'candidate'] as const)
562
+
563
+ for (const node of graph.nodes) {
564
+ skillRoles[inferCognitiveRole(node)] += 1
565
+ stabilityStates[inferStabilityState(node)] += 1
566
+ plasticityStates[inferPlasticityState(node)] += 1
567
+ }
568
+
569
+ const proposals = history.iterations.flatMap((iteration) => iteration.proposals)
570
+ const observedMutationActions = [...new Set(proposals.map((proposal) => proposal.action))].sort()
571
+ const evidenceBackedProposals = proposals.filter((proposal) => {
572
+ const judges = proposal.judges
573
+ const hasJudgeScores = [judges.taskCompletion, judges.correctionAlignment, judges.sideEffectCheck]
574
+ .every((judge) => Number.isFinite(judge.score))
575
+ return hasJudgeScores && proposal.regressionResult.total > 0
576
+ }).length
577
+
578
+ return {
579
+ specVersion: graph.ontologyVersion ?? DASHBOARD_ONTOLOGY_SPEC_VERSION,
580
+ source: graph.ontologyVersion ? 'graph' : 'compat-derived',
581
+ skillRoles,
582
+ stabilityStates,
583
+ plasticityStates,
584
+ enrichedSkillNodes: graph.nodes.filter((node) => Boolean(
585
+ node.cognitiveRole
586
+ || node.stabilityState
587
+ || node.plasticityState
588
+ || node.capabilities?.length
589
+ || node.lineageId,
590
+ )).length,
591
+ relationFamiliesObserved: new Set(graph.edges.map((edge) => edge.type)).size,
592
+ mutationOperationsObserved: observedMutationActions.length,
593
+ observedMutationActions,
594
+ evidenceBackedProposals,
595
+ artifacts: {
596
+ total: artifacts.length,
597
+ native: artifacts.filter((artifact) => artifact.provenance === 'native-evolution').length,
598
+ derived: artifacts.filter((artifact) => artifact.provenance === 'derived-history').length,
599
+ },
600
+ activationTraces: {
601
+ total: activationTraces.length,
602
+ native: activationTraces.filter((trace) => trace.provenance !== 'derived-failure').length,
603
+ derived: activationTraces.filter((trace) => trace.provenance === 'derived-failure').length,
604
+ },
605
+ pressureSignals: {
606
+ total: pressureSignals.length,
607
+ native: pressureSignals.filter((signal) => signal.provenance === 'failure-native' || signal.provenance === 'project-analysis-native').length,
608
+ derived: pressureSignals.filter((signal) => signal.provenance === 'failure-derived' || signal.provenance === 'profile-gap-derived').length,
609
+ },
610
+ annotatedFailures: {
611
+ pressureSignals: failures.filter((failure) => (failure.pressureSignals?.length ?? 0) > 0).length,
612
+ capabilityGaps: failures.filter((failure) => (failure.capabilityGaps?.length ?? 0) > 0).length,
613
+ activationTraces: failures.filter((failure) => Boolean(failure.activationTraceId)).length,
614
+ },
615
+ }
616
+ }
617
+
618
+ export function getOntologyDashboardSummary(): OntologyDashboardSummary {
619
+ return loadOntologySummary()
620
+ }