@hmharness/evolution 0.18.15 → 0.19.1
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/cognitive-audit.d.ts +55 -0
- package/dist/cognitive-audit.js +84 -0
- package/dist/evolve.js +23 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +1 -0
- package/package.json +3 -2
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export interface EvolutionOutcomeLike {
|
|
2
|
+
name: string;
|
|
3
|
+
action: 'promoted' | 'rejected' | 'error';
|
|
4
|
+
reason: string;
|
|
5
|
+
baseline?: {
|
|
6
|
+
passRate: number;
|
|
7
|
+
};
|
|
8
|
+
candidate?: {
|
|
9
|
+
passRate: number;
|
|
10
|
+
};
|
|
11
|
+
holdout?: {
|
|
12
|
+
baselineRate: number;
|
|
13
|
+
candidateRate: number;
|
|
14
|
+
};
|
|
15
|
+
lineage?: {
|
|
16
|
+
parentInsights?: string[];
|
|
17
|
+
scores?: Record<string, number | undefined>;
|
|
18
|
+
metaModel?: string;
|
|
19
|
+
decidedAt?: string;
|
|
20
|
+
};
|
|
21
|
+
}
|
|
22
|
+
export interface SkillContract {
|
|
23
|
+
hypothesis: string;
|
|
24
|
+
expected: string;
|
|
25
|
+
regression: string;
|
|
26
|
+
}
|
|
27
|
+
/** Parse the blueprint declaration header from a skill draft's markdown. */
|
|
28
|
+
export declare function parseSkillContract(skillMd: string): SkillContract | null;
|
|
29
|
+
export interface AuditEvent {
|
|
30
|
+
at: string;
|
|
31
|
+
event: 'promoted' | 'rejected' | 'error' | 'rolled-back' | 'contract-rejected';
|
|
32
|
+
candidateId: string;
|
|
33
|
+
target: string;
|
|
34
|
+
hypothesis: string;
|
|
35
|
+
detail: Record<string, unknown>;
|
|
36
|
+
}
|
|
37
|
+
/** Append one outcome to the immutable audit trail (best-effort, never throws). */
|
|
38
|
+
export declare function auditEvolutionOutcome(home: string, outcome: EvolutionOutcomeLike, skillMd?: string): Promise<void>;
|
|
39
|
+
/** EV-012 at the canary/impact seam: bench pass-rate is the METRIC; real
|
|
40
|
+
* sessions' impact delta is the TASK. Metric up + task flat = Goodhart. */
|
|
41
|
+
export declare function checkCanaryRewardHacking(input: {
|
|
42
|
+
benchPassRateBefore: number;
|
|
43
|
+
benchPassRateAfter: number;
|
|
44
|
+
realTaskSuccessBefore: number;
|
|
45
|
+
realTaskSuccessAfter: number;
|
|
46
|
+
}): {
|
|
47
|
+
hacked: boolean;
|
|
48
|
+
reason?: string;
|
|
49
|
+
};
|
|
50
|
+
/** Strict mode gate (evolution.requireContract=true): undeclared drafts are
|
|
51
|
+
* rejected BEFORE bench spend, with an audit trail entry saying why. */
|
|
52
|
+
export declare function enforceContractGate(home: string, name: string, skillMd: string, requireContract: boolean): Promise<{
|
|
53
|
+
pass: boolean;
|
|
54
|
+
contract: SkillContract | null;
|
|
55
|
+
}>;
|
|
@@ -0,0 +1,84 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @hmharness/evolution - cognitive audit bridge (blueprint EV-002/EV-011/EV-012)
|
|
3
|
+
*
|
|
4
|
+
* Every evolve-loop outcome lands in the IMMUTABLE cognitive audit trail
|
|
5
|
+
* (HMH_HOME/cognitive/evolution/audit.jsonl) with the blueprint's candidate
|
|
6
|
+
* contract when the draft declares one:
|
|
7
|
+
*
|
|
8
|
+
* Hypothesis: <what changes and why it should work>
|
|
9
|
+
* Expected: <metric op value>
|
|
10
|
+
* Regression: <what this could hurt>
|
|
11
|
+
*
|
|
12
|
+
* Drafts WITHOUT declarations still audit (declared:false) — visible
|
|
13
|
+
* honesty, not silent acceptance. Teams can enforce the blueprint strictly
|
|
14
|
+
* with evolution.requireContract=true in config: undeclared candidates are
|
|
15
|
+
* then rejected before any bench spend.
|
|
16
|
+
*/
|
|
17
|
+
import { appendFile, mkdir } from 'node:fs/promises';
|
|
18
|
+
import { join } from 'node:path';
|
|
19
|
+
import { detectRewardHacking } from '@hmharness/cognitive';
|
|
20
|
+
/** Parse the blueprint declaration header from a skill draft's markdown. */
|
|
21
|
+
export function parseSkillContract(skillMd) {
|
|
22
|
+
const grab = (key) => {
|
|
23
|
+
const m = skillMd.match(new RegExp(`^\\s*${key}\\s*[::]\\s*(.+)$`, 'mi'));
|
|
24
|
+
return m?.[1]?.trim() ?? '';
|
|
25
|
+
};
|
|
26
|
+
const hypothesis = grab('Hypothesis');
|
|
27
|
+
const expected = grab('Expected');
|
|
28
|
+
const regression = grab('Regression');
|
|
29
|
+
if (!hypothesis || !regression)
|
|
30
|
+
return null;
|
|
31
|
+
return { hypothesis, expected: expected || 'unspecified', regression };
|
|
32
|
+
}
|
|
33
|
+
/** Append one outcome to the immutable audit trail (best-effort, never throws). */
|
|
34
|
+
export async function auditEvolutionOutcome(home, outcome, skillMd) {
|
|
35
|
+
try {
|
|
36
|
+
const contract = skillMd ? parseSkillContract(skillMd) : null;
|
|
37
|
+
const event = {
|
|
38
|
+
at: outcome.lineage?.decidedAt ?? new Date().toISOString(),
|
|
39
|
+
event: outcome.action === 'promoted' ? 'promoted' : outcome.action === 'rejected' ? /rolled back/i.test(outcome.reason) ? 'rolled-back' : 'rejected' : 'error',
|
|
40
|
+
candidateId: outcome.name,
|
|
41
|
+
target: 'skill',
|
|
42
|
+
hypothesis: contract?.hypothesis ?? '(undeclared draft — no Hypothesis/Regression header)',
|
|
43
|
+
detail: {
|
|
44
|
+
reason: outcome.reason,
|
|
45
|
+
declared: Boolean(contract),
|
|
46
|
+
contract,
|
|
47
|
+
trainBaseline: outcome.baseline?.passRate,
|
|
48
|
+
trainCandidate: outcome.candidate?.passRate,
|
|
49
|
+
holdoutBaseline: outcome.holdout?.baselineRate,
|
|
50
|
+
holdoutCandidate: outcome.holdout?.candidateRate,
|
|
51
|
+
lineage: outcome.lineage,
|
|
52
|
+
},
|
|
53
|
+
};
|
|
54
|
+
const dir = join(home, 'cognitive', 'evolution');
|
|
55
|
+
await mkdir(dir, { recursive: true });
|
|
56
|
+
await appendFile(join(dir, 'audit.jsonl'), JSON.stringify(event) + '\n', 'utf8');
|
|
57
|
+
}
|
|
58
|
+
catch {
|
|
59
|
+
/* audit is best-effort by contract */
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
/** EV-012 at the canary/impact seam: bench pass-rate is the METRIC; real
|
|
63
|
+
* sessions' impact delta is the TASK. Metric up + task flat = Goodhart. */
|
|
64
|
+
export function checkCanaryRewardHacking(input) {
|
|
65
|
+
return detectRewardHacking({
|
|
66
|
+
metricBefore: input.benchPassRateBefore,
|
|
67
|
+
metricAfter: input.benchPassRateAfter,
|
|
68
|
+
realTaskSuccessBefore: input.realTaskSuccessBefore,
|
|
69
|
+
realTaskSuccessAfter: input.realTaskSuccessAfter,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
/** Strict mode gate (evolution.requireContract=true): undeclared drafts are
|
|
73
|
+
* rejected BEFORE bench spend, with an audit trail entry saying why. */
|
|
74
|
+
export async function enforceContractGate(home, name, skillMd, requireContract) {
|
|
75
|
+
const contract = parseSkillContract(skillMd);
|
|
76
|
+
if (contract || !requireContract)
|
|
77
|
+
return { pass: true, contract };
|
|
78
|
+
await auditEvolutionOutcome(home, {
|
|
79
|
+
name,
|
|
80
|
+
action: 'rejected',
|
|
81
|
+
reason: 'evolution.requireContract=true and the draft has no Hypothesis/Regression declaration (blueprint EV-002 contract gate)',
|
|
82
|
+
}, skillMd);
|
|
83
|
+
return { pass: false, contract: null };
|
|
84
|
+
}
|
package/dist/evolve.js
CHANGED
|
@@ -196,9 +196,22 @@ export async function runEvolution(opts) {
|
|
|
196
196
|
}
|
|
197
197
|
catch { /* default floor */ }
|
|
198
198
|
// 5. A/B gate each proposal on the train set.
|
|
199
|
+
const draftsMd = new Map();
|
|
199
200
|
for (const p of proposals.slice(0, maxProposals)) {
|
|
201
|
+
draftsMd.set(p.name, p.skill_md);
|
|
200
202
|
say(`candidate "${p.name}": drafting + candidate bench`);
|
|
201
203
|
try {
|
|
204
|
+
// Cognitive OS contract gate (blueprint EV-002, strict mode only):
|
|
205
|
+
// with evolution.requireContract=true a draft must DECLARE hypothesis
|
|
206
|
+
// + possible regression before any bench budget is spent on it.
|
|
207
|
+
const { enforceContractGate } = await import("./cognitive-audit.js");
|
|
208
|
+
const requireContract = Boolean(opts.requireContract);
|
|
209
|
+
const contractGate = await enforceContractGate(home, p.name, p.skill_md, requireContract);
|
|
210
|
+
if (!contractGate.pass) {
|
|
211
|
+
report.outcomes.push({ name: p.name, action: 'rejected', reason: 'contract gate: draft lacks Hypothesis/Regression declaration (evolution.requireContract)' });
|
|
212
|
+
say(' rejected by contract gate (no declaration)');
|
|
213
|
+
continue;
|
|
214
|
+
}
|
|
202
215
|
// Write-channel anti-poisoning (Misevolve lesson): the behavior gate
|
|
203
216
|
// only sees bench output, so instructions the model merely IGNORES
|
|
204
217
|
// slip through. Screen drafted content for attempts to suppress tool
|
|
@@ -361,6 +374,16 @@ export async function runEvolution(opts) {
|
|
|
361
374
|
say(` error: ${String(err).slice(0, 120)}`);
|
|
362
375
|
}
|
|
363
376
|
}
|
|
377
|
+
// Cognitive OS immutable audit (blueprint EV-011): every outcome of this
|
|
378
|
+
// cycle lands in cognitive/evolution/audit.jsonl — promoted, rejected,
|
|
379
|
+
// rolled-back or error, declared contract or honest declared:false.
|
|
380
|
+
{
|
|
381
|
+
const { auditEvolutionOutcome } = await import("./cognitive-audit.js");
|
|
382
|
+
for (const o of report.outcomes) {
|
|
383
|
+
const draftMd = draftsMd.get(o.name);
|
|
384
|
+
await auditEvolutionOutcome(home, o, draftMd);
|
|
385
|
+
}
|
|
386
|
+
}
|
|
364
387
|
// 6. CODE-LEVEL evolution: propose + sandbox-bench + merge/revert patches.
|
|
365
388
|
// This is the DGM bridge - the agent can now modify its own tool code,
|
|
366
389
|
// gated by the same bench discipline as skill promotion.
|
package/dist/index.d.ts
CHANGED
package/dist/index.js
CHANGED
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@hmharness/evolution",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.19.1",
|
|
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",
|
|
@@ -15,7 +15,8 @@
|
|
|
15
15
|
"build": "tsc -p tsconfig.build.json"
|
|
16
16
|
},
|
|
17
17
|
"dependencies": {
|
|
18
|
-
"@hmharness/
|
|
18
|
+
"@hmharness/cognitive": "0.19.1",
|
|
19
|
+
"@hmharness/kernel": "0.19.1"
|
|
19
20
|
},
|
|
20
21
|
"files": [
|
|
21
22
|
"dist"
|