@hmharness/evolution 0.9.0 → 0.13.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/evolve.js +40 -1
- package/package.json +1 -1
package/dist/evolve.js
CHANGED
|
@@ -59,14 +59,26 @@ export async function runEvolution(opts) {
|
|
|
59
59
|
// anything but cycles - the documented budget was decorative).
|
|
60
60
|
const budget = await readBudget(home);
|
|
61
61
|
const today = new Date().toISOString().slice(0, 10);
|
|
62
|
+
// Skipped cycles ARE data (SELFFEED honesty rule #2: absent/empty days must
|
|
63
|
+
// be visible). The early returns used to bypass the durable log entirely -
|
|
64
|
+
// a day's skips vanished without a trace and "0 skips" was unauditable
|
|
65
|
+
// (caught by the day-16 meta-audit). Persist before returning.
|
|
66
|
+
const persistSkip = async () => {
|
|
67
|
+
report.estTokens = 0;
|
|
68
|
+
const logDir = join(home, 'evolution');
|
|
69
|
+
await mkdir(logDir, { recursive: true });
|
|
70
|
+
await appendFile(join(logDir, 'log.jsonl'), JSON.stringify(report) + '\n', 'utf8');
|
|
71
|
+
};
|
|
62
72
|
if (budget.maxCyclesPerDay && budget.cyclesToday >= budget.maxCyclesPerDay) {
|
|
63
73
|
report.outcomes.push({ name: '(budget)', action: 'error', reason: `daily cycle limit reached (${budget.cyclesToday}/${budget.maxCyclesPerDay} today) - skipped` });
|
|
74
|
+
await persistSkip();
|
|
64
75
|
return report;
|
|
65
76
|
}
|
|
66
77
|
if (budget.maxCyclesPerDay && budget.maxTokensPerCycle) {
|
|
67
78
|
const dailyCap = budget.maxCyclesPerDay * budget.maxTokensPerCycle;
|
|
68
79
|
if ((budget.tokensToday ?? 0) >= dailyCap) {
|
|
69
80
|
report.outcomes.push({ name: '(budget)', action: 'error', reason: `daily token limit reached (~${budget.tokensToday}/${dailyCap} est-tokens today) - skipped` });
|
|
81
|
+
await persistSkip();
|
|
70
82
|
return report;
|
|
71
83
|
}
|
|
72
84
|
}
|
|
@@ -166,6 +178,19 @@ export async function runEvolution(opts) {
|
|
|
166
178
|
}
|
|
167
179
|
}
|
|
168
180
|
report.proposals = proposals;
|
|
181
|
+
// 4b. Promotion quality floor (day-16 meta-audit): "no regression" alone
|
|
182
|
+
// can promote a candidate that is still terrible when the BASELINE itself
|
|
183
|
+
// is weak (baseline 20% -> candidate 25% passes the old gate). An absolute
|
|
184
|
+
// minimum train pass rate closes that hole. Configurable via
|
|
185
|
+
// config.json evolution.minPassRate (default 0.6).
|
|
186
|
+
let minPassRate = 0.6;
|
|
187
|
+
try {
|
|
188
|
+
const cfg = JSON.parse(await readFile(join(home, 'config.json'), 'utf8'));
|
|
189
|
+
if (typeof cfg.evolution?.minPassRate === 'number' && cfg.evolution.minPassRate >= 0 && cfg.evolution.minPassRate <= 1) {
|
|
190
|
+
minPassRate = cfg.evolution.minPassRate;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
catch { /* default floor */ }
|
|
169
194
|
// 5. A/B gate each proposal on the train set.
|
|
170
195
|
for (const p of proposals.slice(0, maxProposals)) {
|
|
171
196
|
say(`candidate "${p.name}": drafting + candidate bench`);
|
|
@@ -224,6 +249,20 @@ export async function runEvolution(opts) {
|
|
|
224
249
|
say(` rejected (${regression ? 'regression' : 'lower pass rate'})`);
|
|
225
250
|
continue;
|
|
226
251
|
}
|
|
252
|
+
if (candRate < minPassRate) {
|
|
253
|
+
await deleteDraft(home, p.name);
|
|
254
|
+
await recordParetoEntry(home, { name: p.name, parentInsights: insightIds, rejectedReason: `below quality floor (${candRate.toFixed(2)} < ${minPassRate})`, scores: { train: candRate }, metaModel: provider.model, at: new Date().toISOString() });
|
|
255
|
+
report.outcomes.push({
|
|
256
|
+
name: p.name,
|
|
257
|
+
action: 'rejected',
|
|
258
|
+
reason: `below quality floor: pass rate ${candRate} < min ${minPassRate} (non-regression vs a weak baseline ${baseRate} is not good enough)`,
|
|
259
|
+
baseline: { passRate: baseRate, cases: summary(baseResults) },
|
|
260
|
+
candidate: { passRate: candRate, cases: summary(candResults) },
|
|
261
|
+
lineage: { parentInsights: insightIds, scores: { train: candRate }, metaModel: provider.model, decidedAt: new Date().toISOString() },
|
|
262
|
+
});
|
|
263
|
+
say(` rejected (below quality floor ${minPassRate})`);
|
|
264
|
+
continue;
|
|
265
|
+
}
|
|
227
266
|
if (costRegressions.length > 0) {
|
|
228
267
|
await deleteDraft(home, p.name);
|
|
229
268
|
await recordParetoEntry(home, { name: p.name, parentInsights: insightIds, rejectedReason: `cost regression on ${costRegressions.join(', ')}`, scores: { train: candRate }, metaModel: provider.model, at: new Date().toISOString() });
|
|
@@ -291,7 +330,7 @@ export async function runEvolution(opts) {
|
|
|
291
330
|
report.outcomes.push({
|
|
292
331
|
name: p.name,
|
|
293
332
|
action: 'promoted',
|
|
294
|
-
reason: `no regression (train ${(candRate * 100).toFixed(0)}% vs ${(baseRate * 100).toFixed(0)}%${holdout.length ? `, holdout ${(holdoutRate * 100).toFixed(0)}%` : ''}) - promoted to CANARY (20% sessions, impact-gated full promotion)${archivedPrevious ? '; previous version archived' : ''}${weakGate ? ' [WEAK GATE: no holdout cases defined]' : ''}`,
|
|
333
|
+
reason: `no regression (train ${(candRate * 100).toFixed(0)}% vs ${(baseRate * 100).toFixed(0)}%${holdout.length ? `, holdout ${(holdoutRate * 100).toFixed(0)}%` : ''}; floor ≥${(minPassRate * 100).toFixed(0)}%) - promoted to CANARY (20% sessions, impact-gated full promotion)${archivedPrevious ? '; previous version archived' : ''}${weakGate ? ' [WEAK GATE: no holdout cases defined]' : ''}`,
|
|
295
334
|
baseline: { passRate: baseRate, cases: summary(baseResults) },
|
|
296
335
|
candidate: { passRate: candRate, cases: summary(candResults) },
|
|
297
336
|
...(holdout.length ? { holdout: { baselineRate: holdoutBaseRate, candidateRate: holdoutRate } } : {}),
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hmharness/evolution",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.13.0",
|
|
4
4
|
"description": "hmharness evolution subsystem: persistent memory, insight capture, skill library, and the bench that gives evolution its fitness signal. First-class kernel citizen, not a plugin.",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "dist/index.js",
|