@evomap/evolver-core 2.0.0-beta.1 → 2.0.0-beta.3
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/assets/gep/genes.jsonl +5 -0
- package/dist/algo/candidateAssembly.js +23 -14
- package/dist/algo/capabilityCandidates.d.ts +2 -0
- package/dist/algo/capabilityCandidates.js +5 -0
- package/dist/algo/conversationSniffer.d.ts +18 -0
- package/dist/algo/conversationSniffer.js +132 -0
- package/dist/algo/cycleEngine.d.ts +11 -0
- package/dist/algo/cycleEngine.js +14 -6
- package/dist/algo/cycleFailureClassifier.d.ts +1 -1
- package/dist/algo/cycleFailureClassifier.js +13 -5
- package/dist/algo/geneIntake.d.ts +14 -4
- package/dist/algo/geneIntake.js +37 -9
- package/dist/algo/geneSelection.d.ts +18 -1
- package/dist/algo/geneSelection.js +42 -18
- package/dist/algo/index.d.ts +2 -0
- package/dist/algo/index.js +2 -0
- package/dist/algo/memoryGraph.d.ts +62 -0
- package/dist/algo/memoryGraph.js +86 -0
- package/dist/algo/orchestrator.d.ts +3 -0
- package/dist/algo/orchestrator.js +14 -2
- package/dist/assetstore/assetSidecarRecords.d.ts +23 -0
- package/dist/assetstore/assetSidecarRecords.js +142 -0
- package/dist/assetstore/assetSidecarRecovery.d.ts +48 -0
- package/dist/assetstore/assetSidecarRecovery.js +288 -0
- package/dist/assetstore/assetStoreHealth.d.ts +75 -0
- package/dist/assetstore/assetStoreHealth.js +277 -0
- package/dist/assetstore/assetStoreLayout.d.ts +2 -0
- package/dist/assetstore/assetStoreLayout.js +6 -0
- package/dist/assetstore/assetStoreStorage.d.ts +42 -0
- package/dist/assetstore/assetStoreStorage.js +318 -0
- package/dist/assetstore/assetSyncLedger.d.ts +32 -0
- package/dist/assetstore/assetSyncLedger.js +66 -0
- package/dist/assetstore/index.d.ts +5 -1
- package/dist/assetstore/index.js +5 -1
- package/dist/assetstore/localJsonl.d.ts +10 -2
- package/dist/assetstore/localJsonl.js +93 -37
- package/dist/assetstore/pendingSignals.js +3 -1
- package/dist/assetstore/provenance.d.ts +20 -2
- package/dist/assetstore/provenance.js +92 -56
- package/dist/assetstore/provider.d.ts +2 -0
- package/dist/assetstore/reviewFilter.js +5 -2
- package/dist/assetstore/reviewLedger.d.ts +14 -2
- package/dist/assetstore/reviewLedger.js +78 -43
- package/dist/assetstore/seedGenes.d.ts +3 -0
- package/dist/assetstore/seedGenes.js +134 -0
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +11 -1
- package/dist/benchmark/antiGeneImpact.d.ts +16 -0
- package/dist/benchmark/antiGeneImpact.js +34 -0
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +13 -1
- package/dist/benchmark/index.d.ts +2 -1
- package/dist/benchmark/index.js +2 -1
- package/dist/benchmark/triggerShift.d.ts +62 -0
- package/dist/benchmark/triggerShift.js +106 -0
- package/dist/events/eventArchive.d.ts +65 -0
- package/dist/events/eventArchive.js +343 -0
- package/dist/events/eventStore.js +2 -12
- package/dist/events/ingest.d.ts +1 -1
- package/dist/events/ingest.js +2 -0
- package/dist/events/paths.d.ts +1 -1
- package/dist/events/paths.js +2 -2
- package/dist/events/public.d.ts +3 -1
- package/dist/events/public.js +2 -1
- package/dist/events/retention.d.ts +24 -1
- package/dist/events/retention.js +133 -37
- package/dist/exec/autoExec.d.ts +6 -1
- package/dist/exec/autoExec.js +34 -0
- package/dist/exec/autonomousCycle.d.ts +2 -0
- package/dist/exec/autonomousCycle.js +5 -0
- package/dist/exec/claudeBridge.d.ts +7 -2
- package/dist/exec/claudeBridge.js +115 -14
- package/dist/exec/prompt.js +9 -0
- package/dist/exec/runnerRegistry.d.ts +64 -18
- package/dist/exec/runnerRegistry.js +341 -38
- package/dist/exec/selfPrObfuscation.d.ts +2 -1
- package/dist/exec/selfPrObfuscation.js +19 -6
- package/dist/hub/agentDirectory.d.ts +90 -0
- package/dist/hub/agentDirectory.js +104 -0
- package/dist/hub/bindings.js +23 -3
- package/dist/hub/capability.d.ts +14 -2
- package/dist/hub/fake.d.ts +4 -2
- package/dist/hub/fake.js +3 -2
- package/dist/hub/index.d.ts +1 -0
- package/dist/hub/index.js +1 -0
- package/dist/mailbox/catalog.js +3 -0
- package/dist/mailbox/ipcServer.js +2 -2
- package/dist/mailbox/store.d.ts +14 -0
- package/dist/mailbox/store.js +44 -2
- package/dist/material/consumer.js +9 -6
- package/dist/material/index.d.ts +1 -0
- package/dist/material/index.js +1 -0
- package/dist/material/materialArchive.d.ts +81 -0
- package/dist/material/materialArchive.js +466 -0
- package/dist/material/materialStore.d.ts +1 -0
- package/dist/material/materialStore.js +6 -13
- package/dist/ops/savingsCore.js +1 -2
- package/dist/ops/selfUpdate.d.ts +10 -1
- package/dist/ops/selfUpdate.js +64 -15
- package/dist/schema/common.d.ts +1 -1
- package/dist/schema/common.js +1 -1
- package/dist/schema/material.d.ts +5 -5
- package/dist/trace/trajectoryExport.js +2 -2
- package/dist/util/fileLock.d.ts +19 -2
- package/dist/util/fileLock.js +166 -31
- package/dist/wire/geneHints.d.ts +42 -1
- package/dist/wire/geneHints.js +52 -1
- package/dist/wire/index.d.ts +14 -2
- package/dist/wire/index.js +1 -1
- package/package.json +6 -1
|
@@ -8,8 +8,12 @@
|
|
|
8
8
|
//
|
|
9
9
|
// Default (no record) = approved/eligible: the only writer of quarantine records is `--distill`; cycle-self-
|
|
10
10
|
// produced and v1-migrated genes are never quarantined, so the explore→prove loop is untouched for them.
|
|
11
|
-
import { appendFileSync, existsSync, readFileSync, mkdirSync, statSync } from 'node:fs';
|
|
12
11
|
import { join, dirname } from 'node:path';
|
|
12
|
+
import { appendUtf8Durable, assertAssetStoreDirectory, ensureAssetStoreDirectory, readUtf8Regular, regularFileFingerprint, withAssetStoreLock, } from './assetStoreStorage.js';
|
|
13
|
+
import { assertTrustSidecarHealthy, parseReviewRecord, parseSidecarJsonl, } from './assetSidecarRecords.js';
|
|
14
|
+
function immutableRecord(record) {
|
|
15
|
+
return Object.freeze({ ...record });
|
|
16
|
+
}
|
|
13
17
|
/**
|
|
14
18
|
* Append-only JSONL sidecar at <baseDir>/review.jsonl. Default for an asset with NO record = approved (eligible):
|
|
15
19
|
* only auto-distilled drafts are quarantined here; everything else is eligible by default.
|
|
@@ -22,11 +26,14 @@ import { join, dirname } from 'node:path';
|
|
|
22
26
|
export class ReviewLedger {
|
|
23
27
|
now;
|
|
24
28
|
path;
|
|
29
|
+
lockPath;
|
|
25
30
|
index = new Map();
|
|
26
|
-
|
|
31
|
+
fileState = null;
|
|
27
32
|
constructor(baseDir, now = Date.now) {
|
|
28
33
|
this.now = now;
|
|
34
|
+
ensureAssetStoreDirectory(baseDir);
|
|
29
35
|
this.path = join(baseDir, 'review.jsonl');
|
|
36
|
+
this.lockPath = join(baseDir, '.assetstore.lock');
|
|
30
37
|
}
|
|
31
38
|
static isHuman(s) { return s === 'approved' || s === 'rejected'; }
|
|
32
39
|
/** Which record wins for an asset_id: a human decision beats a quarantine; otherwise the later one wins. */
|
|
@@ -37,43 +44,50 @@ export class ReviewLedger {
|
|
|
37
44
|
return r; // human decision always wins (and later human beats earlier)
|
|
38
45
|
return ReviewLedger.isHuman(existing.state) ? existing : r; // a quarantine replaces only a prior quarantine
|
|
39
46
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
}
|
|
50
|
-
const st = statSync(this.path);
|
|
51
|
-
const sig = `${st.mtimeMs}:${st.size}`;
|
|
52
|
-
if (sig === this.sig)
|
|
53
|
-
return; // unchanged since last read → cached index is current
|
|
54
|
-
this.index.clear();
|
|
55
|
-
for (const line of readFileSync(this.path, 'utf8').split('\n')) {
|
|
56
|
-
if (!line.trim())
|
|
57
|
-
continue;
|
|
58
|
-
try {
|
|
59
|
-
const r = JSON.parse(line);
|
|
60
|
-
if (r.assetId)
|
|
61
|
-
this.index.set(r.assetId, ReviewLedger.keep(this.index.get(r.assetId), r));
|
|
47
|
+
rebuildIndex(state) {
|
|
48
|
+
const next = new Map();
|
|
49
|
+
const raw = state === 'missing' ? null : readUtf8Regular(this.path);
|
|
50
|
+
if (raw !== null) {
|
|
51
|
+
const parsed = parseSidecarJsonl(raw, parseReviewRecord);
|
|
52
|
+
assertTrustSidecarHealthy('review', parsed);
|
|
53
|
+
for (const record of parsed.records) {
|
|
54
|
+
const frozen = immutableRecord(record);
|
|
55
|
+
next.set(record.assetId, ReviewLedger.keep(next.get(record.assetId), frozen));
|
|
62
56
|
}
|
|
63
|
-
catch { /* skip corrupt line */ }
|
|
64
57
|
}
|
|
65
|
-
this.
|
|
58
|
+
this.index.clear();
|
|
59
|
+
for (const [assetId, record] of next)
|
|
60
|
+
this.index.set(assetId, record);
|
|
61
|
+
this.fileState = state;
|
|
62
|
+
}
|
|
63
|
+
refreshUnderLock() {
|
|
64
|
+
const state = regularFileFingerprint(this.path);
|
|
65
|
+
if (state !== this.fileState)
|
|
66
|
+
this.rebuildIndex(state);
|
|
67
|
+
}
|
|
68
|
+
withFreshRead(read) {
|
|
69
|
+
assertAssetStoreDirectory(dirname(this.path));
|
|
70
|
+
return withAssetStoreLock(this.lockPath, () => {
|
|
71
|
+
this.refreshUnderLock();
|
|
72
|
+
return read(this.index);
|
|
73
|
+
});
|
|
74
|
+
}
|
|
75
|
+
appendUnderLock(full) {
|
|
76
|
+
appendUtf8Durable(this.path, `${JSON.stringify(full)}\n`);
|
|
77
|
+
const frozen = immutableRecord(full);
|
|
78
|
+
this.index.set(full.assetId, ReviewLedger.keep(this.index.get(full.assetId), frozen));
|
|
79
|
+
this.fileState = regularFileFingerprint(this.path);
|
|
80
|
+
return this.index.get(full.assetId);
|
|
66
81
|
}
|
|
67
82
|
/** Record a review-state for an asset_id (append-only; the JSONL history is the audit trail). */
|
|
68
83
|
mark(rec) {
|
|
69
|
-
this.load();
|
|
70
84
|
const full = { ...rec, at: rec.at ?? new Date(this.now()).toISOString() };
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
85
|
+
assertAssetStoreDirectory(dirname(this.path));
|
|
86
|
+
return withAssetStoreLock(this.lockPath, () => {
|
|
87
|
+
this.refreshUnderLock();
|
|
88
|
+
this.appendUnderLock(full);
|
|
89
|
+
return immutableRecord(full);
|
|
90
|
+
});
|
|
77
91
|
}
|
|
78
92
|
/** Quarantine an auto-distilled draft: its strategy must not enter a real run until reviewed. */
|
|
79
93
|
quarantine(assetId, reason = 'auto-distilled — review before use') {
|
|
@@ -87,9 +101,19 @@ export class ReviewLedger {
|
|
|
87
101
|
* approval is inert. Returns the surviving record (the existing decision, or the new quarantine).
|
|
88
102
|
*/
|
|
89
103
|
quarantineIfAbsent(assetId, reason = 'auto-distilled — review before use') {
|
|
90
|
-
this.
|
|
91
|
-
|
|
92
|
-
|
|
104
|
+
assertAssetStoreDirectory(dirname(this.path));
|
|
105
|
+
return withAssetStoreLock(this.lockPath, () => {
|
|
106
|
+
this.refreshUnderLock();
|
|
107
|
+
const existing = this.index.get(assetId);
|
|
108
|
+
if (existing)
|
|
109
|
+
return existing;
|
|
110
|
+
return this.appendUnderLock({
|
|
111
|
+
assetId,
|
|
112
|
+
state: 'quarantined',
|
|
113
|
+
reason,
|
|
114
|
+
at: new Date(this.now()).toISOString(),
|
|
115
|
+
});
|
|
116
|
+
});
|
|
93
117
|
}
|
|
94
118
|
/** Explicit, audited approval (who/why) — flips a draft to eligible. */
|
|
95
119
|
approve(assetId, by, reason) {
|
|
@@ -100,8 +124,7 @@ export class ReviewLedger {
|
|
|
100
124
|
return this.mark({ assetId, state: 'rejected', by, reason });
|
|
101
125
|
}
|
|
102
126
|
get(assetId) {
|
|
103
|
-
this.
|
|
104
|
-
return this.index.get(assetId) ?? null;
|
|
127
|
+
return this.withFreshRead((index) => index.get(assetId) ?? null);
|
|
105
128
|
}
|
|
106
129
|
/**
|
|
107
130
|
* Every recorded review decision (resolved, reload-aware). The authoritative source of which assets are
|
|
@@ -109,13 +132,25 @@ export class ReviewLedger {
|
|
|
109
132
|
* asset list, so a draft awaiting approval is never missed behind a store-list cutoff.
|
|
110
133
|
*/
|
|
111
134
|
records() {
|
|
112
|
-
this.
|
|
113
|
-
|
|
135
|
+
return [...this.snapshot().values()];
|
|
136
|
+
}
|
|
137
|
+
/** One linearizable review snapshot for bounded batch readers. */
|
|
138
|
+
snapshot() {
|
|
139
|
+
return this.withFreshRead((index) => new Map(index));
|
|
114
140
|
}
|
|
115
141
|
/** No record → approved (default eligible); a record → approved only when its state is 'approved'. */
|
|
116
142
|
isApproved(assetId) {
|
|
117
|
-
this.
|
|
118
|
-
|
|
119
|
-
|
|
143
|
+
return this.withFreshRead((index) => {
|
|
144
|
+
const r = index.get(assetId);
|
|
145
|
+
return r ? r.state === 'approved' : true;
|
|
146
|
+
});
|
|
147
|
+
}
|
|
148
|
+
/**
|
|
149
|
+
* True only when a human approval record exists. Safety-sensitive consumers such as AntiGene warning
|
|
150
|
+
* injection must fail closed when review state is absent; unlike cycle/migrate-authored Genes, an unreviewed
|
|
151
|
+
* negative-memory asset must never inherit the ledger's backward-compatible "no record = eligible" default.
|
|
152
|
+
*/
|
|
153
|
+
isExplicitlyApproved(assetId) {
|
|
154
|
+
return this.withFreshRead((index) => index.get(assetId)?.state === 'approved');
|
|
120
155
|
}
|
|
121
156
|
}
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import { computeAssetId, SCHEMA_VERSION } from '../wire/index.js';
|
|
2
|
+
const HARNESS_SEED_GENE_DRAFTS = [
|
|
3
|
+
{
|
|
4
|
+
id: 'gene_seed_publish_feishu_doc',
|
|
5
|
+
category: 'innovate',
|
|
6
|
+
signals_match: [
|
|
7
|
+
'publish_markdown_to_feishu',
|
|
8
|
+
'create_feishu_doc',
|
|
9
|
+
'export_report_to_feishu',
|
|
10
|
+
'publish_results_to_lark_doc',
|
|
11
|
+
'feishu_doc',
|
|
12
|
+
'lark_doc',
|
|
13
|
+
],
|
|
14
|
+
preconditions: [
|
|
15
|
+
'lark-cli is installed and available on PATH.',
|
|
16
|
+
'lark-cli auth status reports a ready user or bot identity.',
|
|
17
|
+
],
|
|
18
|
+
strategy: [
|
|
19
|
+
'Run lark-cli doctor and require an ok result before publishing.',
|
|
20
|
+
'Use the Docs v2 API and avoid deprecated Docs v1 flags.',
|
|
21
|
+
'Write long Markdown content to a temporary file and pass it as an @file input.',
|
|
22
|
+
'Create or update the document with the intended user or bot identity, then return the document URL.',
|
|
23
|
+
'Never overwrite local lark-cli credential files while preparing the document.',
|
|
24
|
+
],
|
|
25
|
+
constraints: { max_files: 2, forbidden_paths: ['.git', 'node_modules', '~/.lark-cli/config.json'] },
|
|
26
|
+
validation: ['node --version'],
|
|
27
|
+
summary: 'Publish Markdown content as a Feishu/Lark document through the official CLI, using file-backed content and Docs v2.',
|
|
28
|
+
},
|
|
29
|
+
{
|
|
30
|
+
id: 'gene_seed_conventional_git_commit',
|
|
31
|
+
category: 'optimize',
|
|
32
|
+
signals_match: [
|
|
33
|
+
'git_commit',
|
|
34
|
+
'create_commit',
|
|
35
|
+
'commit_changes',
|
|
36
|
+
'conventional_commit',
|
|
37
|
+
'stage_and_commit',
|
|
38
|
+
'write_commit_message',
|
|
39
|
+
],
|
|
40
|
+
preconditions: ['A git repository has staged or unstaged changes that should become one logical commit.'],
|
|
41
|
+
strategy: [
|
|
42
|
+
'Inspect git status plus the staged or unstaged diff before choosing a commit shape.',
|
|
43
|
+
'Pick a Conventional Commits type and optional scope from the actual changed files.',
|
|
44
|
+
'Stage only the intended logical files and never stage credentials or unrelated changes.',
|
|
45
|
+
'Write a present-tense imperative subject under 72 characters, with body or footer when needed.',
|
|
46
|
+
'Do not amend, force-push, hard-reset, or bypass hooks without explicit operator approval.',
|
|
47
|
+
],
|
|
48
|
+
constraints: { max_files: 50, forbidden_paths: ['.git', 'node_modules'] },
|
|
49
|
+
validation: ['node --version'],
|
|
50
|
+
summary: 'Create a focused Conventional Commits-style git commit from the real diff while avoiding secrets and unrelated changes.',
|
|
51
|
+
},
|
|
52
|
+
{
|
|
53
|
+
id: 'gene_seed_poll_bugbot_review',
|
|
54
|
+
category: 'optimize',
|
|
55
|
+
signals_match: [
|
|
56
|
+
'poll_bugbot',
|
|
57
|
+
'bugbot_review',
|
|
58
|
+
'wait_for_ci_review',
|
|
59
|
+
'pr_review_gate',
|
|
60
|
+
'cursor_bugbot',
|
|
61
|
+
'pr_opened',
|
|
62
|
+
],
|
|
63
|
+
preconditions: ['An open GitHub PR exists and Cursor Bugbot is expected to review it.'],
|
|
64
|
+
strategy: [
|
|
65
|
+
'Poll the Cursor Bugbot check by name until it reaches a terminal conclusion or the operator timeout is reached.',
|
|
66
|
+
'Treat SUCCESS as mergeable only when required checks are green and there are no unresolved Bugbot comments.',
|
|
67
|
+
'Treat NEUTRAL as not passed; fetch and summarize Cursor Bugbot inline comments for the operator.',
|
|
68
|
+
'On FAILURE or ACTION_REQUIRED, surface the findings and do not merge automatically.',
|
|
69
|
+
'Only squash-merge when the operator explicitly asked for merge and the review gate is clean.',
|
|
70
|
+
],
|
|
71
|
+
constraints: { max_files: 1, forbidden_paths: ['.git', 'node_modules'] },
|
|
72
|
+
validation: ['node --version'],
|
|
73
|
+
summary: 'Wait for Cursor Bugbot on a GitHub PR and gate merge decisions on the actual check conclusion and comments.',
|
|
74
|
+
},
|
|
75
|
+
{
|
|
76
|
+
id: 'gene_seed_gateway_timeout_recovery',
|
|
77
|
+
category: 'repair',
|
|
78
|
+
signals_match: [
|
|
79
|
+
'gateway_timeout',
|
|
80
|
+
'upstream_timeout',
|
|
81
|
+
'http_524',
|
|
82
|
+
'http_504',
|
|
83
|
+
'request_timed_out',
|
|
84
|
+
'retry_on_timeout',
|
|
85
|
+
],
|
|
86
|
+
preconditions: ['A tool call, fetch, subagent request, or long command returned a gateway-class timeout.'],
|
|
87
|
+
strategy: [
|
|
88
|
+
'Treat gateway timeout as transient or size-driven until one bounded recovery attempt proves otherwise.',
|
|
89
|
+
'Retry the exact same operation once, then stop retrying the monolithic call.',
|
|
90
|
+
'If the retry also times out, split the work by file, directory, endpoint, record, section, or time window.',
|
|
91
|
+
'Run independent slices in parallel when safe and merge the results.',
|
|
92
|
+
'Apply the same decomposition recursively only to the slice that still times out.',
|
|
93
|
+
],
|
|
94
|
+
constraints: { max_files: 1, forbidden_paths: ['.git', 'node_modules'] },
|
|
95
|
+
validation: ['node --version'],
|
|
96
|
+
summary: 'Recover from gateway or upstream timeouts by retrying once, then decomposing the work instead of looping the same large call.',
|
|
97
|
+
},
|
|
98
|
+
{
|
|
99
|
+
id: 'gene_seed_github_webhook_listener',
|
|
100
|
+
category: 'innovate',
|
|
101
|
+
signals_match: [
|
|
102
|
+
'github_webhook_listener',
|
|
103
|
+
'bugbot_webhook',
|
|
104
|
+
'passive_pr_notifications',
|
|
105
|
+
'notify_when_bugbot_finishes',
|
|
106
|
+
'webhook_tunnel',
|
|
107
|
+
'github_pr_inbox',
|
|
108
|
+
],
|
|
109
|
+
preconditions: ['A developer machine can run a local listener and an outbound tunnel for GitHub webhook delivery.'],
|
|
110
|
+
strategy: [
|
|
111
|
+
'Use an HMAC-validated local listener for GitHub webhook payloads and deduplicate delivery ids.',
|
|
112
|
+
'Expose the listener through an outbound-only tunnel rather than an inbound public port.',
|
|
113
|
+
'Write vetted notifications into a local inbox with restrictive permissions; never execute webhook payloads.',
|
|
114
|
+
'On session start, re-fetch PR state through GitHub before surfacing any inbox item.',
|
|
115
|
+
'Rotate webhook secrets periodically and immediately after suspected exposure.',
|
|
116
|
+
],
|
|
117
|
+
constraints: { max_files: 20, forbidden_paths: ['.git', 'node_modules'] },
|
|
118
|
+
validation: ['node --version'],
|
|
119
|
+
summary: 'Deploy a local HMAC-validated GitHub webhook listener that turns PR and Bugbot events into safe operator notifications.',
|
|
120
|
+
},
|
|
121
|
+
];
|
|
122
|
+
function materializeSeedGene(draft) {
|
|
123
|
+
const gene = {
|
|
124
|
+
type: 'Gene',
|
|
125
|
+
schema_version: SCHEMA_VERSION,
|
|
126
|
+
...draft,
|
|
127
|
+
asset_id: '',
|
|
128
|
+
};
|
|
129
|
+
return { ...gene, asset_id: computeAssetId(gene) ?? '' };
|
|
130
|
+
}
|
|
131
|
+
export const BUNDLED_HARNESS_SEED_GENES = Object.freeze(HARNESS_SEED_GENE_DRAFTS.map((gene) => Object.freeze(materializeSeedGene(gene))));
|
|
132
|
+
export function bundledHarnessSeedGenesJsonl() {
|
|
133
|
+
return `${BUNDLED_HARNESS_SEED_GENES.map((gene) => JSON.stringify(gene)).join('\n')}\n`;
|
|
134
|
+
}
|
|
@@ -2,6 +2,7 @@ import type { AssetStoreProvider } from '../assetstore/provider.js';
|
|
|
2
2
|
import { ReviewLedger } from '../assetstore/reviewLedger.js';
|
|
3
3
|
import type { ReportEvent } from '../events/reports.js';
|
|
4
4
|
import type { GepCategory } from '../wire/index.js';
|
|
5
|
+
import { type AntiGeneOverblockSummary } from './antiGeneImpact.js';
|
|
5
6
|
export type AntiGeneBenchmarkArm = 'baseline' | 'antiGene';
|
|
6
7
|
export type AntiGeneBenchmarkVerdict = 'anti_gene_better' | 'no_clear_improvement' | 'anti_gene_worse' | 'insufficient_samples';
|
|
7
8
|
export interface AntiGeneBenchmarkOutcomeSpec {
|
|
@@ -58,6 +59,7 @@ export interface AntiGeneBenchmarkReport {
|
|
|
58
59
|
antiGene: AntiGeneBenchmarkArmMetrics;
|
|
59
60
|
missingExpectedWarnings: number;
|
|
60
61
|
overblocked: number;
|
|
62
|
+
overblockedAntiGenes: AntiGeneOverblockSummary[];
|
|
61
63
|
failureDelta: number;
|
|
62
64
|
verdict: AntiGeneBenchmarkVerdict;
|
|
63
65
|
taskResults: AntiGeneBenchmarkTaskResult[];
|
|
@@ -8,6 +8,7 @@ import { LineTooLargeError } from '../events/eventStore.js';
|
|
|
8
8
|
import { CycleEngine } from '../algo/cycleEngine.js';
|
|
9
9
|
import { runEvolutionCycle } from '../algo/orchestrator.js';
|
|
10
10
|
import { makeGeneSelectionPoint } from '../algo/geneSelection.js';
|
|
11
|
+
import { parseOverblockedAntiGenes, summarizeOverblockedAntiGenes } from './antiGeneImpact.js';
|
|
11
12
|
const ALL_ASSETS_LIMIT = Number.MAX_SAFE_INTEGER;
|
|
12
13
|
const ASSET_KINDS = ['Gene', 'Capsule', 'EvolutionEvent', 'AntiGene'];
|
|
13
14
|
function buildProblem(task, now) {
|
|
@@ -148,6 +149,7 @@ function buildReport(suite, taskResults, minSamples, minFailureDelta) {
|
|
|
148
149
|
antiGene,
|
|
149
150
|
missingExpectedWarnings: taskResults.reduce((sum, r) => sum + r.antiGene.missingExpectedWarnings.length, 0),
|
|
150
151
|
overblocked: antiGene.overblocked,
|
|
152
|
+
overblockedAntiGenes: summarizeOverblockedAntiGenes(taskResults),
|
|
151
153
|
failureDelta: baseline.failureRate - antiGene.failureRate,
|
|
152
154
|
taskResults,
|
|
153
155
|
};
|
|
@@ -225,13 +227,21 @@ function isReportPayload(value) {
|
|
|
225
227
|
&& typeof value.tasks === 'number'
|
|
226
228
|
&& typeof value.verdict === 'string';
|
|
227
229
|
}
|
|
230
|
+
function normalizeReportPayload(value) {
|
|
231
|
+
const parsed = parseOverblockedAntiGenes(value.overblockedAntiGenes);
|
|
232
|
+
const truncated = typeof value.taskResultsTruncated === 'number' && value.taskResultsTruncated > 0;
|
|
233
|
+
return {
|
|
234
|
+
...value,
|
|
235
|
+
overblockedAntiGenes: parsed ?? (truncated ? [] : summarizeOverblockedAntiGenes(value.taskResults ?? [])),
|
|
236
|
+
};
|
|
237
|
+
}
|
|
228
238
|
export function antiGeneBenchmarkReportsFromEvents(events) {
|
|
229
239
|
const out = [];
|
|
230
240
|
for (const event of events) {
|
|
231
241
|
if (event.type !== 'anti_gene.benchmark_result')
|
|
232
242
|
continue;
|
|
233
243
|
if (isReportPayload(event.payload))
|
|
234
|
-
out.push(event.payload);
|
|
244
|
+
out.push(normalizeReportPayload(event.payload));
|
|
235
245
|
}
|
|
236
246
|
return out;
|
|
237
247
|
}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
export interface AntiGeneOverblockSummary {
|
|
2
|
+
antiGeneId: string;
|
|
3
|
+
count: number;
|
|
4
|
+
taskIds: string[];
|
|
5
|
+
}
|
|
6
|
+
interface AntiGeneImpactRunResult {
|
|
7
|
+
overblocked: boolean;
|
|
8
|
+
observedAntiWarnings: readonly string[];
|
|
9
|
+
}
|
|
10
|
+
interface AntiGeneImpactTaskResult {
|
|
11
|
+
taskId: string;
|
|
12
|
+
antiGene: AntiGeneImpactRunResult;
|
|
13
|
+
}
|
|
14
|
+
export declare function summarizeOverblockedAntiGenes(taskResults: readonly AntiGeneImpactTaskResult[]): AntiGeneOverblockSummary[];
|
|
15
|
+
export declare function parseOverblockedAntiGenes(value: unknown): AntiGeneOverblockSummary[] | null;
|
|
16
|
+
export {};
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
export function summarizeOverblockedAntiGenes(taskResults) {
|
|
2
|
+
const byId = new Map();
|
|
3
|
+
for (const task of taskResults) {
|
|
4
|
+
if (!task.antiGene.overblocked)
|
|
5
|
+
continue;
|
|
6
|
+
for (const antiGeneId of new Set(task.antiGene.observedAntiWarnings)) {
|
|
7
|
+
const row = byId.get(antiGeneId) ?? { count: 0, taskIds: new Set() };
|
|
8
|
+
row.count += 1;
|
|
9
|
+
row.taskIds.add(task.taskId);
|
|
10
|
+
byId.set(antiGeneId, row);
|
|
11
|
+
}
|
|
12
|
+
}
|
|
13
|
+
return [...byId.entries()]
|
|
14
|
+
.map(([antiGeneId, row]) => ({ antiGeneId, count: row.count, taskIds: [...row.taskIds].sort() }))
|
|
15
|
+
.sort((a, b) => b.count - a.count || a.antiGeneId.localeCompare(b.antiGeneId));
|
|
16
|
+
}
|
|
17
|
+
export function parseOverblockedAntiGenes(value) {
|
|
18
|
+
if (!Array.isArray(value))
|
|
19
|
+
return null;
|
|
20
|
+
const out = [];
|
|
21
|
+
for (const item of value) {
|
|
22
|
+
if (!item || typeof item !== 'object' || Array.isArray(item))
|
|
23
|
+
return null;
|
|
24
|
+
const row = item;
|
|
25
|
+
if (typeof row['antiGeneId'] !== 'string')
|
|
26
|
+
return null;
|
|
27
|
+
if (typeof row['count'] !== 'number' || !Number.isFinite(row['count']))
|
|
28
|
+
return null;
|
|
29
|
+
if (!Array.isArray(row['taskIds']) || !row['taskIds'].every((taskId) => typeof taskId === 'string'))
|
|
30
|
+
return null;
|
|
31
|
+
out.push({ antiGeneId: row['antiGeneId'], count: row['count'], taskIds: [...row['taskIds']] });
|
|
32
|
+
}
|
|
33
|
+
return out;
|
|
34
|
+
}
|
|
@@ -3,6 +3,7 @@ import { ReviewLedger } from '../assetstore/reviewLedger.js';
|
|
|
3
3
|
import type { ReportEvent } from '../events/reports.js';
|
|
4
4
|
import { type CycleInput } from '../algo/cycleEngine.js';
|
|
5
5
|
import type { GepCategory } from '../wire/index.js';
|
|
6
|
+
import { type AntiGeneOverblockSummary } from './antiGeneImpact.js';
|
|
6
7
|
export type AntiGeneRolloutArm = 'baseline' | 'antiGene';
|
|
7
8
|
export type AntiGeneRolloutVerdict = 'anti_gene_better' | 'no_clear_improvement' | 'anti_gene_worse' | 'insufficient_samples';
|
|
8
9
|
export interface AntiGeneRolloutTask {
|
|
@@ -50,6 +51,7 @@ export interface AntiGeneRolloutReport {
|
|
|
50
51
|
antiGene: AntiGeneRolloutArmMetrics;
|
|
51
52
|
missingExpectedWarnings: number;
|
|
52
53
|
overblocked: number;
|
|
54
|
+
overblockedAntiGenes: AntiGeneOverblockSummary[];
|
|
53
55
|
failureDelta: number;
|
|
54
56
|
verdict: AntiGeneRolloutVerdict;
|
|
55
57
|
taskResults: AntiGeneRolloutTaskResult[];
|
|
@@ -8,6 +8,7 @@ import { LineTooLargeError } from '../events/eventStore.js';
|
|
|
8
8
|
import { CycleEngine } from '../algo/cycleEngine.js';
|
|
9
9
|
import { runEvolutionCycle } from '../algo/orchestrator.js';
|
|
10
10
|
import { makeGeneSelectionPoint } from '../algo/geneSelection.js';
|
|
11
|
+
import { parseOverblockedAntiGenes, summarizeOverblockedAntiGenes } from './antiGeneImpact.js';
|
|
11
12
|
const ALL_ASSETS_LIMIT = Number.MAX_SAFE_INTEGER;
|
|
12
13
|
const ASSET_KINDS = ['Gene', 'Capsule', 'EvolutionEvent', 'AntiGene'];
|
|
13
14
|
function buildProblem(task, now) {
|
|
@@ -163,6 +164,7 @@ function buildReport(suite, rawTaskResults, minSamples, minFailureDelta) {
|
|
|
163
164
|
antiGene,
|
|
164
165
|
missingExpectedWarnings: taskResults.reduce((sum, r) => sum + r.antiGene.missingExpectedWarnings.length, 0),
|
|
165
166
|
overblocked: antiGene.overblocked,
|
|
167
|
+
overblockedAntiGenes: summarizeOverblockedAntiGenes(taskResults),
|
|
166
168
|
failureDelta: baseline.failureRate - antiGene.failureRate,
|
|
167
169
|
taskResults,
|
|
168
170
|
};
|
|
@@ -240,13 +242,23 @@ function isReportPayload(value) {
|
|
|
240
242
|
&& typeof value.tasks === 'number'
|
|
241
243
|
&& typeof value.verdict === 'string';
|
|
242
244
|
}
|
|
245
|
+
function normalizeReportPayload(value) {
|
|
246
|
+
const parsed = parseOverblockedAntiGenes(value.overblockedAntiGenes);
|
|
247
|
+
const taskResults = normalizeTaskResults(value.taskResults ?? []);
|
|
248
|
+
const truncated = typeof value.taskResultsTruncated === 'number' && value.taskResultsTruncated > 0;
|
|
249
|
+
return {
|
|
250
|
+
...value,
|
|
251
|
+
taskResults,
|
|
252
|
+
overblockedAntiGenes: parsed ?? (truncated ? [] : summarizeOverblockedAntiGenes(taskResults)),
|
|
253
|
+
};
|
|
254
|
+
}
|
|
243
255
|
export function antiGeneRolloutReportsFromEvents(events) {
|
|
244
256
|
const out = [];
|
|
245
257
|
for (const event of events) {
|
|
246
258
|
if (event.type !== 'anti_gene.rollout_result')
|
|
247
259
|
continue;
|
|
248
260
|
if (isReportPayload(event.payload))
|
|
249
|
-
out.push(event.payload);
|
|
261
|
+
out.push(normalizeReportPayload(event.payload));
|
|
250
262
|
}
|
|
251
263
|
return out;
|
|
252
264
|
}
|
package/dist/benchmark/index.js
CHANGED
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
export declare const TRIGGER_SHIFT_METHOD_VERSION = "trigger-shift-v1";
|
|
2
|
+
/** @experimental Shift dimension for a semantically paired replay task. */
|
|
3
|
+
export type TriggerShiftAxis = 'wrapper_trigger' | 'temporal_context' | 'instruction_phrasing';
|
|
4
|
+
/** @experimental One replay prompt variant in a trigger-shift pair. */
|
|
5
|
+
export interface TriggerShiftTask {
|
|
6
|
+
id: string;
|
|
7
|
+
prompt: string;
|
|
8
|
+
wrapperTrigger?: string;
|
|
9
|
+
temporalContext?: string;
|
|
10
|
+
instructionPhrasing?: string;
|
|
11
|
+
}
|
|
12
|
+
/** @experimental Same objective, paired across one trigger/context shift axis. */
|
|
13
|
+
export interface TriggerShiftPair {
|
|
14
|
+
id: string;
|
|
15
|
+
objectiveId: string;
|
|
16
|
+
axis: TriggerShiftAxis;
|
|
17
|
+
expectedDecision: string;
|
|
18
|
+
train: TriggerShiftTask;
|
|
19
|
+
shifted: TriggerShiftTask;
|
|
20
|
+
}
|
|
21
|
+
/** @experimental Policy output normalized for replay scoring. */
|
|
22
|
+
export interface TriggerShiftDecision {
|
|
23
|
+
label: string;
|
|
24
|
+
confidence?: number;
|
|
25
|
+
}
|
|
26
|
+
/** @experimental Replay-only policy seam; callers own any model/tool execution. */
|
|
27
|
+
export interface TriggerShiftPolicy {
|
|
28
|
+
id: string;
|
|
29
|
+
predict(task: TriggerShiftTask): TriggerShiftDecision;
|
|
30
|
+
}
|
|
31
|
+
/** @experimental One paired replay result with train/shifted rewards and gap. */
|
|
32
|
+
export interface TriggerShiftPairResult {
|
|
33
|
+
pairId: string;
|
|
34
|
+
objectiveId: string;
|
|
35
|
+
axis: TriggerShiftAxis;
|
|
36
|
+
trainTaskId: string;
|
|
37
|
+
shiftedTaskId: string;
|
|
38
|
+
expectedDecision: string;
|
|
39
|
+
trainDecision: string;
|
|
40
|
+
shiftedDecision: string;
|
|
41
|
+
trainReward: number;
|
|
42
|
+
shiftedReward: number;
|
|
43
|
+
gap: number;
|
|
44
|
+
}
|
|
45
|
+
/** @experimental Aggregate replay report; diagnostic only, not a selector input. */
|
|
46
|
+
export interface TriggerShiftReport {
|
|
47
|
+
methodVersion: string;
|
|
48
|
+
policyId: string;
|
|
49
|
+
pairs: number;
|
|
50
|
+
meanTrainReward: number;
|
|
51
|
+
meanShiftedReward: number;
|
|
52
|
+
meanGap: number;
|
|
53
|
+
maxGap: number;
|
|
54
|
+
rows: TriggerShiftPairResult[];
|
|
55
|
+
}
|
|
56
|
+
/**
|
|
57
|
+
* @experimental Offline trigger-shift replay evaluator. It returns inert report
|
|
58
|
+
* rows only: no trigger fires, no store writes, and no live selection updates.
|
|
59
|
+
*/
|
|
60
|
+
export declare function evaluateTriggerShift(policy: TriggerShiftPolicy, pairs: readonly TriggerShiftPair[]): TriggerShiftReport;
|
|
61
|
+
/** @experimental Tiny public calibration/demo suite; not a live threshold. */
|
|
62
|
+
export declare function smallTriggerShiftSuite(): TriggerShiftPair[];
|