@evomap/evolver-core 2.0.0-beta.8 → 2.0.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/algo/candidateAssembly.d.ts +7 -2
- package/dist/algo/candidateAssembly.js +81 -13
- package/dist/algo/capabilityCandidates.js +10 -0
- package/dist/algo/conversationSniffer.js +20 -7
- package/dist/algo/cycleEngine.d.ts +23 -2
- package/dist/algo/cycleEngine.js +236 -36
- package/dist/algo/exploration.d.ts +7 -0
- package/dist/algo/exploration.js +16 -3
- package/dist/algo/geneHealth.d.ts +2 -2
- package/dist/algo/geneHealth.js +5 -4
- package/dist/algo/geneSelection.d.ts +63 -5
- package/dist/algo/geneSelection.js +330 -30
- package/dist/algo/index.d.ts +2 -0
- package/dist/algo/index.js +2 -0
- package/dist/algo/orchestrator.d.ts +8 -1
- package/dist/algo/orchestrator.js +15 -3
- package/dist/algo/publishEligibility.d.ts +34 -0
- package/dist/algo/publishEligibility.js +52 -0
- package/dist/algo/ucb1.d.ts +53 -0
- package/dist/algo/ucb1.js +156 -0
- package/dist/assetstore/assetSidecarRecords.d.ts +7 -2
- package/dist/assetstore/assetSidecarRecords.js +238 -0
- package/dist/assetstore/assetSidecarRecovery.js +2 -2
- package/dist/assetstore/assetStoreHealth.d.ts +6 -0
- package/dist/assetstore/assetStoreHealth.js +56 -14
- package/dist/assetstore/assetStoreStorage.d.ts +1 -1
- package/dist/assetstore/assetStoreStorage.js +27 -9
- package/dist/assetstore/assetSyncLedger.d.ts +86 -0
- package/dist/assetstore/assetSyncLedger.js +683 -4
- package/dist/assetstore/index.d.ts +1 -0
- package/dist/assetstore/index.js +1 -0
- package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
- package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
- package/dist/assetstore/localJsonl.d.ts +4 -2
- package/dist/assetstore/localJsonl.js +91 -11
- package/dist/assetstore/provenance.d.ts +80 -4
- package/dist/assetstore/provenance.js +313 -5
- package/dist/assetstore/provider.d.ts +58 -1
- package/dist/assetstore/provider.js +97 -6
- package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
- package/dist/benchmark/antiGeneBenchmark.js +4 -3
- package/dist/benchmark/antiGeneRollout.d.ts +2 -0
- package/dist/benchmark/antiGeneRollout.js +4 -3
- package/dist/benchmark/index.d.ts +2 -1
- package/dist/benchmark/index.js +2 -1
- package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
- package/dist/benchmark/selectionFlatAbstention.js +481 -0
- package/dist/bootstrap/envFingerprint.d.ts +9 -0
- package/dist/bootstrap/envFingerprint.js +5 -0
- package/dist/bootstrap/index.d.ts +2 -1
- package/dist/bootstrap/index.js +2 -1
- package/dist/bootstrap/v1EnvCompat.d.ts +111 -0
- package/dist/bootstrap/v1EnvCompat.js +280 -0
- package/dist/events/eventArchive.d.ts +2 -0
- package/dist/events/eventArchive.js +13 -3
- package/dist/events/eventSchema.d.ts +7 -7
- package/dist/events/eventStore.d.ts +2 -0
- package/dist/events/eventStore.js +5 -1
- package/dist/events/ingest.d.ts +1 -0
- package/dist/events/ingest.js +1 -0
- package/dist/events/paths.d.ts +3 -1
- package/dist/events/paths.js +4 -0
- package/dist/events/public.d.ts +2 -2
- package/dist/events/public.js +2 -2
- package/dist/events/reports.d.ts +2 -0
- package/dist/events/reports.js +4 -0
- package/dist/exec/autoExec.d.ts +43 -2
- package/dist/exec/autoExec.js +76 -9
- package/dist/exec/autonomousCycle.d.ts +22 -4
- package/dist/exec/autonomousCycle.js +64 -13
- package/dist/exec/claudeBridge.d.ts +32 -7
- package/dist/exec/claudeBridge.js +281 -29
- package/dist/exec/prompt.js +5 -1
- package/dist/exec/runnerRegistry.d.ts +68 -26
- package/dist/exec/runnerRegistry.js +307 -72
- package/dist/exec/selfPr.js +1 -7
- package/dist/feedback/envelope.d.ts +61 -0
- package/dist/feedback/envelope.js +168 -0
- package/dist/feedback/index.d.ts +1 -0
- package/dist/feedback/index.js +1 -0
- package/dist/hooks/hooks.js +1 -0
- package/dist/hub/assetCallLog.d.ts +35 -1
- package/dist/hub/assetCallLog.js +124 -1
- package/dist/hub/bindings.d.ts +8 -1
- package/dist/hub/bindings.js +17 -6
- package/dist/hub/capability.d.ts +90 -3
- package/dist/hub/fake.d.ts +2 -2
- package/dist/hub/fake.js +1 -1
- package/dist/hub/questionGenerator.d.ts +5 -1
- package/dist/hub/questionGenerator.js +8 -6
- package/dist/hub/sanitize.js +3 -2
- package/dist/index.d.ts +3 -1
- package/dist/index.js +4 -1
- package/dist/mailbox/dispatch.d.ts +1 -1
- package/dist/mailbox/dispatch.js +22 -6
- package/dist/mailbox/envelope.d.ts +7 -1
- package/dist/mailbox/envelope.js +9 -2
- package/dist/mailbox/ipcServer.d.ts +10 -2
- package/dist/mailbox/ipcServer.js +163 -13
- package/dist/mailbox/store.d.ts +89 -3
- package/dist/mailbox/store.js +895 -41
- package/dist/ops/evolutionGraphProjection.d.ts +20 -0
- package/dist/ops/evolutionGraphProjection.js +315 -0
- package/dist/ops/index.d.ts +2 -1
- package/dist/ops/index.js +2 -1
- package/dist/ops/selfUpdate.d.ts +8 -0
- package/dist/ops/selfUpdate.js +24 -8
- package/dist/personality/schema.d.ts +16 -16
- package/dist/schema/evolutionGraph.d.ts +784 -0
- package/dist/schema/evolutionGraph.js +187 -0
- package/dist/schema/index.d.ts +1 -0
- package/dist/schema/index.js +1 -0
- package/dist/schema/signal.d.ts +3 -3
- package/dist/schema/signal.js +1 -1
- package/dist/signals/curriculum.d.ts +55 -0
- package/dist/signals/curriculum.js +202 -0
- package/dist/signals/cycleHistoryFromEvents.js +17 -8
- package/dist/signals/expand.d.ts +15 -1
- package/dist/signals/expand.js +169 -1
- package/dist/signals/extractor.d.ts +2 -2
- package/dist/signals/extractor.js +31 -6
- package/dist/signals/index.d.ts +3 -1
- package/dist/signals/index.js +3 -1
- package/dist/signals/metaSignals.d.ts +4 -0
- package/dist/signals/metaSignals.js +42 -0
- package/dist/signals/signalGate.js +1 -1
- package/dist/signals/taskDomain.d.ts +22 -0
- package/dist/signals/taskDomain.js +43 -0
- package/dist/strategy/constraintAblation.d.ts +64 -0
- package/dist/strategy/constraintAblation.js +2820 -0
- package/dist/strategy/constraintAblationPredicates.d.ts +31 -0
- package/dist/strategy/constraintAblationPredicates.js +339 -0
- package/dist/strategy/index.d.ts +2 -1
- package/dist/strategy/index.js +2 -1
- package/dist/trace/index.d.ts +3 -1
- package/dist/trace/index.js +3 -1
- package/dist/trace/learningTrace.d.ts +209 -0
- package/dist/trace/learningTrace.js +276 -0
- package/dist/trace/proxyTurns.d.ts +31 -0
- package/dist/trace/proxyTurns.js +137 -0
- package/dist/verify/sandboxRunner.d.ts +28 -0
- package/dist/verify/sandboxRunner.js +218 -19
- package/dist/verify/sandboxedValidation.d.ts +9 -0
- package/dist/verify/sandboxedValidation.js +113 -13
- package/dist/verify/validation.d.ts +11 -1
- package/dist/verify/validation.js +31 -0
- package/package.json +5 -2
package/dist/algo/cycleEngine.js
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { LineTooLargeError } from '../events/eventStore.js';
|
|
2
|
+
import { TERMINAL, canTransition, stageForEventType } from '../cycle/stateMachine.js';
|
|
2
3
|
import {} from './geneSelection.js';
|
|
3
4
|
import { detectPlateau } from './exploration.js';
|
|
4
5
|
import { epigeneticPenaltyForIds } from './epigenetics.js';
|
|
@@ -11,10 +12,122 @@ import { applySelectForRun, applyStatsUpdate, applyForcePivot } from '../persona
|
|
|
11
12
|
import { solidify } from './solidify.js';
|
|
12
13
|
import { buildEvolutionEvent } from './evolutionEvent.js';
|
|
13
14
|
import { cycleRecordsFromEvents } from '../signals/cycleHistoryFromEvents.js';
|
|
15
|
+
import { withoutTaskDomainSignals } from '../signals/taskDomain.js';
|
|
14
16
|
import { computeMetaSignals, deriveCycleHistory } from '../signals/metaSignals.js';
|
|
17
|
+
import { capabilityGapsFromSignals, curriculumOutcomesFromEvents, generateCurriculumSignals, normalizeCapabilityGaps, } from '../signals/curriculum.js';
|
|
15
18
|
import { resolveStrategy } from './strategyPresets.js';
|
|
16
19
|
import { classifyCycleFailure, } from './cycleFailureClassifier.js';
|
|
17
20
|
import { isDistilledGeneId } from './geneIntake.js';
|
|
21
|
+
import { deriveUcb1History } from './ucb1.js';
|
|
22
|
+
function persistedTerminalAfterLatestSelection(ingestor, cycleId) {
|
|
23
|
+
const events = ingestor.readAll();
|
|
24
|
+
let selectedSeq = -1;
|
|
25
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
26
|
+
const event = events[index];
|
|
27
|
+
if (event.type === 'decision.gene_selected' && event.payload['cycleId'] === cycleId) {
|
|
28
|
+
selectedSeq = event.seq;
|
|
29
|
+
break;
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
if (selectedSeq < 0)
|
|
33
|
+
return null;
|
|
34
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
35
|
+
const event = events[index];
|
|
36
|
+
if (event.seq <= selectedSeq)
|
|
37
|
+
break;
|
|
38
|
+
if (event.payload['cycleId'] !== cycleId)
|
|
39
|
+
continue;
|
|
40
|
+
const terminal = stageForEventType(event.type);
|
|
41
|
+
if (terminal && TERMINAL.has(terminal))
|
|
42
|
+
return terminal;
|
|
43
|
+
}
|
|
44
|
+
return null;
|
|
45
|
+
}
|
|
46
|
+
function selectionPolicyEventProjection(trace) {
|
|
47
|
+
return {
|
|
48
|
+
requested: trace.requested,
|
|
49
|
+
effective: trace.effective,
|
|
50
|
+
version: trace.selectionPolicyVersion,
|
|
51
|
+
rewardVersion: trace.rewardPolicyVersion,
|
|
52
|
+
...(trace.arm ? {
|
|
53
|
+
arm: {
|
|
54
|
+
id: trace.arm.armId,
|
|
55
|
+
pulls: trace.arm.pulls,
|
|
56
|
+
completed: trace.arm.completedPulls,
|
|
57
|
+
total: trace.arm.totalPulls,
|
|
58
|
+
mean: trace.arm.meanReward,
|
|
59
|
+
index: trace.arm.index,
|
|
60
|
+
},
|
|
61
|
+
} : {}),
|
|
62
|
+
...(trace.shadowDisagrees !== undefined ? { disagrees: trace.shadowDisagrees } : {}),
|
|
63
|
+
...(trace.fallbackReason ? { fallback: trace.fallbackReason } : {}),
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
function minimalSelectionPolicyEventProjection(trace) {
|
|
67
|
+
return {
|
|
68
|
+
requested: trace.requested,
|
|
69
|
+
effective: trace.effective,
|
|
70
|
+
version: trace.selectionPolicyVersion,
|
|
71
|
+
rewardVersion: trace.rewardPolicyVersion,
|
|
72
|
+
};
|
|
73
|
+
}
|
|
74
|
+
function selectionGuardEventProjection(trace) {
|
|
75
|
+
return {
|
|
76
|
+
mode: trace.mode,
|
|
77
|
+
version: trace.version,
|
|
78
|
+
status: trace.status,
|
|
79
|
+
...(trace.reason ? { reason: trace.reason } : {}),
|
|
80
|
+
...(trace.maxMatch !== undefined ? { maxMatch: trace.maxMatch } : {}),
|
|
81
|
+
...(trace.matchSpread !== undefined ? { spread: trace.matchSpread } : {}),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
function minimalSelectionGuardEventProjection(trace) {
|
|
85
|
+
return {
|
|
86
|
+
mode: trace.mode,
|
|
87
|
+
version: trace.version,
|
|
88
|
+
status: trace.status,
|
|
89
|
+
...(trace.reason ? { reason: trace.reason } : {}),
|
|
90
|
+
};
|
|
91
|
+
}
|
|
92
|
+
function selectionCandidateEventProjection(candidate) {
|
|
93
|
+
const { matchScore: _matchScore, ...persisted } = candidate;
|
|
94
|
+
return persisted;
|
|
95
|
+
}
|
|
96
|
+
async function ingestGeneSelectedWithTraceBudget(ingestor, event, policyTrace, guardTrace) {
|
|
97
|
+
const guard = guardTrace ? selectionGuardEventProjection(guardTrace) : undefined;
|
|
98
|
+
const minimalGuard = guardTrace ? minimalSelectionGuardEventProjection(guardTrace) : undefined;
|
|
99
|
+
const projections = [];
|
|
100
|
+
if (policyTrace) {
|
|
101
|
+
projections.push({ selectionPolicy: selectionPolicyEventProjection(policyTrace), ...(guard ? { selectionGuard: guard } : {}) });
|
|
102
|
+
projections.push({ selectionPolicy: minimalSelectionPolicyEventProjection(policyTrace), ...(guard ? { selectionGuard: guard } : {}) });
|
|
103
|
+
if (minimalGuard) {
|
|
104
|
+
projections.push({ selectionPolicy: minimalSelectionPolicyEventProjection(policyTrace), selectionGuard: minimalGuard });
|
|
105
|
+
projections.push({ selectionGuard: minimalGuard });
|
|
106
|
+
}
|
|
107
|
+
projections.push({ selectionPolicy: minimalSelectionPolicyEventProjection(policyTrace) });
|
|
108
|
+
}
|
|
109
|
+
else if (guard) {
|
|
110
|
+
projections.push({ selectionGuard: guard });
|
|
111
|
+
if (minimalGuard)
|
|
112
|
+
projections.push({ selectionGuard: minimalGuard });
|
|
113
|
+
}
|
|
114
|
+
projections.push({});
|
|
115
|
+
for (let index = 0; index < projections.length; index += 1) {
|
|
116
|
+
const projection = projections[index];
|
|
117
|
+
try {
|
|
118
|
+
await ingestor.ingest({
|
|
119
|
+
type: event.type,
|
|
120
|
+
human: event.human,
|
|
121
|
+
payload: { ...event.payload, ...projection },
|
|
122
|
+
});
|
|
123
|
+
return;
|
|
124
|
+
}
|
|
125
|
+
catch (error) {
|
|
126
|
+
if (!(error instanceof LineTooLargeError) || index === projections.length - 1)
|
|
127
|
+
throw error;
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
}
|
|
18
131
|
/** Read the trailing run of cycle outcomes (newest last) from the event log, for plateau detection. */
|
|
19
132
|
function recentCycleOutcomes(ingestor, window) {
|
|
20
133
|
let events;
|
|
@@ -106,7 +219,7 @@ function categoryForStrategy(category, strategy) {
|
|
|
106
219
|
* Reading from the same append-only log makes this fully replayable. ad-hoc (innovate) cycles have no gene to
|
|
107
220
|
* credit and are skipped.
|
|
108
221
|
*/
|
|
109
|
-
function recentConfidenceObservations(ingestor, window) {
|
|
222
|
+
function recentConfidenceObservations(ingestor, window, opts = {}) {
|
|
110
223
|
let events;
|
|
111
224
|
try {
|
|
112
225
|
events = ingestor.tail(window);
|
|
@@ -146,8 +259,9 @@ function recentConfidenceObservations(ingestor, window) {
|
|
|
146
259
|
// Older events predate the producedValue tag; when it is absent we treat the cycle as a real success, so we
|
|
147
260
|
// never retroactively reclassify history into a surprise ban.
|
|
148
261
|
const status = e.type === 'cycle.failed' ? 'failed' : p?.producedValue === false ? 'inert' : 'success';
|
|
262
|
+
const fingerprintSignals = opts.ignoreTaskDomain ? withoutTaskDomainSignals(signals) : signals;
|
|
149
263
|
out.push({
|
|
150
|
-
signalFingerprint: signalFingerprint(
|
|
264
|
+
signalFingerprint: signalFingerprint(fingerprintSignals),
|
|
151
265
|
geneId: gene,
|
|
152
266
|
status,
|
|
153
267
|
at: Number.isFinite(at) ? at : 0,
|
|
@@ -164,14 +278,44 @@ export class CycleEngine {
|
|
|
164
278
|
constructor(deps) {
|
|
165
279
|
this.deps = deps;
|
|
166
280
|
}
|
|
281
|
+
/**
|
|
282
|
+
* Add V1-compatible curriculum targets from V2's replayable event history. Public so the orchestrator can run
|
|
283
|
+
* it before candidate assembly; runCycle calls it again for direct callers. Set merging makes that idempotent.
|
|
284
|
+
*/
|
|
285
|
+
prepareCurriculumSignals(signals) {
|
|
286
|
+
let externalCapabilityGaps = [];
|
|
287
|
+
try {
|
|
288
|
+
externalCapabilityGaps = normalizeCapabilityGaps(this.deps.capabilityGaps?.() ?? []);
|
|
289
|
+
}
|
|
290
|
+
catch {
|
|
291
|
+
// An optional lifecycle-state reader must never suppress local curriculum generation.
|
|
292
|
+
}
|
|
293
|
+
try {
|
|
294
|
+
const localCapabilityGaps = capabilityGapsFromSignals(signals);
|
|
295
|
+
const curriculumSignals = generateCurriculumSignals({
|
|
296
|
+
outcomes: curriculumOutcomesFromEvents(this.deps.ingestor.readAll()),
|
|
297
|
+
// Preserve existing explicit local-signal precedence; Hub gaps fill the otherwise-missing V1 source.
|
|
298
|
+
capabilityGaps: normalizeCapabilityGaps([...localCapabilityGaps, ...externalCapabilityGaps]),
|
|
299
|
+
});
|
|
300
|
+
return { signals: mergeSignals(signals, curriculumSignals), curriculumSignals };
|
|
301
|
+
}
|
|
302
|
+
catch {
|
|
303
|
+
return { signals: [...signals], curriculumSignals: [] };
|
|
304
|
+
}
|
|
305
|
+
}
|
|
167
306
|
async runCycle(input) {
|
|
168
307
|
const { ingestor, store } = this.deps;
|
|
169
308
|
const now = this.deps.now();
|
|
170
309
|
const envKey = envFingerprintKey((this.deps.envFingerprint ?? captureEnvFingerprint)());
|
|
171
310
|
const reasons = [];
|
|
172
|
-
const baseSignals = input.signals;
|
|
311
|
+
const baseSignals = [...input.signals];
|
|
312
|
+
const preparedSignals = input.curriculumSignals === undefined
|
|
313
|
+
? this.prepareCurriculumSignals(input.signals)
|
|
314
|
+
: { signals: mergeSignals(input.signals, input.curriculumSignals), curriculumSignals: [...input.curriculumSignals] };
|
|
315
|
+
const selectionSignals = preparedSignals.signals;
|
|
316
|
+
const curriculumSignals = preparedSignals.curriculumSignals;
|
|
173
317
|
const metaSignals = historyMetaSignals(ingestor, 100);
|
|
174
|
-
const cycleSignals = mergeSignals(
|
|
318
|
+
const cycleSignals = mergeSignals(selectionSignals, metaSignals);
|
|
175
319
|
// PORT v1 #279 issue-reporter: classify cycle.failed payloads when the caller supplies host context. The
|
|
176
320
|
// post-solidify failed path adds V2-local context (current gene + blast radius) so hard-filtered local no-op
|
|
177
321
|
// genes are reachable without re-emitting the old ban_gene soft signal.
|
|
@@ -221,6 +365,7 @@ export class CycleEngine {
|
|
|
221
365
|
cycleId: input.cycleId,
|
|
222
366
|
signals: cycleSignals,
|
|
223
367
|
baseSignals,
|
|
368
|
+
...(curriculumSignals.length > 0 ? { curriculumSignals } : {}),
|
|
224
369
|
...(metaSignals.length > 0 ? { metaSignals, strategy: strategy.name } : {}),
|
|
225
370
|
},
|
|
226
371
|
});
|
|
@@ -250,7 +395,7 @@ export class CycleEngine {
|
|
|
250
395
|
// recentEvents 由事件日志的近期 outcome 派生, 让"失败连击触发变异"在环内真正生效.
|
|
251
396
|
const sel = await applySelectForRun(personalityDeps, {
|
|
252
397
|
driftEnabled: plateau.active,
|
|
253
|
-
signals:
|
|
398
|
+
signals: selectionSignals,
|
|
254
399
|
recentEvents: recentOutcomes.map((status) => ({ outcome: { status } })),
|
|
255
400
|
});
|
|
256
401
|
personalityForRun = sel.state;
|
|
@@ -263,8 +408,24 @@ export class CycleEngine {
|
|
|
263
408
|
return;
|
|
264
409
|
await applyStatsUpdate({ store: this.deps.personality, ingestor, cycleId: input.cycleId }, { personality: personalityForRun, outcome, score });
|
|
265
410
|
};
|
|
411
|
+
let ucb1History;
|
|
412
|
+
if (plateau.active && input.selectionPolicy && input.selectionPolicy !== 'engine-health') {
|
|
413
|
+
try {
|
|
414
|
+
ucb1History = deriveUcb1History(ingestor.readAllStrict());
|
|
415
|
+
}
|
|
416
|
+
catch {
|
|
417
|
+
// Active UCB1 fails safe to legacy drift; shadow records missing_history in its compact policy trace.
|
|
418
|
+
ucb1History = undefined;
|
|
419
|
+
}
|
|
420
|
+
}
|
|
266
421
|
const exploration = plateau.active
|
|
267
|
-
? {
|
|
422
|
+
? {
|
|
423
|
+
plateau,
|
|
424
|
+
driftEnabled: true,
|
|
425
|
+
totalAttempts: input.candidates.reduce((s, c) => s + (c.view?.total ?? 0), 0),
|
|
426
|
+
...(input.selectionPolicy ? { policy: input.selectionPolicy } : {}),
|
|
427
|
+
...(ucb1History ? { ucb1History } : {}),
|
|
428
|
+
}
|
|
268
429
|
: undefined;
|
|
269
430
|
// Epigenetic: penalize candidates that fail in THIS environment class (derived from per-env outcome history).
|
|
270
431
|
const geneOutcomes = recentGeneOutcomes(ingestor, 200);
|
|
@@ -272,7 +433,7 @@ export class CycleEngine {
|
|
|
272
433
|
// signal fingerprint. Derived from the same event log, applied as a soft fourth factor in selection.
|
|
273
434
|
const confidenceObs = recentConfidenceObservations(ingestor, 200);
|
|
274
435
|
const confidenceEdges = deriveConfidenceEdges(confidenceObs);
|
|
275
|
-
const
|
|
436
|
+
const currentConfidenceFingerprint = signalFingerprint(baseSignals);
|
|
276
437
|
// #195: drop genes stuck producing only inert (zero-work) cycles on THIS signal. A sole-matching do-nothing
|
|
277
438
|
// gene is otherwise re-selected every cycle (drift can't diversify a single candidate) and the failure-streak
|
|
278
439
|
// ban never fires because nothing "fails" — so it dominates --loop forever while producing no artifacts.
|
|
@@ -282,46 +443,85 @@ export class CycleEngine {
|
|
|
282
443
|
// cycles evict its inert run (or the earlier real success that exempts it) and the ban would flap. Recomputing
|
|
283
444
|
// it over INERT_BAN_OBSERVATION_WINDOW events (mirroring v1's memory-graph read horizon) keeps the trailing run
|
|
284
445
|
// and the successCount exemption alive across intervening cycles. tail() reads the full log either way (no I/O cost).
|
|
285
|
-
const inertBanObs = recentConfidenceObservations(ingestor, INERT_BAN_OBSERVATION_WINDOW);
|
|
286
|
-
const
|
|
446
|
+
const inertBanObs = recentConfidenceObservations(ingestor, INERT_BAN_OBSERVATION_WINDOW, { ignoreTaskDomain: true });
|
|
447
|
+
const inertBanFingerprint = signalFingerprint(withoutTaskDomainSignals(baseSignals));
|
|
448
|
+
const inertBanned = inertBannedGeneIds(deriveConfidenceEdges(inertBanObs), inertBanObs, inertBanFingerprint);
|
|
287
449
|
const candidates = input.candidates
|
|
288
450
|
.filter((c) => !isCandidateInSet(c, inertBanned))
|
|
289
451
|
.map((c) => {
|
|
290
452
|
const epi = epigeneticPenaltyForIds(candidateIds(c), envKey, geneOutcomes);
|
|
291
|
-
const conf = confidenceFor(confidenceEdges,
|
|
453
|
+
const conf = confidenceFor(confidenceEdges, currentConfidenceFingerprint, c.geneId, now);
|
|
292
454
|
const withEpi = epi > 0 ? { ...c, epigeneticPenalty: epi } : c;
|
|
293
455
|
return conf > 0 ? { ...withEpi, confidence: conf } : withEpi;
|
|
294
456
|
});
|
|
295
457
|
// #97 distilled-gene fallback pool: attach the env-scoped epigenetic penalty (so selection can hard-skip a
|
|
296
458
|
// suppressed fallback, v1 parity) and drop any inert-banned id (composes with #195). Selection uses these only
|
|
297
|
-
// when
|
|
459
|
+
// when normal selection has no reusable positive choice.
|
|
298
460
|
const distilledFallback = (input.distilledFallback ?? [])
|
|
299
461
|
.filter((c) => !isCandidateInSet(c, inertBanned))
|
|
300
462
|
.map((c) => {
|
|
301
463
|
const epi = epigeneticPenaltyForIds(candidateIds(c), envKey, geneOutcomes);
|
|
302
464
|
return epi > 0 ? { ...c, epigeneticPenalty: epi } : c;
|
|
303
465
|
});
|
|
304
|
-
const
|
|
305
|
-
await
|
|
466
|
+
const semanticCorpus = input.semanticCorpus?.filter((c) => !isCandidateInSet(c, inertBanned));
|
|
467
|
+
const decision = (await this.deps.selection.run({ signals: selectionSignals, candidates, ...(semanticCorpus ? { semanticCorpus } : {}), ...(input.disableSemanticIdf ? { disableSemanticIdf: true } : {}), floor: input.selectionFloor, ...(input.selectionGuard ? { selectionGuard: input.selectionGuard } : {}), ...(input.forcedGeneId !== undefined ? { forcedGeneId: input.forcedGeneId } : {}), ...(exploration ? { exploration } : {}), ...(distilledFallback.length > 0 ? { distilledFallback } : {}), ...(input.antiWarnings && input.antiWarnings.length > 0 ? { antiWarnings: input.antiWarnings } : {}), ...(input.memoryEvidence && input.memoryEvidence.length > 0 ? { memoryEvidence: input.memoryEvidence } : {}) }, { now, cycleId: input.cycleId, ...(this.deps.rng ? { rng: this.deps.rng } : {}) }));
|
|
468
|
+
const geneId = decision.selectedGeneId ?? 'ad-hoc';
|
|
469
|
+
const runPostSelectionStep = async (phase, step) => {
|
|
470
|
+
try {
|
|
471
|
+
return await step();
|
|
472
|
+
}
|
|
473
|
+
catch (error) {
|
|
474
|
+
let persistedTerminal = null;
|
|
475
|
+
try {
|
|
476
|
+
persistedTerminal = persistedTerminalAfterLatestSelection(ingestor, input.cycleId);
|
|
477
|
+
}
|
|
478
|
+
catch {
|
|
479
|
+
// Readback is best-effort; compensation below remains the fallback when the log is unavailable.
|
|
480
|
+
}
|
|
481
|
+
if (persistedTerminal && !TERMINAL.has(stage) && canTransition(stage, persistedTerminal)) {
|
|
482
|
+
advance(persistedTerminal);
|
|
483
|
+
}
|
|
484
|
+
if (!persistedTerminal && !TERMINAL.has(stage)) {
|
|
485
|
+
try {
|
|
486
|
+
await ingestor.ingest({
|
|
487
|
+
type: 'cycle.failed',
|
|
488
|
+
human: { title: `cycle ${input.cycleId} selection 后异常` },
|
|
489
|
+
payload: { cycleId: input.cycleId, reason: 'post_selection_error', phase, gene: geneId, env: envKey },
|
|
490
|
+
});
|
|
491
|
+
advance('failed');
|
|
492
|
+
try {
|
|
493
|
+
await recordPersonalityOutcome('failed', null);
|
|
494
|
+
}
|
|
495
|
+
catch {
|
|
496
|
+
// Personality is soft state; never let its writeback replace the original failure.
|
|
497
|
+
}
|
|
498
|
+
}
|
|
499
|
+
catch {
|
|
500
|
+
// The event store itself may be unavailable. Preserve and rethrow the original step failure.
|
|
501
|
+
}
|
|
502
|
+
}
|
|
503
|
+
throw error;
|
|
504
|
+
}
|
|
505
|
+
};
|
|
506
|
+
await runPostSelectionStep('decision_event', () => ingestGeneSelectedWithTraceBudget(ingestor, {
|
|
306
507
|
type: 'decision.gene_selected',
|
|
307
508
|
human: { title: `选 gene ${decision.selectedGeneId ?? '(innovate)'}`, why: decision.candidates.map((c) => `${c.geneId}:${c.score.toFixed(3)}`).join(', ') || '无候选→innovate' },
|
|
308
|
-
payload: { cycleId: input.cycleId, selectedGeneId: decision.selectedGeneId, ...(decision.selectedAssetId ? { selectedAssetId: decision.selectedAssetId } : {}), ...(decision.selectedReason ? { selectedReason: decision.selectedReason } : {}), candidates: decision.candidates, ...(decision.antiWarnings && decision.antiWarnings.length > 0 ? { antiWarnings: decision.antiWarnings } : {}), ...(decision.memoryEvidence && decision.memoryEvidence.length > 0 ? { memoryEvidence: decision.memoryEvidence } : {}), weightsVersion: decision.weightsVersion, strategy: decision.strategyName, strategyPreset: strategy.name, ...(plateau.active ? { plateau } : {}), ...(inertBanned.size > 0 ? { inertBanned: [...inertBanned] } : {}) },
|
|
309
|
-
});
|
|
509
|
+
payload: { cycleId: input.cycleId, selectedGeneId: decision.selectedGeneId, ...(decision.selectedAssetId ? { selectedAssetId: decision.selectedAssetId } : {}), ...(decision.selectedReason ? { selectedReason: decision.selectedReason } : {}), candidates: decision.candidates.map(selectionCandidateEventProjection), ...(decision.antiWarnings && decision.antiWarnings.length > 0 ? { antiWarnings: decision.antiWarnings } : {}), ...(decision.memoryEvidence && decision.memoryEvidence.length > 0 ? { memoryEvidence: decision.memoryEvidence } : {}), weightsVersion: decision.weightsVersion, ...(decision.semanticProfileVersion ? { semanticProfileVersion: decision.semanticProfileVersion } : {}), ...(decision.semanticDocumentCount !== undefined ? { semanticDocumentCount: decision.semanticDocumentCount } : {}), strategy: decision.strategyName, strategyPreset: strategy.name, ...(plateau.active ? { plateau } : {}), ...(inertBanned.size > 0 ? { inertBanned: [...inertBanned] } : {}) },
|
|
510
|
+
}, decision.selectionPolicy, decision.selectionGuard));
|
|
310
511
|
advance('gene_selected');
|
|
311
|
-
const geneId = decision.selectedGeneId ?? 'ad-hoc';
|
|
312
512
|
// 变异
|
|
313
|
-
const builtMutation = buildMutation({ decision, category: cycleCategory, signals: cycleSignals, target: input.target, expectedEffect: input.expectedEffect });
|
|
513
|
+
const builtMutation = await runPostSelectionStep('mutation_build', () => buildMutation({ decision, category: cycleCategory, signals: cycleSignals, target: input.target, expectedEffect: input.expectedEffect }));
|
|
314
514
|
// 人格风险闸(用途②): 高危人格禁 innovate / 未达阈值的 high-risk 降级. 未注入人格 ⇒ 原样放行.
|
|
315
515
|
// 用本轮 applySelectForRun 选出的状态(personalityForRun), 而非再读一次 store.currentState() ——
|
|
316
516
|
// 保证"喂 prompt 的风格 / 过风险闸 / 回写统计"三者用的是同一个当轮人格.
|
|
317
517
|
let mutation = builtMutation;
|
|
318
518
|
if (this.deps.personality && personalityForRun) {
|
|
319
|
-
await emitPersonalitySelected(ingestor, { cycleId: input.cycleId, personality: personalityForRun, known: personalityKnown });
|
|
320
|
-
const gate = gatePersonalityRisk(builtMutation, personalityForRun);
|
|
519
|
+
await runPostSelectionStep('personality_selected', () => emitPersonalitySelected(ingestor, { cycleId: input.cycleId, personality: personalityForRun, known: personalityKnown }));
|
|
520
|
+
const gate = await runPostSelectionStep('personality_risk_gate', () => gatePersonalityRisk(builtMutation, personalityForRun));
|
|
321
521
|
mutation = gate.mutation;
|
|
322
|
-
await emitPersonalityRiskGated(ingestor, { cycleId: input.cycleId, personality: personalityForRun, gate });
|
|
522
|
+
await runPostSelectionStep('personality_risk_gate', () => emitPersonalityRiskGated(ingestor, { cycleId: input.cycleId, personality: personalityForRun, gate }));
|
|
323
523
|
}
|
|
324
|
-
await ingestor.ingest({ type: 'mutation.built', human: { title: `变异 ${mutation.category} → ${mutation.target}` }, payload: { cycleId: input.cycleId, mutationId: mutation.id, risk: mutation.risk_level, category: mutation.category, strategyPreset: strategy.name } });
|
|
524
|
+
await runPostSelectionStep('mutation_event', () => ingestor.ingest({ type: 'mutation.built', human: { title: `变异 ${mutation.category} → ${mutation.target}` }, payload: { cycleId: input.cycleId, mutationId: mutation.id, risk: mutation.risk_level, category: mutation.category, strategyPreset: strategy.name } }));
|
|
325
525
|
advance('mutation_built');
|
|
326
526
|
// 执行(agent/runtime)
|
|
327
527
|
let exec;
|
|
@@ -332,7 +532,7 @@ export class CycleEngine {
|
|
|
332
532
|
const msg = e instanceof Error ? e.message : String(e);
|
|
333
533
|
// execute threw before returning a transcript; the error text IS the host context (a provider error that
|
|
334
534
|
// surfaces as a thrown exception still classifies as host_provider_error). Pass it as the sessionLog.
|
|
335
|
-
await ingestor.ingest({ type: 'cycle.failed', human: { title: `cycle ${input.cycleId} 执行抛错` }, payload: { cycleId: input.cycleId, error: msg, gene: geneId, env: envKey, ...failureClassPayload({ geneId }, msg) } });
|
|
535
|
+
await runPostSelectionStep('terminal_event', () => ingestor.ingest({ type: 'cycle.failed', human: { title: `cycle ${input.cycleId} 执行抛错` }, payload: { cycleId: input.cycleId, error: msg, gene: geneId, env: envKey, ...failureClassPayload({ geneId }, msg) } }));
|
|
336
536
|
advance('failed');
|
|
337
537
|
await recordPersonalityOutcome('failed', null);
|
|
338
538
|
return { cycleId: input.cycleId, triggered: true, finalStage: stage, producedValue: false, decision, mutation, reasons: [...reasons, `执行异常: ${msg}`] };
|
|
@@ -356,7 +556,7 @@ export class CycleEngine {
|
|
|
356
556
|
}
|
|
357
557
|
if (!permit.ok) {
|
|
358
558
|
const reason = `solidify permit denied: ${permit.reason}`;
|
|
359
|
-
await ingestor.ingest({
|
|
559
|
+
await runPostSelectionStep('terminal_event', () => ingestor.ingest({
|
|
360
560
|
type: 'cycle.failed',
|
|
361
561
|
human: { title: `cycle ${input.cycleId} permit denied` },
|
|
362
562
|
payload: {
|
|
@@ -367,30 +567,30 @@ export class CycleEngine {
|
|
|
367
567
|
...executionMetadata(exec),
|
|
368
568
|
...failureClassPayload({ geneId }),
|
|
369
569
|
},
|
|
370
|
-
});
|
|
570
|
+
}));
|
|
371
571
|
advance('failed');
|
|
372
572
|
await recordPersonalityOutcome('failed', null);
|
|
373
573
|
return { cycleId: input.cycleId, triggered: true, finalStage: stage, producedValue: false, decision, mutation, ...executionMetadata(exec), reasons: [...reasons, reason] };
|
|
374
574
|
}
|
|
375
575
|
}
|
|
376
576
|
// solidify → Capsule
|
|
377
|
-
const sol = solidify({
|
|
577
|
+
const sol = await runPostSelectionStep('solidify', () => solidify({
|
|
378
578
|
geneId, trigger: cycleSignals, summary: input.summary, confidence: input.confidence,
|
|
379
579
|
outcome: exec.outcome, proofOfWork: exec.proofOfWork, strongEvidence: exec.strongEvidence,
|
|
380
|
-
});
|
|
580
|
+
}));
|
|
381
581
|
reasons.push(...sol.reasons);
|
|
382
|
-
await store.put(sol.capsule);
|
|
383
|
-
await ingestor.ingest({ type: 'capsule.produced', human: { title: `Capsule ${sol.resolutionStatus}` }, payload: { cycleId: input.cycleId, capsuleId: sol.capsule.asset_id, resolutionStatus: sol.resolutionStatus, producedValue: sol.producedValue, blastRadius: sol.capsule.blast_radius } });
|
|
582
|
+
await runPostSelectionStep('capsule_store', () => store.put(sol.capsule));
|
|
583
|
+
await runPostSelectionStep('capsule_event', () => ingestor.ingest({ type: 'capsule.produced', human: { title: `Capsule ${sol.resolutionStatus}` }, payload: { cycleId: input.cycleId, capsuleId: sol.capsule.asset_id, resolutionStatus: sol.resolutionStatus, producedValue: sol.producedValue, blastRadius: sol.capsule.blast_radius } }));
|
|
384
584
|
// EvolutionEvent(capsule_id 引 Capsule; outcome 真值在此)
|
|
385
|
-
const event = buildEvolutionEvent({
|
|
585
|
+
const event = await runPostSelectionStep('evolution_event_build', () => buildEvolutionEvent({
|
|
386
586
|
intent: cycleCategory, signals: cycleSignals, genesUsed: decision.selectedGeneId ? [decision.selectedGeneId] : [],
|
|
387
587
|
mutationId: mutation.id, blastRadius: sol.capsule.blast_radius, outcome: exec.outcome,
|
|
388
588
|
capsuleId: sol.capsule.asset_id, sourceType: decision.selectedGeneId ? 'reused' : 'generated',
|
|
389
|
-
});
|
|
390
|
-
await store.put(event);
|
|
391
|
-
await ingestor.ingest({ type: 'evolution_event.projected', human: { title: `世代记录 ${exec.outcome.status}` }, payload: { cycleId: input.cycleId, eventId: event.asset_id, capsuleId: sol.capsule.asset_id, outcome: exec.outcome } });
|
|
589
|
+
}));
|
|
590
|
+
await runPostSelectionStep('evolution_event_store', () => store.put(event));
|
|
591
|
+
await runPostSelectionStep('evolution_event_projection', () => ingestor.ingest({ type: 'evolution_event.projected', human: { title: `世代记录 ${exec.outcome.status}` }, payload: { cycleId: input.cycleId, eventId: event.asset_id, capsuleId: sol.capsule.asset_id, outcome: exec.outcome } }));
|
|
392
592
|
if (exec.outcome.status === 'failed') {
|
|
393
|
-
await ingestor.ingest({
|
|
593
|
+
await runPostSelectionStep('terminal_event', () => ingestor.ingest({
|
|
394
594
|
type: 'cycle.failed',
|
|
395
595
|
human: { title: `cycle ${input.cycleId} 失败` },
|
|
396
596
|
payload: {
|
|
@@ -411,11 +611,11 @@ export class CycleEngine {
|
|
|
411
611
|
: {}),
|
|
412
612
|
}, exec.sessionLog),
|
|
413
613
|
},
|
|
414
|
-
});
|
|
614
|
+
}));
|
|
415
615
|
advance('failed');
|
|
416
616
|
}
|
|
417
617
|
else {
|
|
418
|
-
await ingestor.ingest({ type: 'cycle.solidified', human: { title: `cycle ${input.cycleId} 固化` }, payload: { cycleId: input.cycleId, capsuleId: sol.capsule.asset_id, outcome: exec.outcome, gene: geneId, env: envKey, producedValue: sol.producedValue } });
|
|
618
|
+
await runPostSelectionStep('terminal_event', () => ingestor.ingest({ type: 'cycle.solidified', human: { title: `cycle ${input.cycleId} 固化` }, payload: { cycleId: input.cycleId, capsuleId: sol.capsule.asset_id, outcome: exec.outcome, gene: geneId, env: envKey, producedValue: sol.producedValue } }));
|
|
419
619
|
advance('solidified');
|
|
420
620
|
}
|
|
421
621
|
// 人格统计回写(用途②): 用本轮真实 outcome/score 回写当轮人格桶, 供下一轮自然选择靠拢. 未注入 ⇒ no-op.
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import type { SelectionPolicy, Ucb1History } from './ucb1.js';
|
|
1
2
|
export type PlateauSeverity = 'suggested' | 'required';
|
|
2
3
|
export interface PlateauState {
|
|
3
4
|
active: boolean;
|
|
@@ -6,12 +7,16 @@ export interface PlateauState {
|
|
|
6
7
|
}
|
|
7
8
|
/** Exploration control fed into selection. Absent → pure deterministic top-score selection. */
|
|
8
9
|
export interface ExplorationInput {
|
|
10
|
+
/** Selection policy requested by the composition layer. Default preserves legacy engine-health + drift. */
|
|
11
|
+
policy?: SelectionPolicy;
|
|
9
12
|
/** Force drift on even without a plateau (e.g. an explicit explore policy). */
|
|
10
13
|
driftEnabled?: boolean;
|
|
11
14
|
/** Plateau detected upstream; an active plateau forces drift intensity up. */
|
|
12
15
|
plateau?: PlateauState;
|
|
13
16
|
/** Total attempts across the candidate pool (drives the maturity decay of the drift offset). */
|
|
14
17
|
totalAttempts?: number;
|
|
18
|
+
/** Event-log-derived state for UCB1. Undefined means fail-safe to legacy drift. */
|
|
19
|
+
ucb1History?: Ucb1History;
|
|
15
20
|
}
|
|
16
21
|
/**
|
|
17
22
|
* Adaptive drift intensity (ported v1): 1/sqrt(Ne) + an offset that decays from 0.3 → 0.02 as the pool
|
|
@@ -25,6 +30,8 @@ export interface DriftDecision {
|
|
|
25
30
|
index: number;
|
|
26
31
|
intensity: number;
|
|
27
32
|
}
|
|
33
|
+
/** Deterministic top-N window shared by legacy random drift and UCB1. */
|
|
34
|
+
export declare function explorationWindowSize(eligibleCount: number, exp: ExplorationInput | undefined): number;
|
|
28
35
|
/**
|
|
29
36
|
* Pick which ranked candidate to take among `eligibleCount` above-floor candidates.
|
|
30
37
|
* No exploration → deterministic top (index 0). With exploration → compute intensity (an active plateau
|
package/dist/algo/exploration.js
CHANGED
|
@@ -15,8 +15,9 @@ const PLATEAU_FORCE = 10;
|
|
|
15
15
|
export function computeDriftIntensity(ne, totalAttempts = 0) {
|
|
16
16
|
if (!Number.isFinite(ne) || ne <= 1)
|
|
17
17
|
return 0.7;
|
|
18
|
+
const attempts = Number.isFinite(totalAttempts) ? Math.max(0, totalAttempts) : 0;
|
|
18
19
|
const maturityThreshold = ne * MATURITY_ATTEMPTS_PER_GENE;
|
|
19
|
-
const maturity = maturityThreshold > 0 ? Math.min(1,
|
|
20
|
+
const maturity = maturityThreshold > 0 ? Math.min(1, attempts / maturityThreshold) : 0;
|
|
20
21
|
const offset = DRIFT_OFFSET_MAX - (DRIFT_OFFSET_MAX - DRIFT_OFFSET_MIN) * maturity;
|
|
21
22
|
return Math.min(1, 1 / Math.sqrt(ne) + offset);
|
|
22
23
|
}
|
|
@@ -34,6 +35,18 @@ export function detectPlateau(recentOutcomes) {
|
|
|
34
35
|
return { active: true, severity: 'suggested', count };
|
|
35
36
|
return { active: false, severity: 'suggested', count };
|
|
36
37
|
}
|
|
38
|
+
function explorationActive(exp) {
|
|
39
|
+
return Boolean(exp && (exp.driftEnabled || exp.plateau?.active));
|
|
40
|
+
}
|
|
41
|
+
/** Deterministic top-N window shared by legacy random drift and UCB1. */
|
|
42
|
+
export function explorationWindowSize(eligibleCount, exp) {
|
|
43
|
+
if (!explorationActive(exp) || eligibleCount <= 1)
|
|
44
|
+
return Math.min(1, Math.max(0, eligibleCount));
|
|
45
|
+
let intensity = computeDriftIntensity(eligibleCount, exp.totalAttempts ?? 0);
|
|
46
|
+
if (exp.plateau?.active)
|
|
47
|
+
intensity = Math.max(intensity, exp.plateau.severity === 'required' ? 1.0 : 0.7);
|
|
48
|
+
return Math.min(eligibleCount, Math.max(2, Math.ceil(eligibleCount * intensity)));
|
|
49
|
+
}
|
|
37
50
|
/**
|
|
38
51
|
* Pick which ranked candidate to take among `eligibleCount` above-floor candidates.
|
|
39
52
|
* No exploration → deterministic top (index 0). With exploration → compute intensity (an active plateau
|
|
@@ -41,7 +54,7 @@ export function detectPlateau(recentOutcomes) {
|
|
|
41
54
|
* with intensity). rng() is called at most twice; pass a seeded rng for deterministic tests.
|
|
42
55
|
*/
|
|
43
56
|
export function driftSelect(eligibleCount, exp, rng) {
|
|
44
|
-
if (!
|
|
57
|
+
if (!explorationActive(exp) || eligibleCount <= 1) {
|
|
45
58
|
return { driftMode: 'deterministic', index: 0, intensity: 0 };
|
|
46
59
|
}
|
|
47
60
|
let intensity = computeDriftIntensity(eligibleCount, exp.totalAttempts ?? 0);
|
|
@@ -49,7 +62,7 @@ export function driftSelect(eligibleCount, exp, rng) {
|
|
|
49
62
|
intensity = Math.max(intensity, exp.plateau.severity === 'required' ? 1.0 : 0.7);
|
|
50
63
|
if (rng() >= intensity)
|
|
51
64
|
return { driftMode: 'deterministic', index: 0, intensity };
|
|
52
|
-
const topN =
|
|
65
|
+
const topN = explorationWindowSize(eligibleCount, exp);
|
|
53
66
|
const index = Math.min(topN - 1, Math.max(0, Math.floor(rng() * topN)));
|
|
54
67
|
return { driftMode: exp.plateau?.active ? 'plateau_drift' : 'drift', index, intensity };
|
|
55
68
|
}
|
|
@@ -12,10 +12,10 @@ export interface GeneHealthWeights {
|
|
|
12
12
|
reuse: number;
|
|
13
13
|
antiPattern: number;
|
|
14
14
|
}
|
|
15
|
-
export declare const HEALTH_WEIGHTS_VERSION = "gh-
|
|
15
|
+
export declare const HEALTH_WEIGHTS_VERSION = "gh-2";
|
|
16
16
|
export declare const DEFAULT_HEALTH_WEIGHTS: GeneHealthWeights;
|
|
17
17
|
/**
|
|
18
|
-
* gene 健康分 = w1
|
|
18
|
+
* gene 健康分 = successRate·(w1 + w2·reuse归一) − w3·antiPattern密度.
|
|
19
19
|
* 输入 = M3-6 聚合视图(不内联 learning_history) + anti_patterns 数 + 复用计数.
|
|
20
20
|
*/
|
|
21
21
|
export declare function geneHealthScore(view: GeneLearningView, opts?: {
|
package/dist/algo/geneHealth.js
CHANGED
|
@@ -1,16 +1,17 @@
|
|
|
1
|
-
export const HEALTH_WEIGHTS_VERSION = 'gh-
|
|
2
|
-
export const DEFAULT_HEALTH_WEIGHTS = { successRate: 0.6, reuse: 0.
|
|
1
|
+
export const HEALTH_WEIGHTS_VERSION = 'gh-2';
|
|
2
|
+
export const DEFAULT_HEALTH_WEIGHTS = { successRate: 0.6, reuse: 0.1, antiPattern: 0.4 };
|
|
3
3
|
/** reuseCount 归一(对数压缩, 复用越多分越高但边际递减). */
|
|
4
4
|
function reuseScore(count) {
|
|
5
5
|
return count <= 0 ? 0 : Math.min(1, Math.log10(count + 1) / 2); // 100 次≈封顶
|
|
6
6
|
}
|
|
7
7
|
/**
|
|
8
|
-
* gene 健康分 = w1
|
|
8
|
+
* gene 健康分 = successRate·(w1 + w2·reuse归一) − w3·antiPattern密度.
|
|
9
9
|
* 输入 = M3-6 聚合视图(不内联 learning_history) + anti_patterns 数 + 复用计数.
|
|
10
10
|
*/
|
|
11
11
|
export function geneHealthScore(view, opts = {}, w = DEFAULT_HEALTH_WEIGHTS) {
|
|
12
12
|
const reuseCount = opts.reuseCount ?? view.total;
|
|
13
13
|
const antiPatternPenalty = Math.min(1, (opts.antiPatternCount ?? 0) / 5); // 5+ anti-pattern 封顶惩罚
|
|
14
|
-
const
|
|
14
|
+
const confidenceAdjustedSuccess = view.successRate * (w.successRate + w.reuse * reuseScore(reuseCount));
|
|
15
|
+
const score = confidenceAdjustedSuccess - w.antiPattern * antiPatternPenalty;
|
|
15
16
|
return { geneId: view.geneId, successRate: view.successRate, reuseCount, antiPatternPenalty, score };
|
|
16
17
|
}
|