@evomap/evolver-core 2.0.0-beta.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/antiDistill.d.ts +35 -0
- package/dist/algo/antiDistill.js +167 -0
- package/dist/algo/bans.d.ts +6 -0
- package/dist/algo/bans.js +33 -0
- package/dist/algo/candidateAssembly.d.ts +65 -0
- package/dist/algo/candidateAssembly.js +196 -0
- package/dist/algo/capabilityCandidates.d.ts +38 -0
- package/dist/algo/capabilityCandidates.js +131 -0
- package/dist/algo/confidence.d.ts +106 -0
- package/dist/algo/confidence.js +196 -0
- package/dist/algo/cycleEngine.d.ts +143 -0
- package/dist/algo/cycleEngine.js +417 -0
- package/dist/algo/cycleFailureClassifier.d.ts +52 -0
- package/dist/algo/cycleFailureClassifier.js +104 -0
- package/dist/algo/epigenetics.d.ts +14 -0
- package/dist/algo/epigenetics.js +41 -0
- package/dist/algo/evolutionEvent.d.ts +23 -0
- package/dist/algo/evolutionEvent.js +24 -0
- package/dist/algo/exploration.d.ts +34 -0
- package/dist/algo/exploration.js +55 -0
- package/dist/algo/geneHealth.d.ts +24 -0
- package/dist/algo/geneHealth.js +16 -0
- package/dist/algo/geneIntake.d.ts +37 -0
- package/dist/algo/geneIntake.js +100 -0
- package/dist/algo/genePromotion.d.ts +48 -0
- package/dist/algo/genePromotion.js +53 -0
- package/dist/algo/geneSelection.d.ts +128 -0
- package/dist/algo/geneSelection.js +222 -0
- package/dist/algo/index.d.ts +18 -0
- package/dist/algo/index.js +18 -0
- package/dist/algo/mutation.d.ts +12 -0
- package/dist/algo/mutation.js +22 -0
- package/dist/algo/orchestrator.d.ts +65 -0
- package/dist/algo/orchestrator.js +63 -0
- package/dist/algo/solidify.d.ts +43 -0
- package/dist/algo/solidify.js +63 -0
- package/dist/algo/strategyPresets.d.ts +32 -0
- package/dist/algo/strategyPresets.js +61 -0
- package/dist/assetstore/index.d.ts +8 -0
- package/dist/assetstore/index.js +8 -0
- package/dist/assetstore/learningHistory.d.ts +25 -0
- package/dist/assetstore/learningHistory.js +38 -0
- package/dist/assetstore/localJsonl.d.ts +34 -0
- package/dist/assetstore/localJsonl.js +182 -0
- package/dist/assetstore/pendingSignals.d.ts +22 -0
- package/dist/assetstore/pendingSignals.js +171 -0
- package/dist/assetstore/provenance.d.ts +39 -0
- package/dist/assetstore/provenance.js +109 -0
- package/dist/assetstore/provider.d.ts +48 -0
- package/dist/assetstore/provider.js +36 -0
- package/dist/assetstore/remoteStub.d.ts +32 -0
- package/dist/assetstore/remoteStub.js +36 -0
- package/dist/assetstore/reviewFilter.d.ts +20 -0
- package/dist/assetstore/reviewFilter.js +44 -0
- package/dist/assetstore/reviewLedger.d.ts +55 -0
- package/dist/assetstore/reviewLedger.js +121 -0
- package/dist/benchmark/antiGeneBenchmark.d.ts +79 -0
- package/dist/benchmark/antiGeneBenchmark.js +241 -0
- package/dist/benchmark/antiGeneRollout.d.ts +80 -0
- package/dist/benchmark/antiGeneRollout.js +256 -0
- package/dist/benchmark/benchmark.d.ts +32 -0
- package/dist/benchmark/benchmark.js +26 -0
- package/dist/benchmark/evolutionThesisSolver.d.ts +25 -0
- package/dist/benchmark/evolutionThesisSolver.js +41 -0
- package/dist/benchmark/index.d.ts +5 -0
- package/dist/benchmark/index.js +5 -0
- package/dist/benchmark/thesis.d.ts +85 -0
- package/dist/benchmark/thesis.js +150 -0
- package/dist/bootstrap/envFingerprint.d.ts +45 -0
- package/dist/bootstrap/envFingerprint.js +72 -0
- package/dist/bootstrap/index.d.ts +2 -0
- package/dist/bootstrap/index.js +2 -0
- package/dist/bootstrap/selfEvolve.d.ts +36 -0
- package/dist/bootstrap/selfEvolve.js +26 -0
- package/dist/cycle/cycleTimeline.d.ts +35 -0
- package/dist/cycle/cycleTimeline.js +74 -0
- package/dist/cycle/index.d.ts +2 -0
- package/dist/cycle/index.js +2 -0
- package/dist/cycle/stateMachine.d.ts +6 -0
- package/dist/cycle/stateMachine.js +25 -0
- package/dist/daemon/cycleContext.d.ts +12 -0
- package/dist/daemon/cycleContext.js +13 -0
- package/dist/daemon/drain.d.ts +11 -0
- package/dist/daemon/drain.js +16 -0
- package/dist/daemon/guards.d.ts +87 -0
- package/dist/daemon/guards.js +142 -0
- package/dist/daemon/idempotency.d.ts +11 -0
- package/dist/daemon/idempotency.js +36 -0
- package/dist/daemon/idleScheduler.d.ts +55 -0
- package/dist/daemon/idleScheduler.js +181 -0
- package/dist/daemon/index.d.ts +8 -0
- package/dist/daemon/index.js +8 -0
- package/dist/daemon/stepRunner.d.ts +12 -0
- package/dist/daemon/stepRunner.js +31 -0
- package/dist/daemon/unhandledRejectionWindow.d.ts +21 -0
- package/dist/daemon/unhandledRejectionWindow.js +52 -0
- package/dist/daemon/watchdog.d.ts +11 -0
- package/dist/daemon/watchdog.js +14 -0
- package/dist/events/eventSchema.d.ts +132 -0
- package/dist/events/eventSchema.js +30 -0
- package/dist/events/eventStore.d.ts +32 -0
- package/dist/events/eventStore.js +137 -0
- package/dist/events/ingest.d.ts +34 -0
- package/dist/events/ingest.js +61 -0
- package/dist/events/paths.d.ts +22 -0
- package/dist/events/paths.js +44 -0
- package/dist/events/projectors.d.ts +9 -0
- package/dist/events/projectors.js +11 -0
- package/dist/events/public.d.ts +14 -0
- package/dist/events/public.js +8 -0
- package/dist/events/replayer.d.ts +26 -0
- package/dist/events/replayer.js +67 -0
- package/dist/events/reports.d.ts +90 -0
- package/dist/events/reports.js +244 -0
- package/dist/events/retention.d.ts +70 -0
- package/dist/events/retention.js +177 -0
- package/dist/events/sink.d.ts +5 -0
- package/dist/events/sink.js +1 -0
- package/dist/exec/autoExec.d.ts +137 -0
- package/dist/exec/autoExec.js +221 -0
- package/dist/exec/autonomousCycle.d.ts +48 -0
- package/dist/exec/autonomousCycle.js +78 -0
- package/dist/exec/claudeBridge.d.ts +112 -0
- package/dist/exec/claudeBridge.js +219 -0
- package/dist/exec/index.d.ts +9 -0
- package/dist/exec/index.js +11 -0
- package/dist/exec/openPrRegistry.d.ts +60 -0
- package/dist/exec/openPrRegistry.js +130 -0
- package/dist/exec/policy/blastRadius.d.ts +31 -0
- package/dist/exec/policy/blastRadius.js +67 -0
- package/dist/exec/policy/constraints.d.ts +26 -0
- package/dist/exec/policy/constraints.js +62 -0
- package/dist/exec/policy/destructive.d.ts +45 -0
- package/dist/exec/policy/destructive.js +108 -0
- package/dist/exec/policy/failureMode.d.ts +33 -0
- package/dist/exec/policy/failureMode.js +49 -0
- package/dist/exec/policy/index.d.ts +24 -0
- package/dist/exec/policy/index.js +30 -0
- package/dist/exec/policy/protectedPaths.d.ts +17 -0
- package/dist/exec/policy/protectedPaths.js +65 -0
- package/dist/exec/policyCheck.d.ts +2 -0
- package/dist/exec/policyCheck.js +7 -0
- package/dist/exec/prompt.d.ts +34 -0
- package/dist/exec/prompt.js +108 -0
- package/dist/exec/proofOfWork.d.ts +9 -0
- package/dist/exec/proofOfWork.js +11 -0
- package/dist/exec/runnerRegistry.d.ts +123 -0
- package/dist/exec/runnerRegistry.js +237 -0
- package/dist/exec/selfPr.d.ts +83 -0
- package/dist/exec/selfPr.js +104 -0
- package/dist/exec/selfPrObfuscation.d.ts +21 -0
- package/dist/exec/selfPrObfuscation.js +85 -0
- package/dist/hooks/hooks.d.ts +117 -0
- package/dist/hooks/hooks.js +89 -0
- package/dist/hooks/index.d.ts +1 -0
- package/dist/hooks/index.js +1 -0
- package/dist/hub/assetCallLog.d.ts +43 -0
- package/dist/hub/assetCallLog.js +72 -0
- package/dist/hub/bindings.d.ts +28 -0
- package/dist/hub/bindings.js +90 -0
- package/dist/hub/capability.d.ts +278 -0
- package/dist/hub/capability.js +1 -0
- package/dist/hub/conversationDistiller.d.ts +88 -0
- package/dist/hub/conversationDistiller.js +264 -0
- package/dist/hub/fake.d.ts +45 -0
- package/dist/hub/fake.js +69 -0
- package/dist/hub/hubReview.d.ts +91 -0
- package/dist/hub/hubReview.js +106 -0
- package/dist/hub/index.d.ts +10 -0
- package/dist/hub/index.js +10 -0
- package/dist/hub/ingest.d.ts +15 -0
- package/dist/hub/ingest.js +15 -0
- package/dist/hub/questionGenerator.d.ts +59 -0
- package/dist/hub/questionGenerator.js +406 -0
- package/dist/hub/reuseDecision.d.ts +87 -0
- package/dist/hub/reuseDecision.js +127 -0
- package/dist/hub/sanitize.d.ts +52 -0
- package/dist/hub/sanitize.js +321 -0
- package/dist/index.d.ts +27 -0
- package/dist/index.js +27 -0
- package/dist/mailbox/catalog.d.ts +17 -0
- package/dist/mailbox/catalog.js +53 -0
- package/dist/mailbox/daemon.d.ts +48 -0
- package/dist/mailbox/daemon.js +61 -0
- package/dist/mailbox/dispatch.d.ts +46 -0
- package/dist/mailbox/dispatch.js +62 -0
- package/dist/mailbox/envelope.d.ts +43 -0
- package/dist/mailbox/envelope.js +52 -0
- package/dist/mailbox/index.d.ts +8 -0
- package/dist/mailbox/index.js +8 -0
- package/dist/mailbox/ipcServer.d.ts +46 -0
- package/dist/mailbox/ipcServer.js +168 -0
- package/dist/mailbox/requestReply.d.ts +40 -0
- package/dist/mailbox/requestReply.js +77 -0
- package/dist/mailbox/store.d.ts +74 -0
- package/dist/mailbox/store.js +308 -0
- package/dist/mailbox/wake.d.ts +45 -0
- package/dist/mailbox/wake.js +60 -0
- package/dist/material/boundary.d.ts +7 -0
- package/dist/material/boundary.js +14 -0
- package/dist/material/consumer.d.ts +21 -0
- package/dist/material/consumer.js +52 -0
- package/dist/material/emit.d.ts +55 -0
- package/dist/material/emit.js +52 -0
- package/dist/material/factory.d.ts +27 -0
- package/dist/material/factory.js +25 -0
- package/dist/material/index.d.ts +8 -0
- package/dist/material/index.js +8 -0
- package/dist/material/materialStore.d.ts +24 -0
- package/dist/material/materialStore.js +76 -0
- package/dist/material/sampling.d.ts +21 -0
- package/dist/material/sampling.js +39 -0
- package/dist/material/sources.d.ts +25 -0
- package/dist/material/sources.js +33 -0
- package/dist/material/watermark.d.ts +21 -0
- package/dist/material/watermark.js +76 -0
- package/dist/observers/cursorRewriteObserver.d.ts +46 -0
- package/dist/observers/cursorRewriteObserver.js +103 -0
- package/dist/observers/distillObserver.d.ts +31 -0
- package/dist/observers/distillObserver.js +93 -0
- package/dist/observers/index.d.ts +5 -0
- package/dist/observers/index.js +5 -0
- package/dist/observers/observerBus.d.ts +44 -0
- package/dist/observers/observerBus.js +73 -0
- package/dist/observers/reflectionObserver.d.ts +31 -0
- package/dist/observers/reflectionObserver.js +124 -0
- package/dist/observers/valueDigestObserver.d.ts +50 -0
- package/dist/observers/valueDigestObserver.js +70 -0
- package/dist/ops/cleanup.d.ts +23 -0
- package/dist/ops/cleanup.js +68 -0
- package/dist/ops/health.d.ts +37 -0
- package/dist/ops/health.js +96 -0
- package/dist/ops/index.d.ts +7 -0
- package/dist/ops/index.js +7 -0
- package/dist/ops/recall.d.ts +65 -0
- package/dist/ops/recall.js +152 -0
- package/dist/ops/reuseOutcomes.d.ts +68 -0
- package/dist/ops/reuseOutcomes.js +126 -0
- package/dist/ops/savingsCore.d.ts +47 -0
- package/dist/ops/savingsCore.js +112 -0
- package/dist/ops/selfUpdate.d.ts +106 -0
- package/dist/ops/selfUpdate.js +294 -0
- package/dist/ops/valueLedger.d.ts +205 -0
- package/dist/ops/valueLedger.js +238 -0
- package/dist/ops/valueOutreach.d.ts +70 -0
- package/dist/ops/valueOutreach.js +244 -0
- package/dist/personality/drift.d.ts +45 -0
- package/dist/personality/drift.js +106 -0
- package/dist/personality/events.d.ts +25 -0
- package/dist/personality/events.js +37 -0
- package/dist/personality/evolveOps.d.ts +20 -0
- package/dist/personality/evolveOps.js +93 -0
- package/dist/personality/index.d.ts +11 -0
- package/dist/personality/index.js +11 -0
- package/dist/personality/mutate.d.ts +21 -0
- package/dist/personality/mutate.js +39 -0
- package/dist/personality/pivot.d.ts +23 -0
- package/dist/personality/pivot.js +22 -0
- package/dist/personality/prompt.d.ts +6 -0
- package/dist/personality/prompt.js +62 -0
- package/dist/personality/riskGate.d.ts +42 -0
- package/dist/personality/riskGate.js +80 -0
- package/dist/personality/schema.d.ts +255 -0
- package/dist/personality/schema.js +119 -0
- package/dist/personality/select.d.ts +46 -0
- package/dist/personality/select.js +71 -0
- package/dist/personality/stats.d.ts +46 -0
- package/dist/personality/stats.js +82 -0
- package/dist/personality/store.d.ts +35 -0
- package/dist/personality/store.js +92 -0
- package/dist/schema/common.d.ts +28 -0
- package/dist/schema/common.js +17 -0
- package/dist/schema/index.d.ts +5 -0
- package/dist/schema/index.js +5 -0
- package/dist/schema/material.d.ts +201 -0
- package/dist/schema/material.js +50 -0
- package/dist/schema/problem.d.ts +160 -0
- package/dist/schema/problem.js +45 -0
- package/dist/schema/proofOfWork.d.ts +97 -0
- package/dist/schema/proofOfWork.js +9 -0
- package/dist/schema/signal.d.ts +41 -0
- package/dist/schema/signal.js +19 -0
- package/dist/shadow/index.d.ts +6 -0
- package/dist/shadow/index.js +6 -0
- package/dist/shadow/jsonlSink.d.ts +17 -0
- package/dist/shadow/jsonlSink.js +27 -0
- package/dist/shadow/redact.d.ts +11 -0
- package/dist/shadow/redact.js +18 -0
- package/dist/shadow/shadowHub.d.ts +12 -0
- package/dist/shadow/shadowHub.js +42 -0
- package/dist/shadow/shadowMailbox.d.ts +17 -0
- package/dist/shadow/shadowMailbox.js +25 -0
- package/dist/shadow/shadowStore.d.ts +18 -0
- package/dist/shadow/shadowStore.js +55 -0
- package/dist/shadow/sink.d.ts +37 -0
- package/dist/shadow/sink.js +18 -0
- package/dist/signals/cycleHistoryFromEvents.d.ts +11 -0
- package/dist/signals/cycleHistoryFromEvents.js +96 -0
- package/dist/signals/expand.d.ts +34 -0
- package/dist/signals/expand.js +125 -0
- package/dist/signals/extractor.d.ts +25 -0
- package/dist/signals/extractor.js +57 -0
- package/dist/signals/index.d.ts +6 -0
- package/dist/signals/index.js +6 -0
- package/dist/signals/metaSignals.d.ts +57 -0
- package/dist/signals/metaSignals.js +158 -0
- package/dist/signals/signalGate.d.ts +15 -0
- package/dist/signals/signalGate.js +40 -0
- package/dist/signals/traceSignals.d.ts +44 -0
- package/dist/signals/traceSignals.js +123 -0
- package/dist/signatures/classifier.d.ts +16 -0
- package/dist/signatures/classifier.js +29 -0
- package/dist/signatures/index.d.ts +2 -0
- package/dist/signatures/index.js +2 -0
- package/dist/signatures/signatures.d.ts +22 -0
- package/dist/signatures/signatures.js +42 -0
- package/dist/strategy/experiment.d.ts +48 -0
- package/dist/strategy/experiment.js +63 -0
- package/dist/strategy/index.d.ts +2 -0
- package/dist/strategy/index.js +2 -0
- package/dist/strategy/strategyPoint.d.ts +30 -0
- package/dist/strategy/strategyPoint.js +23 -0
- package/dist/trace/index.d.ts +2 -0
- package/dist/trace/index.js +2 -0
- package/dist/trace/trajectory.d.ts +82 -0
- package/dist/trace/trajectory.js +117 -0
- package/dist/trace/trajectoryExport.d.ts +237 -0
- package/dist/trace/trajectoryExport.js +1818 -0
- package/dist/trigger/budget.d.ts +16 -0
- package/dist/trigger/budget.js +20 -0
- package/dist/trigger/engine.d.ts +12 -0
- package/dist/trigger/engine.js +23 -0
- package/dist/trigger/index.d.ts +6 -0
- package/dist/trigger/index.js +6 -0
- package/dist/trigger/problemBuilder.d.ts +24 -0
- package/dist/trigger/problemBuilder.js +32 -0
- package/dist/trigger/trigger.d.ts +22 -0
- package/dist/trigger/trigger.js +27 -0
- package/dist/trigger/valueModel.d.ts +26 -0
- package/dist/trigger/valueModel.js +25 -0
- package/dist/trigger/wfq.d.ts +13 -0
- package/dist/trigger/wfq.js +25 -0
- package/dist/util/fileLock.d.ts +24 -0
- package/dist/util/fileLock.js +227 -0
- package/dist/util/index.d.ts +2 -0
- package/dist/util/index.js +2 -0
- package/dist/util/version.d.ts +12 -0
- package/dist/util/version.js +51 -0
- package/dist/verify/index.d.ts +4 -0
- package/dist/verify/index.js +4 -0
- package/dist/verify/rollback.d.ts +22 -0
- package/dist/verify/rollback.js +33 -0
- package/dist/verify/sandboxRunner.d.ts +39 -0
- package/dist/verify/sandboxRunner.js +116 -0
- package/dist/verify/sandboxedValidation.d.ts +35 -0
- package/dist/verify/sandboxedValidation.js +56 -0
- package/dist/verify/validation.d.ts +41 -0
- package/dist/verify/validation.js +85 -0
- package/dist/wire/canonicalize.golden.d.ts +23 -0
- package/dist/wire/canonicalize.golden.js +46 -0
- package/dist/wire/geneHints.d.ts +64 -0
- package/dist/wire/geneHints.js +100 -0
- package/dist/wire/index.d.ts +91 -0
- package/dist/wire/index.js +3 -0
- package/dist/wire/schemaGate.d.ts +11 -0
- package/dist/wire/schemaGate.js +49 -0
- package/dist/workflow/dsl.d.ts +54 -0
- package/dist/workflow/dsl.js +15 -0
- package/dist/workflow/engine.d.ts +30 -0
- package/dist/workflow/engine.js +94 -0
- package/dist/workflow/index.d.ts +2 -0
- package/dist/workflow/index.js +2 -0
- package/package.json +29 -0
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { computeAssetId, verifyAssetId } from '../wire/index.js';
|
|
2
|
+
export class AssetIdMismatchError extends Error {
|
|
3
|
+
claimed;
|
|
4
|
+
actual;
|
|
5
|
+
constructor(claimed, actual) {
|
|
6
|
+
super(`asset_id 不自洽: 声明 ${claimed} 实算 ${actual}`);
|
|
7
|
+
this.claimed = claimed;
|
|
8
|
+
this.actual = actual;
|
|
9
|
+
this.name = 'AssetIdMismatchError';
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
export class CapsuleGeneBindingError extends Error {
|
|
13
|
+
constructor() { super('Capsule.gene 必须非空或显式 "ad-hoc" 哨兵 (批注#28/M3-4)'); this.name = 'CapsuleGeneBindingError'; }
|
|
14
|
+
}
|
|
15
|
+
/**
|
|
16
|
+
* 落库前规范化(共享给各 provider): 计算/校验 asset_id + 强绑定校验.
|
|
17
|
+
* - 缺 asset_id → 计算填入(verified=false 表示非入参自带).
|
|
18
|
+
* - 带 asset_id → 必须自洽, 否则抛 AssetIdMismatchError.
|
|
19
|
+
* - Capsule.gene 必须非空(M3-4 强绑定).
|
|
20
|
+
*/
|
|
21
|
+
export function normalizeForPut(asset) {
|
|
22
|
+
if (asset.type === 'Capsule') {
|
|
23
|
+
const gene = asset.gene;
|
|
24
|
+
if (typeof gene !== 'string' || gene.length === 0)
|
|
25
|
+
throw new CapsuleGeneBindingError();
|
|
26
|
+
}
|
|
27
|
+
const actual = computeAssetId(asset);
|
|
28
|
+
if (actual === null)
|
|
29
|
+
throw new Error('computeAssetId 失败: 资产非对象');
|
|
30
|
+
const claimed = typeof asset.asset_id === 'string' && asset.asset_id.length > 0 && asset.asset_id !== 'IGNORED'
|
|
31
|
+
? asset.asset_id : undefined;
|
|
32
|
+
if (claimed !== undefined && claimed !== actual)
|
|
33
|
+
throw new AssetIdMismatchError(claimed, actual);
|
|
34
|
+
const record = { ...asset, asset_id: actual };
|
|
35
|
+
return { record, verified: claimed !== undefined && verifyAssetId(record) };
|
|
36
|
+
}
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import { type AssetKind, type AssetRecord, type AssetStoreProvider, type PutResult, type SearchQuery } from './provider.js';
|
|
2
|
+
/** 远程后端传输(M6 换真 hub Capability; M3-1 用内存桩). */
|
|
3
|
+
export interface RemoteTransport {
|
|
4
|
+
putRemote(record: AssetRecord): Promise<{
|
|
5
|
+
stored: boolean;
|
|
6
|
+
}>;
|
|
7
|
+
getRemote(assetId: string): Promise<AssetRecord | null>;
|
|
8
|
+
searchRemote(query: SearchQuery): Promise<AssetRecord[]>;
|
|
9
|
+
listRemote(kind: AssetKind | undefined, limit: number): Promise<AssetRecord[]>;
|
|
10
|
+
}
|
|
11
|
+
/**
|
|
12
|
+
* 远程资产库参考实现(M3-1). 与 LocalJsonlProvider 同接口, 证明 wire protocol provider-无关.
|
|
13
|
+
* 落库前同样走 normalizeForPut(asset_id 命门 + 强绑定校验在 core 侧, 不信任远端).
|
|
14
|
+
*/
|
|
15
|
+
export declare class RemoteStubProvider implements AssetStoreProvider {
|
|
16
|
+
private readonly transport;
|
|
17
|
+
constructor(transport: RemoteTransport);
|
|
18
|
+
put(asset: AssetRecord): Promise<PutResult>;
|
|
19
|
+
get(assetId: string): Promise<AssetRecord | null>;
|
|
20
|
+
search(query: SearchQuery): Promise<AssetRecord[]>;
|
|
21
|
+
list(kind?: AssetKind, limit?: number): Promise<AssetRecord[]>;
|
|
22
|
+
}
|
|
23
|
+
/** 内存传输桩(测试/本地演示用). */
|
|
24
|
+
export declare class InMemoryTransport implements RemoteTransport {
|
|
25
|
+
private readonly db;
|
|
26
|
+
putRemote(record: AssetRecord): Promise<{
|
|
27
|
+
stored: boolean;
|
|
28
|
+
}>;
|
|
29
|
+
getRemote(assetId: string): Promise<AssetRecord | null>;
|
|
30
|
+
searchRemote(q: SearchQuery): Promise<AssetRecord[]>;
|
|
31
|
+
listRemote(kind: AssetKind | undefined, limit: number): Promise<AssetRecord[]>;
|
|
32
|
+
}
|
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
import { normalizeForPut, } from './provider.js';
|
|
2
|
+
/**
|
|
3
|
+
* 远程资产库参考实现(M3-1). 与 LocalJsonlProvider 同接口, 证明 wire protocol provider-无关.
|
|
4
|
+
* 落库前同样走 normalizeForPut(asset_id 命门 + 强绑定校验在 core 侧, 不信任远端).
|
|
5
|
+
*/
|
|
6
|
+
export class RemoteStubProvider {
|
|
7
|
+
transport;
|
|
8
|
+
constructor(transport) {
|
|
9
|
+
this.transport = transport;
|
|
10
|
+
}
|
|
11
|
+
async put(asset) {
|
|
12
|
+
const { record, verified } = normalizeForPut(asset);
|
|
13
|
+
const { stored } = await this.transport.putRemote(record);
|
|
14
|
+
return { asset_id: record.asset_id, stored, verified };
|
|
15
|
+
}
|
|
16
|
+
get(assetId) { return this.transport.getRemote(assetId); }
|
|
17
|
+
search(query) { return this.transport.searchRemote(query); }
|
|
18
|
+
list(kind, limit = 1000) { return this.transport.listRemote(kind, limit); }
|
|
19
|
+
}
|
|
20
|
+
/** 内存传输桩(测试/本地演示用). */
|
|
21
|
+
export class InMemoryTransport {
|
|
22
|
+
db = new Map();
|
|
23
|
+
async putRemote(record) {
|
|
24
|
+
if (this.db.has(record.asset_id))
|
|
25
|
+
return { stored: false };
|
|
26
|
+
this.db.set(record.asset_id, record);
|
|
27
|
+
return { stored: true };
|
|
28
|
+
}
|
|
29
|
+
async getRemote(assetId) { return this.db.get(assetId) ?? null; }
|
|
30
|
+
async searchRemote(q) {
|
|
31
|
+
return [...this.db.values()].filter((r) => !q.kind || r.type === q.kind).slice(0, q.limit ?? 1000);
|
|
32
|
+
}
|
|
33
|
+
async listRemote(kind, limit) {
|
|
34
|
+
return [...this.db.values()].filter((r) => !kind || r.type === kind).slice(0, limit);
|
|
35
|
+
}
|
|
36
|
+
}
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import type { AssetStoreProvider, AssetRecord } from './provider.js';
|
|
2
|
+
import { ReviewLedger } from './reviewLedger.js';
|
|
3
|
+
import { ProvenanceStore } from './provenance.js';
|
|
4
|
+
/**
|
|
5
|
+
* The ReviewLedger CO-LOCATED with a store — its quarantine/approve records live in the SAME dir as the genes.
|
|
6
|
+
* A read site that defaults the ledger independently (e.g. always `assetsDir()`) would, when handed an injected
|
|
7
|
+
* store at a different dir, consult the WRONG ledger and let a draft quarantined beside that store leak.
|
|
8
|
+
* `LocalJsonlProvider.baseDir` is public exactly so callers can co-locate sidecars; any other provider falls back
|
|
9
|
+
* to the live assets dir.
|
|
10
|
+
*/
|
|
11
|
+
export declare function reviewLedgerForStore(store: AssetStoreProvider): ReviewLedger;
|
|
12
|
+
/** The ProvenanceStore CO-LOCATED with a store, symmetric to reviewLedgerForStore. */
|
|
13
|
+
export declare function provenanceStoreForStore(store: AssetStoreProvider): ProvenanceStore;
|
|
14
|
+
/**
|
|
15
|
+
* Read up to `maxGenes` trusted-origin and REVIEW-APPROVED genes from the store, preserving store order.
|
|
16
|
+
* Default-eligible: a gene with NO review record passes (local / cycle / migrated genes); only an explicitly
|
|
17
|
+
* quarantined or rejected draft is withheld. A gene with NO provenance record is trusted; hub-ingested genes are
|
|
18
|
+
* withheld until promotion. This mirrors candidateAssembly's trust-first + review-first filters.
|
|
19
|
+
*/
|
|
20
|
+
export declare function listApprovedGenes(store: AssetStoreProvider, review: ReviewLedger, maxGenes: number, provenance?: ProvenanceStore): Promise<AssetRecord[]>;
|
|
@@ -0,0 +1,44 @@
|
|
|
1
|
+
import { LocalJsonlProvider } from './localJsonl.js';
|
|
2
|
+
import { ReviewLedger } from './reviewLedger.js';
|
|
3
|
+
import { ProvenanceStore } from './provenance.js';
|
|
4
|
+
import { assetsDir } from '../events/paths.js';
|
|
5
|
+
// Scan window before review filtering — the local gene pool is bounded; mirrors makeTrustedGeneResolver's
|
|
6
|
+
// `list('Gene', 1000)`. Scanning wider than maxGenes then filtering means a quarantined draft sitting in the
|
|
7
|
+
// top-N can never crowd an approved gene out of the result (filter-then-bound, not bound-then-filter).
|
|
8
|
+
const GENE_SCAN_LIMIT = 1000;
|
|
9
|
+
/**
|
|
10
|
+
* The ReviewLedger CO-LOCATED with a store — its quarantine/approve records live in the SAME dir as the genes.
|
|
11
|
+
* A read site that defaults the ledger independently (e.g. always `assetsDir()`) would, when handed an injected
|
|
12
|
+
* store at a different dir, consult the WRONG ledger and let a draft quarantined beside that store leak.
|
|
13
|
+
* `LocalJsonlProvider.baseDir` is public exactly so callers can co-locate sidecars; any other provider falls back
|
|
14
|
+
* to the live assets dir.
|
|
15
|
+
*/
|
|
16
|
+
export function reviewLedgerForStore(store) {
|
|
17
|
+
const baseDir = store instanceof LocalJsonlProvider ? store.baseDir : assetsDir();
|
|
18
|
+
return new ReviewLedger(baseDir);
|
|
19
|
+
}
|
|
20
|
+
/** The ProvenanceStore CO-LOCATED with a store, symmetric to reviewLedgerForStore. */
|
|
21
|
+
export function provenanceStoreForStore(store) {
|
|
22
|
+
const baseDir = store instanceof LocalJsonlProvider ? store.baseDir : assetsDir();
|
|
23
|
+
return new ProvenanceStore(baseDir);
|
|
24
|
+
}
|
|
25
|
+
/**
|
|
26
|
+
* Read up to `maxGenes` trusted-origin and REVIEW-APPROVED genes from the store, preserving store order.
|
|
27
|
+
* Default-eligible: a gene with NO review record passes (local / cycle / migrated genes); only an explicitly
|
|
28
|
+
* quarantined or rejected draft is withheld. A gene with NO provenance record is trusted; hub-ingested genes are
|
|
29
|
+
* withheld until promotion. This mirrors candidateAssembly's trust-first + review-first filters.
|
|
30
|
+
*/
|
|
31
|
+
export async function listApprovedGenes(store, review, maxGenes, provenance = provenanceStoreForStore(store)) {
|
|
32
|
+
const all = await store.list('Gene', GENE_SCAN_LIMIT);
|
|
33
|
+
const approved = [];
|
|
34
|
+
for (const g of all) {
|
|
35
|
+
if (!provenance.isTrusted(String(g.asset_id)))
|
|
36
|
+
continue; // hub-untrusted → withhold until promoted
|
|
37
|
+
if (!review.isApproved(String(g.asset_id)))
|
|
38
|
+
continue; // quarantined/rejected draft → withhold
|
|
39
|
+
approved.push(g);
|
|
40
|
+
if (approved.length >= maxGenes)
|
|
41
|
+
break;
|
|
42
|
+
}
|
|
43
|
+
return approved;
|
|
44
|
+
}
|
|
@@ -0,0 +1,55 @@
|
|
|
1
|
+
export type ReviewState = 'quarantined' | 'approved' | 'rejected';
|
|
2
|
+
export interface ReviewRecord {
|
|
3
|
+
assetId: string;
|
|
4
|
+
state: ReviewState;
|
|
5
|
+
at: string;
|
|
6
|
+
by?: string;
|
|
7
|
+
reason?: string;
|
|
8
|
+
}
|
|
9
|
+
/**
|
|
10
|
+
* Append-only JSONL sidecar at <baseDir>/review.jsonl. Default for an asset with NO record = approved (eligible):
|
|
11
|
+
* only auto-distilled drafts are quarantined here; everything else is eligible by default.
|
|
12
|
+
*
|
|
13
|
+
* Resolution is precedence-based, NOT pure last-write-wins: a HUMAN decision (approved/rejected) always beats an
|
|
14
|
+
* auto `quarantined` record regardless of append order; later beats earlier only WITHIN the same class. This is
|
|
15
|
+
* the race guard — `ingest --distill` (quarantine) and `evolver review --approve` (human) run as separate
|
|
16
|
+
* processes, so a quarantine line appended AFTER an approval must not silently withhold the approved gene.
|
|
17
|
+
*/
|
|
18
|
+
export declare class ReviewLedger {
|
|
19
|
+
private readonly now;
|
|
20
|
+
private readonly path;
|
|
21
|
+
private readonly index;
|
|
22
|
+
private sig;
|
|
23
|
+
constructor(baseDir: string, now?: () => number);
|
|
24
|
+
private static isHuman;
|
|
25
|
+
/** Which record wins for an asset_id: a human decision beats a quarantine; otherwise the later one wins. */
|
|
26
|
+
private static keep;
|
|
27
|
+
private load;
|
|
28
|
+
/** Record a review-state for an asset_id (append-only; the JSONL history is the audit trail). */
|
|
29
|
+
mark(rec: Omit<ReviewRecord, 'at'> & {
|
|
30
|
+
at?: string;
|
|
31
|
+
}): ReviewRecord;
|
|
32
|
+
/** Quarantine an auto-distilled draft: its strategy must not enter a real run until reviewed. */
|
|
33
|
+
quarantine(assetId: string, reason?: string): ReviewRecord;
|
|
34
|
+
/**
|
|
35
|
+
* Quarantine ONLY if the asset has no record yet — the atomic, reload-aware form `ingest --distill` uses so a
|
|
36
|
+
* re-distill never re-quarantines a gene a human already approved/rejected. The check + append happen here as
|
|
37
|
+
* one operation (reloads first to see any external decision), closing the caller-side get→quarantine gap; and
|
|
38
|
+
* because `keep()` lets a human decision win regardless of order, a quarantine that still races in behind an
|
|
39
|
+
* approval is inert. Returns the surviving record (the existing decision, or the new quarantine).
|
|
40
|
+
*/
|
|
41
|
+
quarantineIfAbsent(assetId: string, reason?: string): ReviewRecord;
|
|
42
|
+
/** Explicit, audited approval (who/why) — flips a draft to eligible. */
|
|
43
|
+
approve(assetId: string, by: string, reason: string): ReviewRecord;
|
|
44
|
+
/** Explicit, audited rejection (who/why) — a reviewed terminal state that stays out of runs. */
|
|
45
|
+
reject(assetId: string, by: string, reason: string): ReviewRecord;
|
|
46
|
+
get(assetId: string): ReviewRecord | null;
|
|
47
|
+
/**
|
|
48
|
+
* Every recorded review decision (resolved, reload-aware). The authoritative source of which assets are
|
|
49
|
+
* quarantined/approved/rejected — a queue/UI must read pending state from HERE, not by scanning a truncated
|
|
50
|
+
* asset list, so a draft awaiting approval is never missed behind a store-list cutoff.
|
|
51
|
+
*/
|
|
52
|
+
records(): ReviewRecord[];
|
|
53
|
+
/** No record → approved (default eligible); a record → approved only when its state is 'approved'. */
|
|
54
|
+
isApproved(assetId: string): boolean;
|
|
55
|
+
}
|
|
@@ -0,0 +1,121 @@
|
|
|
1
|
+
// Review-state gate: `ingest --distill` mints UNPROVEN gene drafts from real sessions. Their CONTENT quality is
|
|
2
|
+
// unvetted (auto-extracted transcript excerpts), so — like a hub-ingested asset under provenance (#30) — a draft
|
|
3
|
+
// must not influence a real autonomous run until a human reviews it. Review-state is a SIDECAR keyed by asset_id,
|
|
4
|
+
// NOT a field on the asset: asset_id = sha256(canonicalize(asset)), and "has a human vetted this" metadata must
|
|
5
|
+
// not enter the content hash, or it would break content-addressing. Symmetric to provenance, but a DIFFERENT
|
|
6
|
+
// axis: provenance = trustworthy origin (anti-poisoning); review-state = vetted draft quality. The embed gate
|
|
7
|
+
// (#45 requireTrustedGene) only injects a gene's strategy when it is BOTH trusted-origin AND review-approved.
|
|
8
|
+
//
|
|
9
|
+
// Default (no record) = approved/eligible: the only writer of quarantine records is `--distill`; cycle-self-
|
|
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
|
+
import { join, dirname } from 'node:path';
|
|
13
|
+
/**
|
|
14
|
+
* Append-only JSONL sidecar at <baseDir>/review.jsonl. Default for an asset with NO record = approved (eligible):
|
|
15
|
+
* only auto-distilled drafts are quarantined here; everything else is eligible by default.
|
|
16
|
+
*
|
|
17
|
+
* Resolution is precedence-based, NOT pure last-write-wins: a HUMAN decision (approved/rejected) always beats an
|
|
18
|
+
* auto `quarantined` record regardless of append order; later beats earlier only WITHIN the same class. This is
|
|
19
|
+
* the race guard — `ingest --distill` (quarantine) and `evolver review --approve` (human) run as separate
|
|
20
|
+
* processes, so a quarantine line appended AFTER an approval must not silently withhold the approved gene.
|
|
21
|
+
*/
|
|
22
|
+
export class ReviewLedger {
|
|
23
|
+
now;
|
|
24
|
+
path;
|
|
25
|
+
index = new Map();
|
|
26
|
+
sig = ''; // `${mtimeMs}:${size}` of review.jsonl at the last index build; '' = never loaded / file absent
|
|
27
|
+
constructor(baseDir, now = Date.now) {
|
|
28
|
+
this.now = now;
|
|
29
|
+
this.path = join(baseDir, 'review.jsonl');
|
|
30
|
+
}
|
|
31
|
+
static isHuman(s) { return s === 'approved' || s === 'rejected'; }
|
|
32
|
+
/** Which record wins for an asset_id: a human decision beats a quarantine; otherwise the later one wins. */
|
|
33
|
+
static keep(existing, r) {
|
|
34
|
+
if (!existing)
|
|
35
|
+
return r;
|
|
36
|
+
if (ReviewLedger.isHuman(r.state))
|
|
37
|
+
return r; // human decision always wins (and later human beats earlier)
|
|
38
|
+
return ReviewLedger.isHuman(existing.state) ? existing : r; // a quarantine replaces only a prior quarantine
|
|
39
|
+
}
|
|
40
|
+
// Reload-aware (not load-once): the resident `autoexec` daemon holds ONE ReviewLedger, yet an operator runs
|
|
41
|
+
// `evolver review --approve` in a SEPARATE process. Re-read review.jsonl whenever its mtime+size changed, so a
|
|
42
|
+
// live approval lifts a quarantined gene without restarting the daemon. The file is small and queried at most
|
|
43
|
+
// once per cycle, so a statSync per query is cheap. (mtime alone can collide within one ms → pair it with size.)
|
|
44
|
+
load() {
|
|
45
|
+
if (!existsSync(this.path)) {
|
|
46
|
+
this.index.clear();
|
|
47
|
+
this.sig = '';
|
|
48
|
+
return;
|
|
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));
|
|
62
|
+
}
|
|
63
|
+
catch { /* skip corrupt line */ }
|
|
64
|
+
}
|
|
65
|
+
this.sig = sig;
|
|
66
|
+
}
|
|
67
|
+
/** Record a review-state for an asset_id (append-only; the JSONL history is the audit trail). */
|
|
68
|
+
mark(rec) {
|
|
69
|
+
this.load();
|
|
70
|
+
const full = { ...rec, at: rec.at ?? new Date(this.now()).toISOString() };
|
|
71
|
+
mkdirSync(dirname(this.path), { recursive: true });
|
|
72
|
+
appendFileSync(this.path, `${JSON.stringify(full)}\n`);
|
|
73
|
+
this.index.set(full.assetId, ReviewLedger.keep(this.index.get(full.assetId), full));
|
|
74
|
+
const st = statSync(this.path); // single stat: two separate calls could straddle a concurrent same-ms append
|
|
75
|
+
this.sig = `${st.mtimeMs}:${st.size}`; // our own write is already indexed; don't force a re-read
|
|
76
|
+
return full;
|
|
77
|
+
}
|
|
78
|
+
/** Quarantine an auto-distilled draft: its strategy must not enter a real run until reviewed. */
|
|
79
|
+
quarantine(assetId, reason = 'auto-distilled — review before use') {
|
|
80
|
+
return this.mark({ assetId, state: 'quarantined', reason });
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Quarantine ONLY if the asset has no record yet — the atomic, reload-aware form `ingest --distill` uses so a
|
|
84
|
+
* re-distill never re-quarantines a gene a human already approved/rejected. The check + append happen here as
|
|
85
|
+
* one operation (reloads first to see any external decision), closing the caller-side get→quarantine gap; and
|
|
86
|
+
* because `keep()` lets a human decision win regardless of order, a quarantine that still races in behind an
|
|
87
|
+
* approval is inert. Returns the surviving record (the existing decision, or the new quarantine).
|
|
88
|
+
*/
|
|
89
|
+
quarantineIfAbsent(assetId, reason = 'auto-distilled — review before use') {
|
|
90
|
+
this.load();
|
|
91
|
+
const existing = this.index.get(assetId);
|
|
92
|
+
return existing ?? this.quarantine(assetId, reason);
|
|
93
|
+
}
|
|
94
|
+
/** Explicit, audited approval (who/why) — flips a draft to eligible. */
|
|
95
|
+
approve(assetId, by, reason) {
|
|
96
|
+
return this.mark({ assetId, state: 'approved', by, reason });
|
|
97
|
+
}
|
|
98
|
+
/** Explicit, audited rejection (who/why) — a reviewed terminal state that stays out of runs. */
|
|
99
|
+
reject(assetId, by, reason) {
|
|
100
|
+
return this.mark({ assetId, state: 'rejected', by, reason });
|
|
101
|
+
}
|
|
102
|
+
get(assetId) {
|
|
103
|
+
this.load();
|
|
104
|
+
return this.index.get(assetId) ?? null;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Every recorded review decision (resolved, reload-aware). The authoritative source of which assets are
|
|
108
|
+
* quarantined/approved/rejected — a queue/UI must read pending state from HERE, not by scanning a truncated
|
|
109
|
+
* asset list, so a draft awaiting approval is never missed behind a store-list cutoff.
|
|
110
|
+
*/
|
|
111
|
+
records() {
|
|
112
|
+
this.load();
|
|
113
|
+
return [...this.index.values()];
|
|
114
|
+
}
|
|
115
|
+
/** No record → approved (default eligible); a record → approved only when its state is 'approved'. */
|
|
116
|
+
isApproved(assetId) {
|
|
117
|
+
this.load();
|
|
118
|
+
const r = this.index.get(assetId);
|
|
119
|
+
return r ? r.state === 'approved' : true;
|
|
120
|
+
}
|
|
121
|
+
}
|
|
@@ -0,0 +1,79 @@
|
|
|
1
|
+
import type { AssetStoreProvider } from '../assetstore/provider.js';
|
|
2
|
+
import { ReviewLedger } from '../assetstore/reviewLedger.js';
|
|
3
|
+
import type { ReportEvent } from '../events/reports.js';
|
|
4
|
+
import type { GepCategory } from '../wire/index.js';
|
|
5
|
+
export type AntiGeneBenchmarkArm = 'baseline' | 'antiGene';
|
|
6
|
+
export type AntiGeneBenchmarkVerdict = 'anti_gene_better' | 'no_clear_improvement' | 'anti_gene_worse' | 'insufficient_samples';
|
|
7
|
+
export interface AntiGeneBenchmarkOutcomeSpec {
|
|
8
|
+
status: 'success' | 'failed';
|
|
9
|
+
repeatedFailure?: boolean;
|
|
10
|
+
overblocked?: boolean;
|
|
11
|
+
score?: number;
|
|
12
|
+
reason?: string;
|
|
13
|
+
}
|
|
14
|
+
export interface AntiGeneBenchmarkTask {
|
|
15
|
+
id: string;
|
|
16
|
+
signals: string[];
|
|
17
|
+
target?: string;
|
|
18
|
+
expectedEffect?: string;
|
|
19
|
+
category?: GepCategory;
|
|
20
|
+
summary?: string;
|
|
21
|
+
confidence?: number;
|
|
22
|
+
expectedAntiWarnings?: string[];
|
|
23
|
+
outcomes: {
|
|
24
|
+
baseline: AntiGeneBenchmarkOutcomeSpec;
|
|
25
|
+
antiGene: AntiGeneBenchmarkOutcomeSpec;
|
|
26
|
+
};
|
|
27
|
+
}
|
|
28
|
+
export interface AntiGeneBenchmarkSuite {
|
|
29
|
+
name: string;
|
|
30
|
+
tasks: AntiGeneBenchmarkTask[];
|
|
31
|
+
}
|
|
32
|
+
export interface AntiGeneBenchmarkArmMetrics {
|
|
33
|
+
arm: AntiGeneBenchmarkArm;
|
|
34
|
+
n: number;
|
|
35
|
+
failures: number;
|
|
36
|
+
failureRate: number;
|
|
37
|
+
repeatedFailures: number;
|
|
38
|
+
overblocked: number;
|
|
39
|
+
observedWarnings: number;
|
|
40
|
+
}
|
|
41
|
+
export interface AntiGeneBenchmarkTaskResult {
|
|
42
|
+
taskId: string;
|
|
43
|
+
baseline: AntiGeneBenchmarkRunResult;
|
|
44
|
+
antiGene: AntiGeneBenchmarkRunResult;
|
|
45
|
+
}
|
|
46
|
+
export interface AntiGeneBenchmarkRunResult {
|
|
47
|
+
status: 'success' | 'failed';
|
|
48
|
+
score: number;
|
|
49
|
+
repeatedFailure: boolean;
|
|
50
|
+
overblocked: boolean;
|
|
51
|
+
observedAntiWarnings: string[];
|
|
52
|
+
missingExpectedWarnings: string[];
|
|
53
|
+
}
|
|
54
|
+
export interface AntiGeneBenchmarkReport {
|
|
55
|
+
suite: string;
|
|
56
|
+
tasks: number;
|
|
57
|
+
baseline: AntiGeneBenchmarkArmMetrics;
|
|
58
|
+
antiGene: AntiGeneBenchmarkArmMetrics;
|
|
59
|
+
missingExpectedWarnings: number;
|
|
60
|
+
overblocked: number;
|
|
61
|
+
failureDelta: number;
|
|
62
|
+
verdict: AntiGeneBenchmarkVerdict;
|
|
63
|
+
taskResults: AntiGeneBenchmarkTaskResult[];
|
|
64
|
+
taskResultsTruncated?: number;
|
|
65
|
+
}
|
|
66
|
+
export interface AntiGeneBenchmarkOptions {
|
|
67
|
+
minSamples?: number;
|
|
68
|
+
minFailureDelta?: number;
|
|
69
|
+
now?: () => number;
|
|
70
|
+
eventsPath?: string;
|
|
71
|
+
}
|
|
72
|
+
export interface AntiGeneBenchmarkDeps {
|
|
73
|
+
store: AssetStoreProvider;
|
|
74
|
+
review?: ReviewLedger;
|
|
75
|
+
}
|
|
76
|
+
export declare function runAntiGeneBenchmark(suite: AntiGeneBenchmarkSuite, deps: AntiGeneBenchmarkDeps, options?: AntiGeneBenchmarkOptions): Promise<AntiGeneBenchmarkReport>;
|
|
77
|
+
export declare function writeAntiGeneBenchmarkResult(report: AntiGeneBenchmarkReport, eventsPath: string, now?: () => number): Promise<void>;
|
|
78
|
+
export declare function antiGeneBenchmarkReportsFromEvents(events: readonly ReportEvent[]): AntiGeneBenchmarkReport[];
|
|
79
|
+
export declare function latestAntiGeneBenchmarkReport(events: readonly ReportEvent[]): AntiGeneBenchmarkReport | null;
|
|
@@ -0,0 +1,241 @@
|
|
|
1
|
+
import { mkdtempSync, rmSync } from 'node:fs';
|
|
2
|
+
import { tmpdir } from 'node:os';
|
|
3
|
+
import { join } from 'node:path';
|
|
4
|
+
import { LocalJsonlProvider } from '../assetstore/localJsonl.js';
|
|
5
|
+
import { ReviewLedger } from '../assetstore/reviewLedger.js';
|
|
6
|
+
import { Ingestor } from '../events/ingest.js';
|
|
7
|
+
import { LineTooLargeError } from '../events/eventStore.js';
|
|
8
|
+
import { CycleEngine } from '../algo/cycleEngine.js';
|
|
9
|
+
import { runEvolutionCycle } from '../algo/orchestrator.js';
|
|
10
|
+
import { makeGeneSelectionPoint } from '../algo/geneSelection.js';
|
|
11
|
+
const ALL_ASSETS_LIMIT = Number.MAX_SAFE_INTEGER;
|
|
12
|
+
const ASSET_KINDS = ['Gene', 'Capsule', 'EvolutionEvent', 'AntiGene'];
|
|
13
|
+
function buildProblem(task, now) {
|
|
14
|
+
return {
|
|
15
|
+
id: task.id,
|
|
16
|
+
signature: `anti-gene-benchmark:${task.id}`,
|
|
17
|
+
signatureV: 1,
|
|
18
|
+
firstSeenAt: new Date(now - 1000).toISOString(),
|
|
19
|
+
lastSeenAt: new Date(now).toISOString(),
|
|
20
|
+
occurrences: 3,
|
|
21
|
+
linkedSignals: task.signals,
|
|
22
|
+
resolvedBy: null,
|
|
23
|
+
status: 'open',
|
|
24
|
+
value: { severity: 1, reach: 1, strategicFit: 1, novelty: 0, costEst: 0 },
|
|
25
|
+
consecutiveFailures: 0,
|
|
26
|
+
cooldownUntil: null,
|
|
27
|
+
extensions: {},
|
|
28
|
+
};
|
|
29
|
+
}
|
|
30
|
+
async function copyAssets(source, dest, includeAntiGenes) {
|
|
31
|
+
for (const kind of ASSET_KINDS) {
|
|
32
|
+
if (kind === 'AntiGene' && !includeAntiGenes)
|
|
33
|
+
continue;
|
|
34
|
+
for (const asset of await source.list(kind, ALL_ASSETS_LIMIT))
|
|
35
|
+
await dest.put(asset);
|
|
36
|
+
}
|
|
37
|
+
}
|
|
38
|
+
function copyReview(source, dest) {
|
|
39
|
+
for (const record of source?.records() ?? []) {
|
|
40
|
+
dest.mark({
|
|
41
|
+
assetId: record.assetId,
|
|
42
|
+
state: record.state,
|
|
43
|
+
...(record.by ? { by: record.by } : {}),
|
|
44
|
+
...(record.reason ? { reason: record.reason } : {}),
|
|
45
|
+
at: record.at,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
}
|
|
49
|
+
function defaultReviewForStore(store, now) {
|
|
50
|
+
const baseDir = store.baseDir;
|
|
51
|
+
return typeof baseDir === 'string' ? new ReviewLedger(baseDir, now) : null;
|
|
52
|
+
}
|
|
53
|
+
function observedWarningIds(decision) {
|
|
54
|
+
if (!decision || typeof decision !== 'object')
|
|
55
|
+
return [];
|
|
56
|
+
const warnings = decision.antiWarnings;
|
|
57
|
+
if (!Array.isArray(warnings))
|
|
58
|
+
return [];
|
|
59
|
+
return warnings
|
|
60
|
+
.map((warning) => {
|
|
61
|
+
if (!warning || typeof warning !== 'object' || Array.isArray(warning))
|
|
62
|
+
return '';
|
|
63
|
+
const row = warning;
|
|
64
|
+
return String(row.antiGeneId ?? row.assetId ?? '');
|
|
65
|
+
})
|
|
66
|
+
.filter(Boolean);
|
|
67
|
+
}
|
|
68
|
+
function missingExpected(expected, observed) {
|
|
69
|
+
const seen = new Set(observed);
|
|
70
|
+
return (expected ?? []).filter((id) => !seen.has(id));
|
|
71
|
+
}
|
|
72
|
+
function executeFor(outcome) {
|
|
73
|
+
return () => ({
|
|
74
|
+
outcome: {
|
|
75
|
+
status: outcome.status,
|
|
76
|
+
score: outcome.score ?? (outcome.status === 'success' ? 0.9 : 0.2),
|
|
77
|
+
...(outcome.reason ? { reason: outcome.reason } : {}),
|
|
78
|
+
},
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
async function runOneArm(arm, task, source, sourceReview, rootDir, now) {
|
|
82
|
+
const armDir = join(rootDir, arm, task.id);
|
|
83
|
+
const store = new LocalJsonlProvider(join(armDir, 'assets'));
|
|
84
|
+
const review = new ReviewLedger(join(armDir, 'assets'), now);
|
|
85
|
+
await copyAssets(source, store, arm === 'antiGene');
|
|
86
|
+
copyReview(sourceReview, review);
|
|
87
|
+
const eventPath = join(armDir, 'events.jsonl');
|
|
88
|
+
const engine = new CycleEngine({
|
|
89
|
+
ingestor: new Ingestor({ path: eventPath, now }),
|
|
90
|
+
selection: makeGeneSelectionPoint(),
|
|
91
|
+
store,
|
|
92
|
+
now,
|
|
93
|
+
});
|
|
94
|
+
const spec = task.outcomes[arm];
|
|
95
|
+
const opts = {
|
|
96
|
+
cycleId: `${arm}-${task.id}`,
|
|
97
|
+
problem: buildProblem(task, now()),
|
|
98
|
+
signals: task.signals,
|
|
99
|
+
category: task.category ?? 'repair',
|
|
100
|
+
target: task.target ?? 't.ts',
|
|
101
|
+
expectedEffect: task.expectedEffect ?? `anti-gene benchmark ${task.id}`,
|
|
102
|
+
summary: task.summary ?? `anti-gene benchmark ${task.id}`,
|
|
103
|
+
confidence: task.confidence ?? 0.9,
|
|
104
|
+
execute: executeFor(spec),
|
|
105
|
+
review,
|
|
106
|
+
consumePendingSignals: false,
|
|
107
|
+
};
|
|
108
|
+
const result = await runEvolutionCycle(engine, store, opts);
|
|
109
|
+
const observed = observedWarningIds(result.decision);
|
|
110
|
+
return {
|
|
111
|
+
status: spec.status,
|
|
112
|
+
score: spec.score ?? (spec.status === 'success' ? 0.9 : 0.2),
|
|
113
|
+
repeatedFailure: spec.repeatedFailure === true,
|
|
114
|
+
overblocked: spec.overblocked === true,
|
|
115
|
+
observedAntiWarnings: observed,
|
|
116
|
+
missingExpectedWarnings: arm === 'antiGene' ? missingExpected(task.expectedAntiWarnings, observed) : [],
|
|
117
|
+
};
|
|
118
|
+
}
|
|
119
|
+
function armMetrics(arm, results) {
|
|
120
|
+
const n = results.length;
|
|
121
|
+
const failures = results.filter((r) => r.status === 'failed').length;
|
|
122
|
+
return {
|
|
123
|
+
arm,
|
|
124
|
+
n,
|
|
125
|
+
failures,
|
|
126
|
+
failureRate: n > 0 ? failures / n : 0,
|
|
127
|
+
repeatedFailures: results.filter((r) => r.repeatedFailure).length,
|
|
128
|
+
overblocked: results.filter((r) => r.overblocked).length,
|
|
129
|
+
observedWarnings: results.reduce((sum, r) => sum + r.observedAntiWarnings.length, 0),
|
|
130
|
+
};
|
|
131
|
+
}
|
|
132
|
+
function verdict(report, minSamples, minFailureDelta) {
|
|
133
|
+
if (report.baseline.n < minSamples || report.antiGene.n < minSamples)
|
|
134
|
+
return 'insufficient_samples';
|
|
135
|
+
if (report.failureDelta >= minFailureDelta && report.antiGene.observedWarnings > 0 && report.missingExpectedWarnings === 0)
|
|
136
|
+
return 'anti_gene_better';
|
|
137
|
+
if (report.failureDelta < -minFailureDelta || (minFailureDelta === 0 && report.failureDelta < 0))
|
|
138
|
+
return 'anti_gene_worse';
|
|
139
|
+
return 'no_clear_improvement';
|
|
140
|
+
}
|
|
141
|
+
function buildReport(suite, taskResults, minSamples, minFailureDelta) {
|
|
142
|
+
const baseline = armMetrics('baseline', taskResults.map((r) => r.baseline));
|
|
143
|
+
const antiGene = armMetrics('antiGene', taskResults.map((r) => r.antiGene));
|
|
144
|
+
const partial = {
|
|
145
|
+
suite: suite.name,
|
|
146
|
+
tasks: suite.tasks.length,
|
|
147
|
+
baseline,
|
|
148
|
+
antiGene,
|
|
149
|
+
missingExpectedWarnings: taskResults.reduce((sum, r) => sum + r.antiGene.missingExpectedWarnings.length, 0),
|
|
150
|
+
overblocked: antiGene.overblocked,
|
|
151
|
+
failureDelta: baseline.failureRate - antiGene.failureRate,
|
|
152
|
+
taskResults,
|
|
153
|
+
};
|
|
154
|
+
return { ...partial, verdict: verdict(partial, minSamples, minFailureDelta) };
|
|
155
|
+
}
|
|
156
|
+
export async function runAntiGeneBenchmark(suite, deps, options = {}) {
|
|
157
|
+
const now = options.now ?? Date.now;
|
|
158
|
+
const sourceReview = deps.review ?? defaultReviewForStore(deps.store, now);
|
|
159
|
+
if (!sourceReview)
|
|
160
|
+
throw new Error('AntiGene benchmark requires a ReviewLedger or a LocalJsonlProvider-backed store so review state can be copied');
|
|
161
|
+
const minSamples = options.minSamples ?? 30;
|
|
162
|
+
const minFailureDelta = options.minFailureDelta ?? 0.05;
|
|
163
|
+
const rootDir = mkdtempSync(join(tmpdir(), 'anti-gene-benchmark-'));
|
|
164
|
+
try {
|
|
165
|
+
const taskResults = [];
|
|
166
|
+
for (const task of suite.tasks) {
|
|
167
|
+
taskResults.push({
|
|
168
|
+
taskId: task.id,
|
|
169
|
+
baseline: await runOneArm('baseline', task, deps.store, sourceReview, rootDir, now),
|
|
170
|
+
antiGene: await runOneArm('antiGene', task, deps.store, sourceReview, rootDir, now),
|
|
171
|
+
});
|
|
172
|
+
}
|
|
173
|
+
const report = buildReport(suite, taskResults, minSamples, minFailureDelta);
|
|
174
|
+
if (options.eventsPath)
|
|
175
|
+
await writeAntiGeneBenchmarkResult(report, options.eventsPath, now);
|
|
176
|
+
return report;
|
|
177
|
+
}
|
|
178
|
+
finally {
|
|
179
|
+
rmSync(rootDir, { recursive: true, force: true });
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
export async function writeAntiGeneBenchmarkResult(report, eventsPath, now = Date.now) {
|
|
183
|
+
const ingestor = new Ingestor({ path: eventsPath, now });
|
|
184
|
+
try {
|
|
185
|
+
await ingestReportPayload(ingestor, report);
|
|
186
|
+
}
|
|
187
|
+
catch (error) {
|
|
188
|
+
if (!(error instanceof LineTooLargeError))
|
|
189
|
+
throw error;
|
|
190
|
+
await ingestReportPayload(ingestor, compactReportPayload(report, 5));
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
async function ingestReportPayload(ingestor, payload) {
|
|
194
|
+
await ingestor.ingest({
|
|
195
|
+
type: 'anti_gene.benchmark_result',
|
|
196
|
+
human: { title: `anti-gene benchmark ${payload.suite}` },
|
|
197
|
+
payload: payload,
|
|
198
|
+
actor: { kind: 'machine', id: 'anti-gene-benchmark' },
|
|
199
|
+
});
|
|
200
|
+
}
|
|
201
|
+
function compactWarnings(items) {
|
|
202
|
+
return items.slice(0, 3);
|
|
203
|
+
}
|
|
204
|
+
function compactRunResult(result) {
|
|
205
|
+
return {
|
|
206
|
+
...result,
|
|
207
|
+
observedAntiWarnings: compactWarnings(result.observedAntiWarnings),
|
|
208
|
+
missingExpectedWarnings: compactWarnings(result.missingExpectedWarnings),
|
|
209
|
+
};
|
|
210
|
+
}
|
|
211
|
+
function compactReportPayload(report, maxTasks) {
|
|
212
|
+
return {
|
|
213
|
+
...report,
|
|
214
|
+
taskResults: report.taskResults.slice(0, maxTasks).map((task) => ({
|
|
215
|
+
taskId: task.taskId,
|
|
216
|
+
baseline: compactRunResult(task.baseline),
|
|
217
|
+
antiGene: compactRunResult(task.antiGene),
|
|
218
|
+
})),
|
|
219
|
+
taskResultsTruncated: Math.max(0, report.taskResults.length - maxTasks),
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
function isReportPayload(value) {
|
|
223
|
+
return !!value && typeof value === 'object'
|
|
224
|
+
&& typeof value.suite === 'string'
|
|
225
|
+
&& typeof value.tasks === 'number'
|
|
226
|
+
&& typeof value.verdict === 'string';
|
|
227
|
+
}
|
|
228
|
+
export function antiGeneBenchmarkReportsFromEvents(events) {
|
|
229
|
+
const out = [];
|
|
230
|
+
for (const event of events) {
|
|
231
|
+
if (event.type !== 'anti_gene.benchmark_result')
|
|
232
|
+
continue;
|
|
233
|
+
if (isReportPayload(event.payload))
|
|
234
|
+
out.push(event.payload);
|
|
235
|
+
}
|
|
236
|
+
return out;
|
|
237
|
+
}
|
|
238
|
+
export function latestAntiGeneBenchmarkReport(events) {
|
|
239
|
+
const reports = antiGeneBenchmarkReportsFromEvents(events);
|
|
240
|
+
return reports[reports.length - 1] ?? null;
|
|
241
|
+
}
|