@evomap/evolver-core 2.0.0-beta.8 → 2.0.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/algo/candidateAssembly.d.ts +7 -2
- package/dist/algo/candidateAssembly.js +81 -13
- package/dist/algo/capabilityCandidates.js +10 -0
- package/dist/algo/conversationSniffer.js +20 -7
- package/dist/algo/cycleEngine.d.ts +23 -2
- package/dist/algo/cycleEngine.js +236 -36
- package/dist/algo/exploration.d.ts +7 -0
- package/dist/algo/exploration.js +16 -3
- package/dist/algo/geneHealth.d.ts +2 -2
- package/dist/algo/geneHealth.js +5 -4
- package/dist/algo/geneSelection.d.ts +63 -5
- package/dist/algo/geneSelection.js +330 -30
- package/dist/algo/index.d.ts +2 -0
- package/dist/algo/index.js +2 -0
- package/dist/algo/orchestrator.d.ts +8 -1
- package/dist/algo/orchestrator.js +15 -3
- package/dist/algo/publishEligibility.d.ts +34 -0
- package/dist/algo/publishEligibility.js +52 -0
- package/dist/algo/ucb1.d.ts +53 -0
- package/dist/algo/ucb1.js +156 -0
- package/dist/assetstore/assetSidecarRecords.d.ts +7 -2
- package/dist/assetstore/assetSidecarRecords.js +238 -0
- package/dist/assetstore/assetSidecarRecovery.js +2 -2
- package/dist/assetstore/assetStoreHealth.d.ts +6 -0
- package/dist/assetstore/assetStoreHealth.js +56 -14
- package/dist/assetstore/assetStoreStorage.d.ts +1 -1
- package/dist/assetstore/assetStoreStorage.js +27 -9
- package/dist/assetstore/assetSyncLedger.d.ts +86 -0
- package/dist/assetstore/assetSyncLedger.js +683 -4
- package/dist/assetstore/index.d.ts +1 -0
- package/dist/assetstore/index.js +1 -0
- package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
- package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
- package/dist/assetstore/localJsonl.d.ts +4 -2
- package/dist/assetstore/localJsonl.js +91 -11
- package/dist/assetstore/provenance.d.ts +80 -4
- package/dist/assetstore/provenance.js +313 -5
- package/dist/assetstore/provider.d.ts +58 -1
- package/dist/assetstore/provider.js +97 -6
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +4 -3
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +4 -3
- package/dist/benchmark/index.d.ts +2 -1
- package/dist/benchmark/index.js +2 -1
- package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
- package/dist/benchmark/selectionFlatAbstention.js +481 -0
- package/dist/bootstrap/envFingerprint.d.ts +9 -0
- package/dist/bootstrap/envFingerprint.js +5 -0
- package/dist/bootstrap/index.d.ts +2 -1
- package/dist/bootstrap/index.js +2 -1
- package/dist/bootstrap/v1EnvCompat.d.ts +111 -0
- package/dist/bootstrap/v1EnvCompat.js +280 -0
- package/dist/events/eventArchive.d.ts +2 -0
- package/dist/events/eventArchive.js +13 -3
- package/dist/events/eventSchema.d.ts +7 -7
- package/dist/events/eventStore.d.ts +2 -0
- package/dist/events/eventStore.js +5 -1
- package/dist/events/ingest.d.ts +1 -0
- package/dist/events/ingest.js +1 -0
- package/dist/events/paths.d.ts +3 -1
- package/dist/events/paths.js +4 -0
- package/dist/events/public.d.ts +2 -2
- package/dist/events/public.js +2 -2
- package/dist/events/reports.d.ts +2 -0
- package/dist/events/reports.js +4 -0
- package/dist/exec/autoExec.d.ts +43 -2
- package/dist/exec/autoExec.js +76 -9
- package/dist/exec/autonomousCycle.d.ts +22 -4
- package/dist/exec/autonomousCycle.js +64 -13
- package/dist/exec/claudeBridge.d.ts +32 -7
- package/dist/exec/claudeBridge.js +281 -29
- package/dist/exec/prompt.js +5 -1
- package/dist/exec/runnerRegistry.d.ts +68 -26
- package/dist/exec/runnerRegistry.js +307 -72
- package/dist/exec/selfPr.js +1 -7
- package/dist/feedback/envelope.d.ts +61 -0
- package/dist/feedback/envelope.js +168 -0
- package/dist/feedback/index.d.ts +1 -0
- package/dist/feedback/index.js +1 -0
- package/dist/hooks/hooks.js +1 -0
- package/dist/hub/assetCallLog.d.ts +35 -1
- package/dist/hub/assetCallLog.js +124 -1
- package/dist/hub/bindings.d.ts +8 -1
- package/dist/hub/bindings.js +17 -6
- package/dist/hub/capability.d.ts +90 -3
- package/dist/hub/fake.d.ts +2 -2
- package/dist/hub/fake.js +1 -1
- package/dist/hub/questionGenerator.d.ts +5 -1
- package/dist/hub/questionGenerator.js +8 -6
- package/dist/hub/sanitize.js +3 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -1
- package/dist/mailbox/dispatch.d.ts +1 -1
- package/dist/mailbox/dispatch.js +22 -6
- package/dist/mailbox/envelope.d.ts +7 -1
- package/dist/mailbox/envelope.js +9 -2
- package/dist/mailbox/ipcServer.d.ts +10 -2
- package/dist/mailbox/ipcServer.js +163 -13
- package/dist/mailbox/store.d.ts +89 -3
- package/dist/mailbox/store.js +895 -41
- package/dist/ops/evolutionGraphProjection.d.ts +20 -0
- package/dist/ops/evolutionGraphProjection.js +315 -0
- package/dist/ops/index.d.ts +2 -1
- package/dist/ops/index.js +2 -1
- package/dist/ops/selfUpdate.d.ts +8 -0
- package/dist/ops/selfUpdate.js +24 -8
- package/dist/personality/schema.d.ts +16 -16
- package/dist/schema/evolutionGraph.d.ts +784 -0
- package/dist/schema/evolutionGraph.js +187 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/signal.d.ts +3 -3
- package/dist/schema/signal.js +1 -1
- package/dist/signals/curriculum.d.ts +55 -0
- package/dist/signals/curriculum.js +202 -0
- package/dist/signals/cycleHistoryFromEvents.js +17 -8
- package/dist/signals/expand.d.ts +15 -1
- package/dist/signals/expand.js +169 -1
- package/dist/signals/extractor.d.ts +2 -2
- package/dist/signals/extractor.js +31 -6
- package/dist/signals/index.d.ts +3 -1
- package/dist/signals/index.js +3 -1
- package/dist/signals/metaSignals.d.ts +4 -0
- package/dist/signals/metaSignals.js +42 -0
- package/dist/signals/signalGate.js +1 -1
- package/dist/signals/taskDomain.d.ts +22 -0
- package/dist/signals/taskDomain.js +43 -0
- package/dist/strategy/constraintAblation.d.ts +64 -0
- package/dist/strategy/constraintAblation.js +2820 -0
- package/dist/strategy/constraintAblationPredicates.d.ts +31 -0
- package/dist/strategy/constraintAblationPredicates.js +339 -0
- package/dist/strategy/index.d.ts +2 -1
- package/dist/strategy/index.js +2 -1
- package/dist/trace/index.d.ts +3 -1
- package/dist/trace/index.js +3 -1
- package/dist/trace/learningTrace.d.ts +209 -0
- package/dist/trace/learningTrace.js +276 -0
- package/dist/trace/proxyTurns.d.ts +31 -0
- package/dist/trace/proxyTurns.js +137 -0
- package/dist/verify/sandboxRunner.d.ts +28 -0
- package/dist/verify/sandboxRunner.js +218 -19
- package/dist/verify/sandboxedValidation.d.ts +9 -0
- package/dist/verify/sandboxedValidation.js +113 -13
- package/dist/verify/validation.d.ts +11 -1
- package/dist/verify/validation.js +31 -0
- package/package.json +5 -2
|
@@ -0,0 +1,187 @@
|
|
|
1
|
+
import { z } from 'zod';
|
|
2
|
+
import { extensions, sha256Hash } from './common.js';
|
|
3
|
+
export const EVOLUTION_GRAPH_SCHEMA_VERSION = '1.0.0';
|
|
4
|
+
export const evolutionGraphNodeKinds = [
|
|
5
|
+
'prompt',
|
|
6
|
+
'code',
|
|
7
|
+
'spec',
|
|
8
|
+
'test',
|
|
9
|
+
'feedback',
|
|
10
|
+
'run',
|
|
11
|
+
'metric',
|
|
12
|
+
'variant',
|
|
13
|
+
'selection',
|
|
14
|
+
];
|
|
15
|
+
export const evolutionGraphEdgeKinds = [
|
|
16
|
+
'derivation',
|
|
17
|
+
'evaluated_by',
|
|
18
|
+
'regressed',
|
|
19
|
+
'fixed',
|
|
20
|
+
'selected',
|
|
21
|
+
];
|
|
22
|
+
export const evolutionGraphProvenanceKinds = [
|
|
23
|
+
'root_event',
|
|
24
|
+
'asset',
|
|
25
|
+
'material',
|
|
26
|
+
'review',
|
|
27
|
+
'cycle',
|
|
28
|
+
'workflow',
|
|
29
|
+
'runtime_trace',
|
|
30
|
+
'plugin',
|
|
31
|
+
'manual',
|
|
32
|
+
];
|
|
33
|
+
export const evolutionGraphNodeKind = z.enum(evolutionGraphNodeKinds);
|
|
34
|
+
export const evolutionGraphEdgeKind = z.enum(evolutionGraphEdgeKinds);
|
|
35
|
+
export const evolutionGraphProvenanceKind = z.enum(evolutionGraphProvenanceKinds);
|
|
36
|
+
export const evolutionGraphNodeRef = z.string().min(1);
|
|
37
|
+
export const evolutionGraphArtifactRef = z.object({
|
|
38
|
+
kind: z.enum(['asset', 'material', 'root_event', 'file', 'runtime_trace', 'plugin_resource']),
|
|
39
|
+
ref: z.string().min(1),
|
|
40
|
+
sha256: sha256Hash.optional(),
|
|
41
|
+
});
|
|
42
|
+
export const evolutionGraphMetricValue = z.object({
|
|
43
|
+
metric: z.string().min(1),
|
|
44
|
+
value: z.number(),
|
|
45
|
+
unit: z.string().min(1).optional(),
|
|
46
|
+
direction: z.enum(['higher_is_better', 'lower_is_better', 'target']).default('higher_is_better'),
|
|
47
|
+
target: z.number().optional(),
|
|
48
|
+
});
|
|
49
|
+
export const evolutionGraphMetricDelta = z.object({
|
|
50
|
+
metric: z.string().min(1),
|
|
51
|
+
before: z.number().optional(),
|
|
52
|
+
after: z.number(),
|
|
53
|
+
delta: z.number().optional(),
|
|
54
|
+
direction: z.enum(['improved', 'regressed', 'unchanged', 'unknown']).default('unknown'),
|
|
55
|
+
});
|
|
56
|
+
export const evolutionGraphNode = z.object({
|
|
57
|
+
id: evolutionGraphNodeRef,
|
|
58
|
+
kind: evolutionGraphNodeKind,
|
|
59
|
+
label: z.string().min(1),
|
|
60
|
+
createdAt: z.string().datetime(),
|
|
61
|
+
artifact: evolutionGraphArtifactRef.optional(),
|
|
62
|
+
metrics: z.array(evolutionGraphMetricValue).default([]),
|
|
63
|
+
summary: z.string().optional(),
|
|
64
|
+
tags: z.array(z.string().min(1)).default([]),
|
|
65
|
+
extensions,
|
|
66
|
+
});
|
|
67
|
+
export const evolutionGraphProvenance = z.object({
|
|
68
|
+
kind: evolutionGraphProvenanceKind,
|
|
69
|
+
ref: z.string().min(1),
|
|
70
|
+
capturedAt: z.string().datetime(),
|
|
71
|
+
actor: z.string().min(1).optional(),
|
|
72
|
+
digest: sha256Hash.optional(),
|
|
73
|
+
summary: z.string().optional(),
|
|
74
|
+
});
|
|
75
|
+
export const evolutionGraphEdge = z.object({
|
|
76
|
+
id: z.string().min(1),
|
|
77
|
+
kind: evolutionGraphEdgeKind,
|
|
78
|
+
from: evolutionGraphNodeRef,
|
|
79
|
+
to: evolutionGraphNodeRef,
|
|
80
|
+
createdAt: z.string().datetime(),
|
|
81
|
+
provenance: z.array(evolutionGraphProvenance).min(1),
|
|
82
|
+
metricDelta: evolutionGraphMetricDelta.optional(),
|
|
83
|
+
reason: z.string().min(1).optional(),
|
|
84
|
+
confidence: z.number().min(0).max(1).optional(),
|
|
85
|
+
extensions,
|
|
86
|
+
});
|
|
87
|
+
export const evolutionGraphDashboardSummary = z.object({
|
|
88
|
+
nodeCount: z.number().int().nonnegative(),
|
|
89
|
+
edgeCount: z.number().int().nonnegative(),
|
|
90
|
+
variantCount: z.number().int().nonnegative(),
|
|
91
|
+
selectedCount: z.number().int().nonnegative(),
|
|
92
|
+
regressionCount: z.number().int().nonnegative(),
|
|
93
|
+
fixedCount: z.number().int().nonnegative(),
|
|
94
|
+
evaluatedVariantCount: z.number().int().nonnegative(),
|
|
95
|
+
unevaluatedVariantCount: z.number().int().nonnegative(),
|
|
96
|
+
orphanEdgeCount: z.number().int().nonnegative(),
|
|
97
|
+
provenanceGapCount: z.number().int().nonnegative(),
|
|
98
|
+
});
|
|
99
|
+
export const evolutionGraph = z.object({
|
|
100
|
+
schemaVersion: z.literal(EVOLUTION_GRAPH_SCHEMA_VERSION).default(EVOLUTION_GRAPH_SCHEMA_VERSION),
|
|
101
|
+
graphId: z.string().min(1),
|
|
102
|
+
generatedAt: z.string().datetime(),
|
|
103
|
+
nodes: z.array(evolutionGraphNode),
|
|
104
|
+
edges: z.array(evolutionGraphEdge),
|
|
105
|
+
dashboard: evolutionGraphDashboardSummary.optional(),
|
|
106
|
+
extensions,
|
|
107
|
+
}).superRefine((graph, ctx) => {
|
|
108
|
+
const nodesById = new Map();
|
|
109
|
+
for (const [index, node] of graph.nodes.entries()) {
|
|
110
|
+
if (nodesById.has(node.id)) {
|
|
111
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['nodes', index, 'id'], message: 'duplicate evolution graph node id' });
|
|
112
|
+
continue;
|
|
113
|
+
}
|
|
114
|
+
nodesById.set(node.id, node.kind);
|
|
115
|
+
}
|
|
116
|
+
const edgeIds = new Set();
|
|
117
|
+
for (const [index, edge] of graph.edges.entries()) {
|
|
118
|
+
if (edgeIds.has(edge.id)) {
|
|
119
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['edges', index, 'id'], message: 'duplicate evolution graph edge id' });
|
|
120
|
+
}
|
|
121
|
+
else {
|
|
122
|
+
edgeIds.add(edge.id);
|
|
123
|
+
}
|
|
124
|
+
const fromKind = nodesById.get(edge.from);
|
|
125
|
+
const toKind = nodesById.get(edge.to);
|
|
126
|
+
if (!fromKind)
|
|
127
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['edges', index, 'from'], message: 'edge source node is missing' });
|
|
128
|
+
if (!toKind)
|
|
129
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['edges', index, 'to'], message: 'edge target node is missing' });
|
|
130
|
+
if (!fromKind || !toKind)
|
|
131
|
+
continue;
|
|
132
|
+
validateEvolutionGraphEdgeShape(edge, fromKind, toKind, ctx, index);
|
|
133
|
+
}
|
|
134
|
+
});
|
|
135
|
+
export function deriveEvolutionGraphDashboardSummary(input) {
|
|
136
|
+
const variantIds = new Set(input.nodes.filter((node) => node.kind === 'variant').map((node) => node.id));
|
|
137
|
+
const evaluatedVariantIds = new Set();
|
|
138
|
+
let selectedCount = 0;
|
|
139
|
+
let regressionCount = 0;
|
|
140
|
+
let fixedCount = 0;
|
|
141
|
+
let orphanEdgeCount = 0;
|
|
142
|
+
let provenanceGapCount = 0;
|
|
143
|
+
const nodeIds = new Set(input.nodes.map((node) => node.id));
|
|
144
|
+
for (const edge of input.edges) {
|
|
145
|
+
if (!nodeIds.has(edge.from) || !nodeIds.has(edge.to))
|
|
146
|
+
orphanEdgeCount += 1;
|
|
147
|
+
if (edge.provenance.length === 0)
|
|
148
|
+
provenanceGapCount += 1;
|
|
149
|
+
if (edge.kind === 'evaluated_by' && variantIds.has(edge.from))
|
|
150
|
+
evaluatedVariantIds.add(edge.from);
|
|
151
|
+
if (edge.kind === 'selected')
|
|
152
|
+
selectedCount += 1;
|
|
153
|
+
if (edge.kind === 'regressed')
|
|
154
|
+
regressionCount += 1;
|
|
155
|
+
if (edge.kind === 'fixed')
|
|
156
|
+
fixedCount += 1;
|
|
157
|
+
}
|
|
158
|
+
return {
|
|
159
|
+
nodeCount: input.nodes.length,
|
|
160
|
+
edgeCount: input.edges.length,
|
|
161
|
+
variantCount: variantIds.size,
|
|
162
|
+
selectedCount,
|
|
163
|
+
regressionCount,
|
|
164
|
+
fixedCount,
|
|
165
|
+
evaluatedVariantCount: evaluatedVariantIds.size,
|
|
166
|
+
unevaluatedVariantCount: Math.max(0, variantIds.size - evaluatedVariantIds.size),
|
|
167
|
+
orphanEdgeCount,
|
|
168
|
+
provenanceGapCount,
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
function validateEvolutionGraphEdgeShape(edge, fromKind, toKind, ctx, edgeIndex) {
|
|
172
|
+
if (edge.kind === 'selected' && (fromKind !== 'variant' || toKind !== 'selection')) {
|
|
173
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['edges', edgeIndex, 'kind'], message: 'selected edges must connect variant -> selection' });
|
|
174
|
+
}
|
|
175
|
+
if (edge.kind === 'evaluated_by' && !['run', 'metric', 'test'].includes(toKind)) {
|
|
176
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['edges', edgeIndex, 'to'], message: 'evaluated_by target must be run, metric, or test' });
|
|
177
|
+
}
|
|
178
|
+
if ((edge.kind === 'regressed' || edge.kind === 'fixed') && !['run', 'metric', 'test'].includes(toKind)) {
|
|
179
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['edges', edgeIndex, 'to'], message: `${edge.kind} target must be run, metric, or test` });
|
|
180
|
+
}
|
|
181
|
+
if ((edge.kind === 'regressed' || edge.kind === 'fixed') && !edge.metricDelta) {
|
|
182
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['edges', edgeIndex, 'metricDelta'], message: `${edge.kind} edges require metricDelta` });
|
|
183
|
+
}
|
|
184
|
+
if (edge.kind === 'selected' && !edge.reason) {
|
|
185
|
+
ctx.addIssue({ code: z.ZodIssueCode.custom, path: ['edges', edgeIndex, 'reason'], message: 'selected edges require a reason' });
|
|
186
|
+
}
|
|
187
|
+
}
|
package/dist/schema/index.d.ts
CHANGED
package/dist/schema/index.js
CHANGED
package/dist/schema/signal.d.ts
CHANGED
|
@@ -7,7 +7,7 @@ export declare const problemSignature: z.ZodString;
|
|
|
7
7
|
export declare const signal: z.ZodObject<{
|
|
8
8
|
signalId: z.ZodString;
|
|
9
9
|
fromMaterial: z.ZodDefault<z.ZodArray<z.ZodString, "many">>;
|
|
10
|
-
kind: z.ZodEnum<["strong_structured", "weak_corpus", "agent_marked"]>;
|
|
10
|
+
kind: z.ZodEnum<["strong_structured", "weak_corpus", "agent_marked", "verified_success"]>;
|
|
11
11
|
text: z.ZodString;
|
|
12
12
|
score: z.ZodDefault<z.ZodNumber>;
|
|
13
13
|
eventSignature: z.ZodString;
|
|
@@ -16,7 +16,7 @@ export declare const signal: z.ZodObject<{
|
|
|
16
16
|
discoveredAt: z.ZodString;
|
|
17
17
|
extensions: z.ZodDefault<z.ZodRecord<z.ZodString, z.ZodUnknown>>;
|
|
18
18
|
}, "strip", z.ZodTypeAny, {
|
|
19
|
-
kind: "strong_structured" | "weak_corpus" | "agent_marked";
|
|
19
|
+
kind: "strong_structured" | "weak_corpus" | "agent_marked" | "verified_success";
|
|
20
20
|
extensions: Record<string, unknown>;
|
|
21
21
|
signalId: string;
|
|
22
22
|
fromMaterial: string[];
|
|
@@ -27,7 +27,7 @@ export declare const signal: z.ZodObject<{
|
|
|
27
27
|
signatureV: number;
|
|
28
28
|
discoveredAt: string;
|
|
29
29
|
}, {
|
|
30
|
-
kind: "strong_structured" | "weak_corpus" | "agent_marked";
|
|
30
|
+
kind: "strong_structured" | "weak_corpus" | "agent_marked" | "verified_success";
|
|
31
31
|
signalId: string;
|
|
32
32
|
text: string;
|
|
33
33
|
eventSignature: string;
|
package/dist/schema/signal.js
CHANGED
|
@@ -8,7 +8,7 @@ export const problemSignature = z.string();
|
|
|
8
8
|
export const signal = z.object({
|
|
9
9
|
signalId: ulid,
|
|
10
10
|
fromMaterial: z.array(ulid).default([]),
|
|
11
|
-
kind: z.enum(['strong_structured', 'weak_corpus', 'agent_marked']),
|
|
11
|
+
kind: z.enum(['strong_structured', 'weak_corpus', 'agent_marked', 'verified_success']),
|
|
12
12
|
text: z.string(),
|
|
13
13
|
score: z.number().min(0).max(1).default(0),
|
|
14
14
|
eventSignature,
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progressive curriculum signals, adapted from V1's curriculum producer.
|
|
3
|
+
*
|
|
4
|
+
* V2 keeps the policy pure and derives its history from the append-only root event log. This avoids V1's
|
|
5
|
+
* separate mutable curriculum_state.json sidecar while preserving the behavior that matters to selection:
|
|
6
|
+
* classify recent outcomes and add at most one capability-gap target plus one frontier target.
|
|
7
|
+
*/
|
|
8
|
+
export interface CurriculumOutcome {
|
|
9
|
+
key: string;
|
|
10
|
+
status: 'success' | 'failed';
|
|
11
|
+
}
|
|
12
|
+
export interface CurriculumBucket {
|
|
13
|
+
key: string;
|
|
14
|
+
success: number;
|
|
15
|
+
failed: number;
|
|
16
|
+
total: number;
|
|
17
|
+
rate: number;
|
|
18
|
+
}
|
|
19
|
+
export interface CurriculumClassification {
|
|
20
|
+
mastered: CurriculumBucket[];
|
|
21
|
+
failing: CurriculumBucket[];
|
|
22
|
+
frontier: CurriculumBucket[];
|
|
23
|
+
}
|
|
24
|
+
export interface CurriculumEventLike {
|
|
25
|
+
type: string;
|
|
26
|
+
payload?: unknown;
|
|
27
|
+
}
|
|
28
|
+
export interface GenerateCurriculumSignalsInput {
|
|
29
|
+
outcomes: readonly CurriculumOutcome[];
|
|
30
|
+
capabilityGaps?: readonly string[];
|
|
31
|
+
}
|
|
32
|
+
export declare const MAX_CAPABILITY_GAPS = 16;
|
|
33
|
+
export declare const CAPABILITY_GAPS_STATE_KEY = "curriculum:capability_gaps";
|
|
34
|
+
/** Normalize untrusted capability names before they cross adapter, persistence, or selection boundaries. */
|
|
35
|
+
export declare function normalizeCapabilityGaps(value: unknown): string[];
|
|
36
|
+
/** Versioned, bounded snapshot stored atomically in the lifecycle mailbox KV table. */
|
|
37
|
+
export declare function serializeCapabilityGapsState(capabilityGaps: unknown, observedAt: number): string;
|
|
38
|
+
/** Parse only the current bounded snapshot format. Unknown versions fail open without influencing selection. */
|
|
39
|
+
export declare function capabilityGapsFromState(value: unknown): string[];
|
|
40
|
+
/** Stable V2 equivalent of V1 computeSignalKey, excluding previously generated curriculum targets. */
|
|
41
|
+
export declare function curriculumSignalKey(signals: readonly string[]): string;
|
|
42
|
+
/** Classify signal-key outcomes using V1's thresholds. */
|
|
43
|
+
export declare function classifyCurriculumOutcomes(outcomes: readonly CurriculumOutcome[]): CurriculumClassification;
|
|
44
|
+
/** Generate no more than the two target families V1 produced: gap first, then closest frontier. */
|
|
45
|
+
export declare function generateCurriculumSignals(input: GenerateCurriculumSignalsInput): string[];
|
|
46
|
+
/**
|
|
47
|
+
* Map V2's capability signal conventions to concrete curriculum gaps. A bare cap:* tag is not sufficient by
|
|
48
|
+
* itself: it becomes a curriculum gap only when the same signal set explicitly declares capability_gap.
|
|
49
|
+
*/
|
|
50
|
+
export declare function capabilityGapsFromSignals(signals: readonly string[]): string[];
|
|
51
|
+
/**
|
|
52
|
+
* Join cycle.signals_collected to terminal cycle events and retain the latest outcome window. `baseSignals`
|
|
53
|
+
* wins over `signals`, so history-derived meta-signals do not become a self-reinforcing curriculum key.
|
|
54
|
+
*/
|
|
55
|
+
export declare function curriculumOutcomesFromEvents(events: readonly CurriculumEventLike[], maxOutcomes?: number): CurriculumOutcome[];
|
|
@@ -0,0 +1,202 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Progressive curriculum signals, adapted from V1's curriculum producer.
|
|
3
|
+
*
|
|
4
|
+
* V2 keeps the policy pure and derives its history from the append-only root event log. This avoids V1's
|
|
5
|
+
* separate mutable curriculum_state.json sidecar while preserving the behavior that matters to selection:
|
|
6
|
+
* classify recent outcomes and add at most one capability-gap target plus one frontier target.
|
|
7
|
+
*/
|
|
8
|
+
const MASTERY_THRESHOLD = 0.8;
|
|
9
|
+
const MASTERY_MIN_ATTEMPTS = 3;
|
|
10
|
+
const FAILURE_THRESHOLD = 0.3;
|
|
11
|
+
const MIN_CLASSIFICATION_ATTEMPTS = 2;
|
|
12
|
+
const MAX_CURRICULUM_SIGNALS = 2;
|
|
13
|
+
const MAX_TARGET_CHARS = 60;
|
|
14
|
+
const DEFAULT_OUTCOME_WINDOW = 200;
|
|
15
|
+
const CAPABILITY_NAME = /^[A-Za-z][A-Za-z0-9_-]{0,59}$/;
|
|
16
|
+
export const MAX_CAPABILITY_GAPS = 16;
|
|
17
|
+
export const CAPABILITY_GAPS_STATE_KEY = 'curriculum:capability_gaps';
|
|
18
|
+
const CAPABILITY_GAPS_STATE_VERSION = 1;
|
|
19
|
+
function record(value) {
|
|
20
|
+
return value && typeof value === 'object' && !Array.isArray(value)
|
|
21
|
+
? value
|
|
22
|
+
: undefined;
|
|
23
|
+
}
|
|
24
|
+
function stringArray(value) {
|
|
25
|
+
if (!Array.isArray(value))
|
|
26
|
+
return undefined;
|
|
27
|
+
return value.filter((entry) => typeof entry === 'string');
|
|
28
|
+
}
|
|
29
|
+
function boundedWindow(value) {
|
|
30
|
+
if (!Number.isFinite(value))
|
|
31
|
+
return DEFAULT_OUTCOME_WINDOW;
|
|
32
|
+
return Math.max(1, Math.min(10_000, Math.floor(value)));
|
|
33
|
+
}
|
|
34
|
+
/** Normalize untrusted capability names before they cross adapter, persistence, or selection boundaries. */
|
|
35
|
+
export function normalizeCapabilityGaps(value) {
|
|
36
|
+
if (!Array.isArray(value))
|
|
37
|
+
return [];
|
|
38
|
+
const gaps = [];
|
|
39
|
+
for (const entry of value) {
|
|
40
|
+
if (typeof entry !== 'string')
|
|
41
|
+
continue;
|
|
42
|
+
const normalized = entry.trim().toLowerCase();
|
|
43
|
+
if (!CAPABILITY_NAME.test(normalized) || gaps.includes(normalized))
|
|
44
|
+
continue;
|
|
45
|
+
gaps.push(normalized);
|
|
46
|
+
if (gaps.length >= MAX_CAPABILITY_GAPS)
|
|
47
|
+
break;
|
|
48
|
+
}
|
|
49
|
+
return gaps;
|
|
50
|
+
}
|
|
51
|
+
/** Versioned, bounded snapshot stored atomically in the lifecycle mailbox KV table. */
|
|
52
|
+
export function serializeCapabilityGapsState(capabilityGaps, observedAt) {
|
|
53
|
+
const safeObservedAt = Number.isFinite(observedAt) && observedAt >= 0 ? Math.floor(observedAt) : 0;
|
|
54
|
+
return JSON.stringify({
|
|
55
|
+
version: CAPABILITY_GAPS_STATE_VERSION,
|
|
56
|
+
capabilityGaps: normalizeCapabilityGaps(capabilityGaps),
|
|
57
|
+
observedAt: safeObservedAt,
|
|
58
|
+
});
|
|
59
|
+
}
|
|
60
|
+
/** Parse only the current bounded snapshot format. Unknown versions fail open without influencing selection. */
|
|
61
|
+
export function capabilityGapsFromState(value) {
|
|
62
|
+
if (typeof value !== 'string' || value.length === 0)
|
|
63
|
+
return [];
|
|
64
|
+
try {
|
|
65
|
+
const parsed = record(JSON.parse(value));
|
|
66
|
+
if (!parsed || parsed['version'] !== CAPABILITY_GAPS_STATE_VERSION)
|
|
67
|
+
return [];
|
|
68
|
+
const observedAt = parsed['observedAt'];
|
|
69
|
+
if (typeof observedAt !== 'number' || !Number.isFinite(observedAt) || observedAt < 0)
|
|
70
|
+
return [];
|
|
71
|
+
return normalizeCapabilityGaps(parsed['capabilityGaps']);
|
|
72
|
+
}
|
|
73
|
+
catch {
|
|
74
|
+
return [];
|
|
75
|
+
}
|
|
76
|
+
}
|
|
77
|
+
/** Stable V2 equivalent of V1 computeSignalKey, excluding previously generated curriculum targets. */
|
|
78
|
+
export function curriculumSignalKey(signals) {
|
|
79
|
+
return [...new Set(signals
|
|
80
|
+
.map((signal) => signal.trim())
|
|
81
|
+
.filter((signal) => signal.length > 0 && !signal.startsWith('curriculum_target:')))]
|
|
82
|
+
.sort()
|
|
83
|
+
.join('|');
|
|
84
|
+
}
|
|
85
|
+
/** Classify signal-key outcomes using V1's thresholds. */
|
|
86
|
+
export function classifyCurriculumOutcomes(outcomes) {
|
|
87
|
+
const aggregates = new Map();
|
|
88
|
+
for (const outcome of outcomes) {
|
|
89
|
+
const key = typeof outcome?.key === 'string' ? outcome.key.trim() : '';
|
|
90
|
+
if (!key || (outcome.status !== 'success' && outcome.status !== 'failed'))
|
|
91
|
+
continue;
|
|
92
|
+
const aggregate = aggregates.get(key) ?? { success: 0, failed: 0 };
|
|
93
|
+
if (outcome.status === 'success')
|
|
94
|
+
aggregate.success += 1;
|
|
95
|
+
else
|
|
96
|
+
aggregate.failed += 1;
|
|
97
|
+
aggregates.set(key, aggregate);
|
|
98
|
+
}
|
|
99
|
+
const mastered = [];
|
|
100
|
+
const failing = [];
|
|
101
|
+
const frontier = [];
|
|
102
|
+
for (const [key, aggregate] of aggregates) {
|
|
103
|
+
const total = aggregate.success + aggregate.failed;
|
|
104
|
+
if (total < MIN_CLASSIFICATION_ATTEMPTS)
|
|
105
|
+
continue;
|
|
106
|
+
const rate = aggregate.success / total;
|
|
107
|
+
const bucket = { key, success: aggregate.success, failed: aggregate.failed, total, rate };
|
|
108
|
+
if (rate >= MASTERY_THRESHOLD && total >= MASTERY_MIN_ATTEMPTS)
|
|
109
|
+
mastered.push(bucket);
|
|
110
|
+
else if (rate <= FAILURE_THRESHOLD)
|
|
111
|
+
failing.push(bucket);
|
|
112
|
+
else
|
|
113
|
+
frontier.push(bucket);
|
|
114
|
+
}
|
|
115
|
+
frontier.sort((left, right) => Math.abs(left.rate - 0.5) - Math.abs(right.rate - 0.5));
|
|
116
|
+
return { mastered, failing, frontier };
|
|
117
|
+
}
|
|
118
|
+
/** Generate no more than the two target families V1 produced: gap first, then closest frontier. */
|
|
119
|
+
export function generateCurriculumSignals(input) {
|
|
120
|
+
const analysis = classifyCurriculumOutcomes(input.outcomes);
|
|
121
|
+
const generated = [];
|
|
122
|
+
const gap = (input.capabilityGaps ?? []).find((entry) => typeof entry === 'string' && entry.trim().length > 0)?.trim();
|
|
123
|
+
if (gap) {
|
|
124
|
+
const normalizedGap = gap.toLowerCase();
|
|
125
|
+
const alreadyMastered = analysis.mastered.some((entry) => entry.key.toLowerCase().includes(normalizedGap));
|
|
126
|
+
if (!alreadyMastered)
|
|
127
|
+
generated.push(`curriculum_target:gap:${gap.slice(0, MAX_TARGET_CHARS)}`);
|
|
128
|
+
}
|
|
129
|
+
const best = analysis.frontier[0];
|
|
130
|
+
if (generated.length < MAX_CURRICULUM_SIGNALS && best) {
|
|
131
|
+
const alreadyTargeted = generated.some((signal) => signal.includes(best.key));
|
|
132
|
+
if (!alreadyTargeted)
|
|
133
|
+
generated.push(`curriculum_target:frontier:${best.key.slice(0, MAX_TARGET_CHARS)}`);
|
|
134
|
+
}
|
|
135
|
+
return generated.slice(0, MAX_CURRICULUM_SIGNALS);
|
|
136
|
+
}
|
|
137
|
+
/**
|
|
138
|
+
* Map V2's capability signal conventions to concrete curriculum gaps. A bare cap:* tag is not sufficient by
|
|
139
|
+
* itself: it becomes a curriculum gap only when the same signal set explicitly declares capability_gap.
|
|
140
|
+
*/
|
|
141
|
+
export function capabilityGapsFromSignals(signals) {
|
|
142
|
+
const hasGapMarker = signals.some((signal) => signal === 'capability_gap' || signal.startsWith('capability_gap:'));
|
|
143
|
+
if (!hasGapMarker)
|
|
144
|
+
return [];
|
|
145
|
+
const gaps = [];
|
|
146
|
+
const add = (raw) => {
|
|
147
|
+
const normalized = raw.trim().toLowerCase();
|
|
148
|
+
if (!CAPABILITY_NAME.test(normalized) || gaps.includes(normalized))
|
|
149
|
+
return;
|
|
150
|
+
gaps.push(normalized);
|
|
151
|
+
};
|
|
152
|
+
for (const signal of signals)
|
|
153
|
+
if (signal.startsWith('cap:'))
|
|
154
|
+
add(signal.slice('cap:'.length));
|
|
155
|
+
for (const signal of signals)
|
|
156
|
+
if (signal.startsWith('capability_gap:'))
|
|
157
|
+
add(signal.slice('capability_gap:'.length));
|
|
158
|
+
return normalizeCapabilityGaps(gaps);
|
|
159
|
+
}
|
|
160
|
+
/**
|
|
161
|
+
* Join cycle.signals_collected to terminal cycle events and retain the latest outcome window. `baseSignals`
|
|
162
|
+
* wins over `signals`, so history-derived meta-signals do not become a self-reinforcing curriculum key.
|
|
163
|
+
*/
|
|
164
|
+
export function curriculumOutcomesFromEvents(events, maxOutcomes = DEFAULT_OUTCOME_WINDOW) {
|
|
165
|
+
const limit = boundedWindow(maxOutcomes);
|
|
166
|
+
const selectedNewestFirst = [];
|
|
167
|
+
const terminalCycles = new Set();
|
|
168
|
+
// Pick the latest unique terminal cycles first. EventStore.readAll already owns the input array, while every
|
|
169
|
+
// auxiliary collection in this adapter remains bounded by the configured outcome window.
|
|
170
|
+
for (let index = events.length - 1; index >= 0 && selectedNewestFirst.length < limit; index -= 1) {
|
|
171
|
+
const event = events[index];
|
|
172
|
+
if (event.type !== 'cycle.solidified' && event.type !== 'cycle.failed')
|
|
173
|
+
continue;
|
|
174
|
+
const payload = record(event.payload);
|
|
175
|
+
if (!payload || typeof payload['cycleId'] !== 'string' || terminalCycles.has(payload['cycleId']))
|
|
176
|
+
continue;
|
|
177
|
+
const cycleId = payload['cycleId'];
|
|
178
|
+
terminalCycles.add(cycleId);
|
|
179
|
+
selectedNewestFirst.push({
|
|
180
|
+
cycleId,
|
|
181
|
+
status: event.type === 'cycle.failed' || payload['producedValue'] === false ? 'failed' : 'success',
|
|
182
|
+
});
|
|
183
|
+
}
|
|
184
|
+
const signalsByCycle = new Map();
|
|
185
|
+
// Build the signal lookup independently of terminal ordering. Normal logs collect signals first, but a
|
|
186
|
+
// recovered/imported history can be reordered without making otherwise valid outcomes disappear.
|
|
187
|
+
for (const event of events) {
|
|
188
|
+
const payload = record(event.payload);
|
|
189
|
+
if (!payload)
|
|
190
|
+
continue;
|
|
191
|
+
const cycleId = typeof payload['cycleId'] === 'string' ? payload['cycleId'] : undefined;
|
|
192
|
+
if (!cycleId || !terminalCycles.has(cycleId) || event.type !== 'cycle.signals_collected')
|
|
193
|
+
continue;
|
|
194
|
+
const baseSignals = stringArray(payload['baseSignals']);
|
|
195
|
+
const fallbackSignals = stringArray(payload['signals']);
|
|
196
|
+
signalsByCycle.set(cycleId, baseSignals ?? fallbackSignals ?? []);
|
|
197
|
+
}
|
|
198
|
+
return selectedNewestFirst.reverse().flatMap(({ cycleId, status }) => {
|
|
199
|
+
const key = curriculumSignalKey(signalsByCycle.get(cycleId) ?? []);
|
|
200
|
+
return key ? [{ key, status }] : [];
|
|
201
|
+
});
|
|
202
|
+
}
|
|
@@ -8,7 +8,8 @@
|
|
|
8
8
|
// - intent: mutation.built.payload.category (the cycle's GepCategory), if emitted
|
|
9
9
|
// - genesUsed: decision.gene_selected.payload.selectedGeneId || cycle.*.payload.gene
|
|
10
10
|
// - score: cycle.*.payload.outcome.score
|
|
11
|
-
// - blastRadius:capsule.produced.payload.blastRadius, if emitted
|
|
11
|
+
// - blastRadius:capsule.produced.payload.blastRadius, if emitted. Productive {0,0} is omitted because non-git
|
|
12
|
+
// proofs use it as "unknown"; measured/no-value {0,0} remains an empty-cycle marker.
|
|
12
13
|
// Only terminal cycles (a cycle.solidified or cycle.failed) become records; in-flight cycles are skipped.
|
|
13
14
|
function payloadObj(e) {
|
|
14
15
|
return e.payload && typeof e.payload === 'object' ? e.payload : {};
|
|
@@ -22,13 +23,12 @@ function cycleIdOf(p) {
|
|
|
22
23
|
*/
|
|
23
24
|
export function cycleRecordsFromEvents(events) {
|
|
24
25
|
const byCycle = new Map();
|
|
25
|
-
const
|
|
26
|
+
const terminalOrder = [];
|
|
26
27
|
const get = (id) => {
|
|
27
28
|
let a = byCycle.get(id);
|
|
28
29
|
if (!a) {
|
|
29
30
|
a = { terminal: false };
|
|
30
31
|
byCycle.set(id, a);
|
|
31
|
-
order.push(id);
|
|
32
32
|
}
|
|
33
33
|
return a;
|
|
34
34
|
};
|
|
@@ -53,15 +53,24 @@ export function cycleRecordsFromEvents(events) {
|
|
|
53
53
|
const br = p['blastRadius'];
|
|
54
54
|
if (br && typeof br === 'object') {
|
|
55
55
|
const o = br;
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
56
|
+
const files = typeof o['files'] === 'number' ? o['files'] : undefined;
|
|
57
|
+
const lines = typeof o['lines'] === 'number' ? o['lines'] : undefined;
|
|
58
|
+
// Non-git proofs use {0,0} as "blast unknown". producedValue is the authoritative distinction between
|
|
59
|
+
// those productive cycles and a measured zero-change git cycle.
|
|
60
|
+
const productiveUnknownBlast = p['producedValue'] === true && files === 0 && lines === 0;
|
|
61
|
+
if (!productiveUnknownBlast) {
|
|
62
|
+
a.blastRadius = {
|
|
63
|
+
...(files !== undefined ? { files } : {}),
|
|
64
|
+
...(lines !== undefined ? { lines } : {}),
|
|
65
|
+
};
|
|
66
|
+
}
|
|
60
67
|
}
|
|
61
68
|
break;
|
|
62
69
|
}
|
|
63
70
|
case 'cycle.solidified':
|
|
64
71
|
case 'cycle.failed': {
|
|
72
|
+
if (!a.terminal)
|
|
73
|
+
terminalOrder.push(id);
|
|
65
74
|
a.terminal = true;
|
|
66
75
|
a.status = e.type === 'cycle.solidified' ? 'success' : 'failed';
|
|
67
76
|
if (typeof p['gene'] === 'string')
|
|
@@ -77,7 +86,7 @@ export function cycleRecordsFromEvents(events) {
|
|
|
77
86
|
}
|
|
78
87
|
}
|
|
79
88
|
const records = [];
|
|
80
|
-
for (const id of
|
|
89
|
+
for (const id of terminalOrder) {
|
|
81
90
|
const a = byCycle.get(id);
|
|
82
91
|
if (!a.terminal)
|
|
83
92
|
continue; // skip in-flight cycles
|
package/dist/signals/expand.d.ts
CHANGED
|
@@ -31,4 +31,18 @@ export declare function geneTags(gene: GeneTagInput): string[];
|
|
|
31
31
|
* literal=1.0 upstream in expandedMatchScore. v1 #99 gave CJK full literal credit via pipe-aliases; v2 reaches
|
|
32
32
|
* the right gene at ~2/3 strength. Lifting this to parity is a scoring-design change beyond this signal port.
|
|
33
33
|
*/
|
|
34
|
-
export declare function tagOverlapScore(signals: readonly string[], gene: GeneTagInput): number;
|
|
34
|
+
export declare function tagOverlapScore(signals: readonly string[], gene: GeneTagInput): number;
|
|
35
|
+
export interface SemanticIdfDocument {
|
|
36
|
+
readonly tags: readonly string[];
|
|
37
|
+
readonly text: string;
|
|
38
|
+
}
|
|
39
|
+
export interface SemanticIdfProfile {
|
|
40
|
+
readonly documentCount: number;
|
|
41
|
+
readonly tagIdf: ReadonlyMap<string, number>;
|
|
42
|
+
readonly tokenIdf: ReadonlyMap<string, number>;
|
|
43
|
+
/** Deterministic, data-free identity for replaying the exact document-frequency profile. */
|
|
44
|
+
readonly version: string;
|
|
45
|
+
}
|
|
46
|
+
export declare function buildSemanticIdfProfile(documents: readonly SemanticIdfDocument[]): SemanticIdfProfile;
|
|
47
|
+
export declare function idfTagOverlapScore(signals: readonly string[], gene: GeneTagInput, profile: SemanticIdfProfile): number;
|
|
48
|
+
export declare function idfBagCosine(a: string, b: string, profile: SemanticIdfProfile): number;
|