@evomap/evolver-core 2.0.0-beta.19 → 2.0.0-beta.22
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/assets/gep/genes.jsonl +5 -5
- package/dist/algo/bans.d.ts +10 -1
- package/dist/algo/bans.js +56 -6
- package/dist/algo/candidateAssembly.d.ts +25 -2
- package/dist/algo/candidateAssembly.js +143 -20
- package/dist/algo/capabilityCandidates.js +10 -0
- package/dist/algo/cycleEngine.d.ts +40 -5
- package/dist/algo/cycleEngine.js +277 -37
- package/dist/algo/evolutionEvent.d.ts +26 -0
- package/dist/algo/evolutionEvent.js +32 -0
- package/dist/algo/exploration.d.ts +7 -0
- package/dist/algo/exploration.js +16 -3
- package/dist/algo/geneHealth.d.ts +34 -1
- package/dist/algo/geneHealth.js +42 -0
- package/dist/algo/geneIntake.d.ts +46 -1
- package/dist/algo/geneIntake.js +121 -10
- package/dist/algo/geneSelection.d.ts +115 -6
- package/dist/algo/geneSelection.js +459 -32
- package/dist/algo/index.d.ts +4 -1
- package/dist/algo/index.js +4 -1
- package/dist/algo/kautoProjection.d.ts +41 -0
- package/dist/algo/kautoProjection.js +95 -0
- package/dist/algo/kautoValidator.d.ts +68 -0
- package/dist/algo/kautoValidator.js +256 -0
- package/dist/algo/orchestrator.d.ts +14 -1
- package/dist/algo/orchestrator.js +9 -2
- package/dist/algo/solidify.d.ts +11 -2
- package/dist/algo/solidify.js +37 -7
- package/dist/algo/ucb1.d.ts +53 -0
- package/dist/algo/ucb1.js +156 -0
- package/dist/assetrepair/hubRejection.d.ts +12 -0
- package/dist/assetrepair/hubRejection.js +109 -0
- package/dist/assetrepair/index.d.ts +2 -0
- package/dist/assetrepair/index.js +2 -0
- package/dist/assetrepair/repair.d.ts +33 -0
- package/dist/assetrepair/repair.js +155 -0
- package/dist/assetstore/assetSidecarRecords.js +5 -1
- package/dist/assetstore/assetStoreHealth.d.ts +2 -3
- package/dist/assetstore/assetStoreHealth.js +4 -10
- package/dist/assetstore/foreignJsonlSource.d.ts +48 -0
- package/dist/assetstore/foreignJsonlSource.js +150 -0
- package/dist/assetstore/index.d.ts +3 -0
- package/dist/assetstore/index.js +3 -0
- package/dist/assetstore/learningHistory.js +3 -3
- package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
- package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
- package/dist/assetstore/localJsonl.d.ts +8 -0
- package/dist/assetstore/localJsonl.js +128 -3
- package/dist/assetstore/provenance.d.ts +38 -3
- package/dist/assetstore/provenance.js +103 -45
- package/dist/assetstore/provider.d.ts +4 -0
- package/dist/assetstore/reviewFilter.d.ts +19 -1
- package/dist/assetstore/reviewFilter.js +36 -0
- package/dist/assetstore/unionReadStore.d.ts +25 -0
- package/dist/assetstore/unionReadStore.js +119 -0
- 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/lifecycleBootstrap.d.ts +111 -0
- package/dist/bootstrap/lifecycleBootstrap.js +433 -0
- package/dist/bootstrap/v1EnvCompat.d.ts +3 -0
- package/dist/bootstrap/v1EnvCompat.js +48 -1
- 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 +2 -1
- package/dist/events/ingest.js +6 -0
- package/dist/exec/autoExec.d.ts +30 -3
- package/dist/exec/autoExec.js +294 -29
- package/dist/exec/autonomousCycle.d.ts +7 -0
- package/dist/exec/autonomousCycle.js +2 -0
- package/dist/exec/claudeBridge.d.ts +28 -6
- package/dist/exec/claudeBridge.js +411 -258
- package/dist/exec/executionBinding.d.ts +414 -0
- package/dist/exec/executionBinding.js +588 -0
- package/dist/exec/index.d.ts +1 -0
- package/dist/exec/index.js +1 -0
- package/dist/exec/proofOfWork.d.ts +1 -1
- package/dist/exec/proofOfWork.js +2 -2
- package/dist/exec/runnerRegistry.d.ts +5 -0
- package/dist/exec/runnerRegistry.js +54 -14
- package/dist/hub/capability.d.ts +40 -1
- package/dist/hub/conversationDistiller.d.ts +19 -0
- package/dist/hub/conversationDistiller.js +115 -37
- package/dist/hub/fake.d.ts +1 -0
- package/dist/hub/fake.js +1 -0
- package/dist/hub/index.d.ts +1 -0
- package/dist/hub/index.js +1 -0
- package/dist/hub/recipeCompose.d.ts +27 -0
- package/dist/hub/recipeCompose.js +90 -0
- package/dist/hub/sanitize.js +119 -5
- package/dist/index.d.ts +2 -0
- package/dist/index.js +2 -0
- package/dist/mailbox/ipcServer.d.ts +2 -0
- package/dist/mailbox/ipcServer.js +20 -0
- package/dist/mailbox/store.d.ts +52 -2
- package/dist/mailbox/store.js +546 -81
- package/dist/modelCompatibility.d.ts +164 -0
- package/dist/modelCompatibility.js +309 -0
- package/dist/observers/valueDigestObserver.d.ts +9 -0
- package/dist/observers/valueDigestObserver.js +35 -2
- package/dist/ops/cleanup.js +1 -1
- 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/ops/valueOutreach.d.ts +3 -1
- package/dist/ops/valueOutreach.js +5 -1
- package/dist/personality/schema.d.ts +18 -18
- 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/proofOfWork.d.ts +125 -6
- package/dist/schema/proofOfWork.js +102 -4
- package/dist/shadow/shadowHub.js +1 -0
- package/dist/signals/expand.d.ts +15 -1
- package/dist/signals/expand.js +148 -0
- package/dist/signals/index.d.ts +3 -1
- package/dist/signals/index.js +3 -1
- package/dist/signals/scopeVocabulary.d.ts +75 -0
- package/dist/signals/scopeVocabulary.js +91 -0
- package/dist/signals/taskDomain.d.ts +22 -0
- package/dist/signals/taskDomain.js +43 -0
- package/dist/trace/learningTrace.d.ts +7 -0
- package/dist/trace/learningTrace.js +22 -0
- package/dist/trace/trajectory.d.ts +8 -0
- package/dist/trace/trajectory.js +14 -2
- package/dist/util/fileLock.d.ts +92 -5
- package/dist/util/fileLock.js +809 -70
- package/dist/util/index.d.ts +2 -1
- package/dist/util/index.js +1 -1
- package/dist/verify/index.d.ts +2 -1
- package/dist/verify/index.js +1 -1
- package/dist/verify/sandboxRunner.d.ts +2 -0
- package/dist/verify/sandboxRunner.js +154 -10
- package/dist/verify/sandboxedValidation.d.ts +7 -2
- package/dist/verify/sandboxedValidation.js +102 -24
- package/dist/verify/validation.d.ts +24 -3
- package/dist/verify/validation.js +141 -24
- package/dist/wire/geneHints.d.ts +88 -16
- package/dist/wire/geneHints.js +124 -15
- package/dist/wire/index.d.ts +8 -3
- package/dist/wire/index.js +2 -2
- package/dist/wire/schemaGate.d.ts +21 -0
- package/dist/wire/schemaGate.js +119 -10
- package/package.json +5 -4
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,11 +12,126 @@ 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';
|
|
15
17
|
import { capabilityGapsFromSignals, curriculumOutcomesFromEvents, generateCurriculumSignals, normalizeCapabilityGaps, } from '../signals/curriculum.js';
|
|
16
18
|
import { resolveStrategy } from './strategyPresets.js';
|
|
17
19
|
import { classifyCycleFailure, } from './cycleFailureClassifier.js';
|
|
18
20
|
import { isDistilledGeneId } from './geneIntake.js';
|
|
21
|
+
import { deriveUcb1History } from './ucb1.js';
|
|
22
|
+
function persistedTerminalAfterLatestSelection(ingestor, cycleId, attemptStartedSeq) {
|
|
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.seq <= attemptStartedSeq)
|
|
28
|
+
break;
|
|
29
|
+
if (event.type === 'decision.gene_selected' && event.payload['cycleId'] === cycleId) {
|
|
30
|
+
selectedSeq = event.seq;
|
|
31
|
+
break;
|
|
32
|
+
}
|
|
33
|
+
}
|
|
34
|
+
if (selectedSeq < 0)
|
|
35
|
+
return null;
|
|
36
|
+
for (let index = events.length - 1; index >= 0; index -= 1) {
|
|
37
|
+
const event = events[index];
|
|
38
|
+
if (event.seq <= selectedSeq)
|
|
39
|
+
break;
|
|
40
|
+
if (event.payload['cycleId'] !== cycleId)
|
|
41
|
+
continue;
|
|
42
|
+
const terminal = stageForEventType(event.type);
|
|
43
|
+
if (terminal && TERMINAL.has(terminal))
|
|
44
|
+
return terminal;
|
|
45
|
+
}
|
|
46
|
+
return null;
|
|
47
|
+
}
|
|
48
|
+
function selectionPolicyEventProjection(trace) {
|
|
49
|
+
return {
|
|
50
|
+
requested: trace.requested,
|
|
51
|
+
effective: trace.effective,
|
|
52
|
+
version: trace.selectionPolicyVersion,
|
|
53
|
+
rewardVersion: trace.rewardPolicyVersion,
|
|
54
|
+
...(trace.arm ? {
|
|
55
|
+
arm: {
|
|
56
|
+
id: trace.arm.armId,
|
|
57
|
+
pulls: trace.arm.pulls,
|
|
58
|
+
completed: trace.arm.completedPulls,
|
|
59
|
+
total: trace.arm.totalPulls,
|
|
60
|
+
mean: trace.arm.meanReward,
|
|
61
|
+
index: trace.arm.index,
|
|
62
|
+
},
|
|
63
|
+
} : {}),
|
|
64
|
+
...(trace.shadowDisagrees !== undefined ? { disagrees: trace.shadowDisagrees } : {}),
|
|
65
|
+
...(trace.fallbackReason ? { fallback: trace.fallbackReason } : {}),
|
|
66
|
+
};
|
|
67
|
+
}
|
|
68
|
+
function minimalSelectionPolicyEventProjection(trace) {
|
|
69
|
+
return {
|
|
70
|
+
requested: trace.requested,
|
|
71
|
+
effective: trace.effective,
|
|
72
|
+
version: trace.selectionPolicyVersion,
|
|
73
|
+
rewardVersion: trace.rewardPolicyVersion,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
function selectionGuardEventProjection(trace) {
|
|
77
|
+
return {
|
|
78
|
+
mode: trace.mode,
|
|
79
|
+
version: trace.version,
|
|
80
|
+
status: trace.status,
|
|
81
|
+
...(trace.reason ? { reason: trace.reason } : {}),
|
|
82
|
+
...(trace.maxMatch !== undefined ? { maxMatch: trace.maxMatch } : {}),
|
|
83
|
+
...(trace.matchSpread !== undefined ? { spread: trace.matchSpread } : {}),
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
function minimalSelectionGuardEventProjection(trace) {
|
|
87
|
+
return {
|
|
88
|
+
mode: trace.mode,
|
|
89
|
+
version: trace.version,
|
|
90
|
+
status: trace.status,
|
|
91
|
+
...(trace.reason ? { reason: trace.reason } : {}),
|
|
92
|
+
};
|
|
93
|
+
}
|
|
94
|
+
function selectionCandidateEventProjection(candidate) {
|
|
95
|
+
// matchScore and scoring diagnostics stay off decision.gene_selected payloads so
|
|
96
|
+
// UCB1/plateau telemetry retains the 4096B root-event line budget.
|
|
97
|
+
const { matchScore: _matchScore, scoreBase: _scoreBase, kautoContribution: _kautoContribution, ...persisted } = candidate;
|
|
98
|
+
return persisted;
|
|
99
|
+
}
|
|
100
|
+
async function ingestGeneSelectedWithTraceBudget(ingestor, event, policyTrace, guardTrace) {
|
|
101
|
+
const guard = guardTrace ? selectionGuardEventProjection(guardTrace) : undefined;
|
|
102
|
+
const minimalGuard = guardTrace ? minimalSelectionGuardEventProjection(guardTrace) : undefined;
|
|
103
|
+
const projections = [];
|
|
104
|
+
if (policyTrace) {
|
|
105
|
+
projections.push({ selectionPolicy: selectionPolicyEventProjection(policyTrace), ...(guard ? { selectionGuard: guard } : {}) });
|
|
106
|
+
projections.push({ selectionPolicy: minimalSelectionPolicyEventProjection(policyTrace), ...(guard ? { selectionGuard: guard } : {}) });
|
|
107
|
+
if (minimalGuard) {
|
|
108
|
+
projections.push({ selectionPolicy: minimalSelectionPolicyEventProjection(policyTrace), selectionGuard: minimalGuard });
|
|
109
|
+
projections.push({ selectionGuard: minimalGuard });
|
|
110
|
+
}
|
|
111
|
+
projections.push({ selectionPolicy: minimalSelectionPolicyEventProjection(policyTrace) });
|
|
112
|
+
}
|
|
113
|
+
else if (guard) {
|
|
114
|
+
projections.push({ selectionGuard: guard });
|
|
115
|
+
if (minimalGuard)
|
|
116
|
+
projections.push({ selectionGuard: minimalGuard });
|
|
117
|
+
}
|
|
118
|
+
projections.push({});
|
|
119
|
+
for (let index = 0; index < projections.length; index += 1) {
|
|
120
|
+
const projection = projections[index];
|
|
121
|
+
try {
|
|
122
|
+
await ingestor.ingest({
|
|
123
|
+
type: event.type,
|
|
124
|
+
human: event.human,
|
|
125
|
+
payload: { ...event.payload, ...projection },
|
|
126
|
+
});
|
|
127
|
+
return;
|
|
128
|
+
}
|
|
129
|
+
catch (error) {
|
|
130
|
+
if (!(error instanceof LineTooLargeError) || index === projections.length - 1)
|
|
131
|
+
throw error;
|
|
132
|
+
}
|
|
133
|
+
}
|
|
134
|
+
}
|
|
19
135
|
/** Read the trailing run of cycle outcomes (newest last) from the event log, for plateau detection. */
|
|
20
136
|
function recentCycleOutcomes(ingestor, window) {
|
|
21
137
|
let events;
|
|
@@ -107,7 +223,7 @@ function categoryForStrategy(category, strategy) {
|
|
|
107
223
|
* Reading from the same append-only log makes this fully replayable. ad-hoc (innovate) cycles have no gene to
|
|
108
224
|
* credit and are skipped.
|
|
109
225
|
*/
|
|
110
|
-
function recentConfidenceObservations(ingestor, window) {
|
|
226
|
+
function recentConfidenceObservations(ingestor, window, opts = {}) {
|
|
111
227
|
let events;
|
|
112
228
|
try {
|
|
113
229
|
events = ingestor.tail(window);
|
|
@@ -147,8 +263,9 @@ function recentConfidenceObservations(ingestor, window) {
|
|
|
147
263
|
// Older events predate the producedValue tag; when it is absent we treat the cycle as a real success, so we
|
|
148
264
|
// never retroactively reclassify history into a surprise ban.
|
|
149
265
|
const status = e.type === 'cycle.failed' ? 'failed' : p?.producedValue === false ? 'inert' : 'success';
|
|
266
|
+
const fingerprintSignals = opts.ignoreTaskDomain ? withoutTaskDomainSignals(signals) : signals;
|
|
150
267
|
out.push({
|
|
151
|
-
signalFingerprint: signalFingerprint(
|
|
268
|
+
signalFingerprint: signalFingerprint(fingerprintSignals),
|
|
152
269
|
geneId: gene,
|
|
153
270
|
status,
|
|
154
271
|
at: Number.isFinite(at) ? at : 0,
|
|
@@ -234,6 +351,18 @@ export class CycleEngine {
|
|
|
234
351
|
const executionMetadata = (exec) => ({
|
|
235
352
|
...(exec.failureKind !== undefined ? { failureKind: exec.failureKind } : {}),
|
|
236
353
|
...(exec.exitCode !== undefined ? { exitCode: exec.exitCode } : {}),
|
|
354
|
+
...(exec.bindingCorrelation ? { bindingDigest: exec.bindingCorrelation.bindingDigest, runId: exec.bindingCorrelation.runId } : {}),
|
|
355
|
+
...(exec.executionTerminal ? { terminalDisposition: exec.executionTerminal.disposition } : {}),
|
|
356
|
+
...(exec.hubLifecycle ? { hubLifecycle: exec.hubLifecycle } : {}),
|
|
357
|
+
...(exec.provenance ? {
|
|
358
|
+
provenance: {
|
|
359
|
+
geneIds: exec.provenance.gene_ids,
|
|
360
|
+
capsuleIds: exec.provenance.capsule_ids,
|
|
361
|
+
resultAssetRefs: exec.provenance.result_asset_refs,
|
|
362
|
+
proofRefs: exec.provenance.proof_refs,
|
|
363
|
+
terminalDisposition: exec.provenance.terminal_disposition,
|
|
364
|
+
},
|
|
365
|
+
} : {}),
|
|
237
366
|
});
|
|
238
367
|
const strategy = resolveStrategy({ name: input.strategyName, signals: cycleSignals, cycleCount: historyCycleCount(ingestor, 1000, input.cycleId) });
|
|
239
368
|
const cycleCategory = categoryForStrategy(input.category, strategy);
|
|
@@ -243,7 +372,7 @@ export class CycleEngine {
|
|
|
243
372
|
throw new Error(`非法 cycle 迁移 ${stage}→${to}`);
|
|
244
373
|
stage = to;
|
|
245
374
|
};
|
|
246
|
-
await ingestor.ingest({ type: 'cycle.started', human: { title: `cycle ${input.cycleId} 启动` }, payload: { cycleId: input.cycleId, problemId: input.problem.id } });
|
|
375
|
+
const cycleStarted = await ingestor.ingest({ type: 'cycle.started', human: { title: `cycle ${input.cycleId} 启动` }, payload: { cycleId: input.cycleId, problemId: input.problem.id } });
|
|
247
376
|
advance('started');
|
|
248
377
|
await ingestor.ingest({
|
|
249
378
|
type: 'cycle.signals_collected',
|
|
@@ -295,8 +424,24 @@ export class CycleEngine {
|
|
|
295
424
|
return;
|
|
296
425
|
await applyStatsUpdate({ store: this.deps.personality, ingestor, cycleId: input.cycleId }, { personality: personalityForRun, outcome, score });
|
|
297
426
|
};
|
|
427
|
+
let ucb1History;
|
|
428
|
+
if (plateau.active && input.selectionPolicy && input.selectionPolicy !== 'engine-health') {
|
|
429
|
+
try {
|
|
430
|
+
ucb1History = deriveUcb1History(ingestor.readAllStrict());
|
|
431
|
+
}
|
|
432
|
+
catch {
|
|
433
|
+
// Active UCB1 fails safe to legacy drift; shadow records missing_history in its compact policy trace.
|
|
434
|
+
ucb1History = undefined;
|
|
435
|
+
}
|
|
436
|
+
}
|
|
298
437
|
const exploration = plateau.active
|
|
299
|
-
? {
|
|
438
|
+
? {
|
|
439
|
+
plateau,
|
|
440
|
+
driftEnabled: true,
|
|
441
|
+
totalAttempts: input.candidates.reduce((s, c) => s + (c.view?.total ?? 0), 0),
|
|
442
|
+
...(input.selectionPolicy ? { policy: input.selectionPolicy } : {}),
|
|
443
|
+
...(ucb1History ? { ucb1History } : {}),
|
|
444
|
+
}
|
|
300
445
|
: undefined;
|
|
301
446
|
// Epigenetic: penalize candidates that fail in THIS environment class (derived from per-env outcome history).
|
|
302
447
|
const geneOutcomes = recentGeneOutcomes(ingestor, 200);
|
|
@@ -304,7 +449,7 @@ export class CycleEngine {
|
|
|
304
449
|
// signal fingerprint. Derived from the same event log, applied as a soft fourth factor in selection.
|
|
305
450
|
const confidenceObs = recentConfidenceObservations(ingestor, 200);
|
|
306
451
|
const confidenceEdges = deriveConfidenceEdges(confidenceObs);
|
|
307
|
-
const
|
|
452
|
+
const currentConfidenceFingerprint = signalFingerprint(baseSignals);
|
|
308
453
|
// #195: drop genes stuck producing only inert (zero-work) cycles on THIS signal. A sole-matching do-nothing
|
|
309
454
|
// gene is otherwise re-selected every cycle (drift can't diversify a single candidate) and the failure-streak
|
|
310
455
|
// ban never fires because nothing "fails" — so it dominates --loop forever while producing no artifacts.
|
|
@@ -314,46 +459,85 @@ export class CycleEngine {
|
|
|
314
459
|
// cycles evict its inert run (or the earlier real success that exempts it) and the ban would flap. Recomputing
|
|
315
460
|
// it over INERT_BAN_OBSERVATION_WINDOW events (mirroring v1's memory-graph read horizon) keeps the trailing run
|
|
316
461
|
// and the successCount exemption alive across intervening cycles. tail() reads the full log either way (no I/O cost).
|
|
317
|
-
const inertBanObs = recentConfidenceObservations(ingestor, INERT_BAN_OBSERVATION_WINDOW);
|
|
318
|
-
const
|
|
462
|
+
const inertBanObs = recentConfidenceObservations(ingestor, INERT_BAN_OBSERVATION_WINDOW, { ignoreTaskDomain: true });
|
|
463
|
+
const inertBanFingerprint = signalFingerprint(withoutTaskDomainSignals(baseSignals));
|
|
464
|
+
const inertBanned = inertBannedGeneIds(deriveConfidenceEdges(inertBanObs), inertBanObs, inertBanFingerprint);
|
|
319
465
|
const candidates = input.candidates
|
|
320
466
|
.filter((c) => !isCandidateInSet(c, inertBanned))
|
|
321
467
|
.map((c) => {
|
|
322
468
|
const epi = epigeneticPenaltyForIds(candidateIds(c), envKey, geneOutcomes);
|
|
323
|
-
const conf = confidenceFor(confidenceEdges,
|
|
469
|
+
const conf = confidenceFor(confidenceEdges, currentConfidenceFingerprint, c.geneId, now);
|
|
324
470
|
const withEpi = epi > 0 ? { ...c, epigeneticPenalty: epi } : c;
|
|
325
471
|
return conf > 0 ? { ...withEpi, confidence: conf } : withEpi;
|
|
326
472
|
});
|
|
327
473
|
// #97 distilled-gene fallback pool: attach the env-scoped epigenetic penalty (so selection can hard-skip a
|
|
328
474
|
// suppressed fallback, v1 parity) and drop any inert-banned id (composes with #195). Selection uses these only
|
|
329
|
-
// when
|
|
475
|
+
// when normal selection has no reusable positive choice.
|
|
330
476
|
const distilledFallback = (input.distilledFallback ?? [])
|
|
331
477
|
.filter((c) => !isCandidateInSet(c, inertBanned))
|
|
332
478
|
.map((c) => {
|
|
333
479
|
const epi = epigeneticPenaltyForIds(candidateIds(c), envKey, geneOutcomes);
|
|
334
480
|
return epi > 0 ? { ...c, epigeneticPenalty: epi } : c;
|
|
335
481
|
});
|
|
336
|
-
const
|
|
337
|
-
await
|
|
482
|
+
const semanticCorpus = input.semanticCorpus?.filter((c) => !isCandidateInSet(c, inertBanned));
|
|
483
|
+
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 } : {}) }));
|
|
484
|
+
const geneId = decision.selectedGeneId ?? 'ad-hoc';
|
|
485
|
+
const runPostSelectionStep = async (phase, step) => {
|
|
486
|
+
try {
|
|
487
|
+
return await step();
|
|
488
|
+
}
|
|
489
|
+
catch (error) {
|
|
490
|
+
let persistedTerminal = null;
|
|
491
|
+
try {
|
|
492
|
+
persistedTerminal = persistedTerminalAfterLatestSelection(ingestor, input.cycleId, cycleStarted.seq);
|
|
493
|
+
}
|
|
494
|
+
catch {
|
|
495
|
+
// Readback is best-effort; compensation below remains the fallback when the log is unavailable.
|
|
496
|
+
}
|
|
497
|
+
if (persistedTerminal && !TERMINAL.has(stage) && canTransition(stage, persistedTerminal)) {
|
|
498
|
+
advance(persistedTerminal);
|
|
499
|
+
}
|
|
500
|
+
if (!persistedTerminal && !TERMINAL.has(stage)) {
|
|
501
|
+
try {
|
|
502
|
+
await ingestor.ingest({
|
|
503
|
+
type: 'cycle.failed',
|
|
504
|
+
human: { title: `cycle ${input.cycleId} selection 后异常` },
|
|
505
|
+
payload: { cycleId: input.cycleId, reason: 'post_selection_error', phase, gene: geneId, env: envKey },
|
|
506
|
+
});
|
|
507
|
+
advance('failed');
|
|
508
|
+
try {
|
|
509
|
+
await recordPersonalityOutcome('failed', null);
|
|
510
|
+
}
|
|
511
|
+
catch {
|
|
512
|
+
// Personality is soft state; never let its writeback replace the original failure.
|
|
513
|
+
}
|
|
514
|
+
}
|
|
515
|
+
catch {
|
|
516
|
+
// The event store itself may be unavailable. Preserve and rethrow the original step failure.
|
|
517
|
+
}
|
|
518
|
+
}
|
|
519
|
+
throw error;
|
|
520
|
+
}
|
|
521
|
+
};
|
|
522
|
+
await runPostSelectionStep('decision_event', () => ingestGeneSelectedWithTraceBudget(ingestor, {
|
|
338
523
|
type: 'decision.gene_selected',
|
|
339
524
|
human: { title: `选 gene ${decision.selectedGeneId ?? '(innovate)'}`, why: decision.candidates.map((c) => `${c.geneId}:${c.score.toFixed(3)}`).join(', ') || '无候选→innovate' },
|
|
340
|
-
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] } : {}) },
|
|
341
|
-
});
|
|
525
|
+
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] } : {}) },
|
|
526
|
+
}, decision.selectionPolicy, decision.selectionGuard));
|
|
342
527
|
advance('gene_selected');
|
|
343
|
-
const geneId = decision.selectedGeneId ?? 'ad-hoc';
|
|
344
528
|
// 变异
|
|
345
|
-
const builtMutation = buildMutation({ decision, category: cycleCategory, signals: cycleSignals, target: input.target, expectedEffect: input.expectedEffect });
|
|
529
|
+
const builtMutation = await runPostSelectionStep('mutation_build', () => buildMutation({ decision, category: cycleCategory, signals: cycleSignals, target: input.target, expectedEffect: input.expectedEffect }));
|
|
346
530
|
// 人格风险闸(用途②): 高危人格禁 innovate / 未达阈值的 high-risk 降级. 未注入人格 ⇒ 原样放行.
|
|
347
531
|
// 用本轮 applySelectForRun 选出的状态(personalityForRun), 而非再读一次 store.currentState() ——
|
|
348
532
|
// 保证"喂 prompt 的风格 / 过风险闸 / 回写统计"三者用的是同一个当轮人格.
|
|
349
533
|
let mutation = builtMutation;
|
|
350
534
|
if (this.deps.personality && personalityForRun) {
|
|
351
|
-
await emitPersonalitySelected(ingestor, { cycleId: input.cycleId, personality: personalityForRun, known: personalityKnown });
|
|
352
|
-
const gate = gatePersonalityRisk(builtMutation, personalityForRun);
|
|
535
|
+
await runPostSelectionStep('personality_selected', () => emitPersonalitySelected(ingestor, { cycleId: input.cycleId, personality: personalityForRun, known: personalityKnown }));
|
|
536
|
+
const gate = await runPostSelectionStep('personality_risk_gate', () => gatePersonalityRisk(builtMutation, personalityForRun));
|
|
353
537
|
mutation = gate.mutation;
|
|
354
|
-
await emitPersonalityRiskGated(ingestor, { cycleId: input.cycleId, personality: personalityForRun, gate });
|
|
538
|
+
await runPostSelectionStep('personality_risk_gate', () => emitPersonalityRiskGated(ingestor, { cycleId: input.cycleId, personality: personalityForRun, gate }));
|
|
355
539
|
}
|
|
356
|
-
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 } });
|
|
540
|
+
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 } }));
|
|
357
541
|
advance('mutation_built');
|
|
358
542
|
// 执行(agent/runtime)
|
|
359
543
|
let exec;
|
|
@@ -364,7 +548,7 @@ export class CycleEngine {
|
|
|
364
548
|
const msg = e instanceof Error ? e.message : String(e);
|
|
365
549
|
// execute threw before returning a transcript; the error text IS the host context (a provider error that
|
|
366
550
|
// surfaces as a thrown exception still classifies as host_provider_error). Pass it as the sessionLog.
|
|
367
|
-
await ingestor.ingest({ type: 'cycle.failed', human: { title: `cycle ${input.cycleId} 执行抛错` }, payload: { cycleId: input.cycleId, error: msg, gene: geneId, env: envKey, ...failureClassPayload({ geneId }, msg) } });
|
|
551
|
+
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) } }));
|
|
368
552
|
advance('failed');
|
|
369
553
|
await recordPersonalityOutcome('failed', null);
|
|
370
554
|
return { cycleId: input.cycleId, triggered: true, finalStage: stage, producedValue: false, decision, mutation, reasons: [...reasons, `执行异常: ${msg}`] };
|
|
@@ -374,6 +558,9 @@ export class CycleEngine {
|
|
|
374
558
|
try {
|
|
375
559
|
permit = await input.solidifyPermit({
|
|
376
560
|
cycleId: input.cycleId,
|
|
561
|
+
...(input.executionBinding ? { bindingDigest: input.executionBinding.binding_digest, runId: input.executionBinding.correlation.run_id } : {}),
|
|
562
|
+
...(exec.executionTerminal ? { executionTerminal: exec.executionTerminal } : {}),
|
|
563
|
+
...(exec.provenance ? { provenance: exec.provenance } : {}),
|
|
377
564
|
geneId,
|
|
378
565
|
signals: cycleSignals,
|
|
379
566
|
mutation,
|
|
@@ -388,7 +575,15 @@ export class CycleEngine {
|
|
|
388
575
|
}
|
|
389
576
|
if (!permit.ok) {
|
|
390
577
|
const reason = `solidify permit denied: ${permit.reason}`;
|
|
391
|
-
|
|
578
|
+
exec = {
|
|
579
|
+
...exec,
|
|
580
|
+
outcome: { ...exec.outcome, status: 'failed', reason },
|
|
581
|
+
executionTerminal: { status: 'failed', disposition: 'denied' },
|
|
582
|
+
provenance: exec.provenance
|
|
583
|
+
? { ...exec.provenance, permit, terminal_disposition: 'denied' }
|
|
584
|
+
: undefined,
|
|
585
|
+
};
|
|
586
|
+
await runPostSelectionStep('terminal_event', () => ingestor.ingest({
|
|
392
587
|
type: 'cycle.failed',
|
|
393
588
|
human: { title: `cycle ${input.cycleId} permit denied` },
|
|
394
589
|
payload: {
|
|
@@ -399,30 +594,60 @@ export class CycleEngine {
|
|
|
399
594
|
...executionMetadata(exec),
|
|
400
595
|
...failureClassPayload({ geneId }),
|
|
401
596
|
},
|
|
402
|
-
});
|
|
597
|
+
}));
|
|
403
598
|
advance('failed');
|
|
404
599
|
await recordPersonalityOutcome('failed', null);
|
|
405
|
-
return {
|
|
600
|
+
return {
|
|
601
|
+
cycleId: input.cycleId,
|
|
602
|
+
triggered: true,
|
|
603
|
+
finalStage: stage,
|
|
604
|
+
producedValue: false,
|
|
605
|
+
decision,
|
|
606
|
+
mutation,
|
|
607
|
+
execution: exec,
|
|
608
|
+
...(exec.bindingCorrelation ? { bindingDigest: exec.bindingCorrelation.bindingDigest, runId: exec.bindingCorrelation.runId } : {}),
|
|
609
|
+
...(exec.executionTerminal ? { terminalDisposition: exec.executionTerminal.disposition } : {}),
|
|
610
|
+
...executionMetadata(exec),
|
|
611
|
+
reasons: [...reasons, reason],
|
|
612
|
+
};
|
|
406
613
|
}
|
|
407
614
|
}
|
|
408
615
|
// solidify → Capsule
|
|
409
|
-
const sol = solidify({
|
|
616
|
+
const sol = await runPostSelectionStep('solidify', () => solidify({
|
|
410
617
|
geneId, trigger: cycleSignals, summary: input.summary, confidence: input.confidence,
|
|
411
618
|
outcome: exec.outcome, proofOfWork: exec.proofOfWork, strongEvidence: exec.strongEvidence,
|
|
412
|
-
|
|
619
|
+
...(exec.failureIdentity ? { failureIdentity: exec.failureIdentity } : {}),
|
|
620
|
+
}));
|
|
413
621
|
reasons.push(...sol.reasons);
|
|
414
|
-
await store.put(sol.capsule);
|
|
415
|
-
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 } });
|
|
416
|
-
// EvolutionEvent(capsule_id 引 Capsule; outcome 真值在此)
|
|
417
|
-
|
|
622
|
+
await runPostSelectionStep('capsule_store', () => store.put(sol.capsule));
|
|
623
|
+
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 } }));
|
|
624
|
+
// EvolutionEvent(capsule_id 引 Capsule; outcome 真值在此).
|
|
625
|
+
// Selection stamp: selected_asset_id / kauto_member / evolver_version / selection_stage=applied
|
|
626
|
+
// live in event.meta so production cohort collection can separate writer membership from
|
|
627
|
+
// selected/applied member share. Only true kauto membership is stamped (omit-not-null).
|
|
628
|
+
const selectedCandidate = decision.selectedGeneId
|
|
629
|
+
? input.candidates.find((c) => (c.geneId === decision.selectedGeneId
|
|
630
|
+
&& (decision.selectedAssetId === undefined || c.assetId === decision.selectedAssetId)))
|
|
631
|
+
?? input.candidates.find((c) => c.geneId === decision.selectedGeneId)
|
|
632
|
+
?? (input.distilledFallback ?? []).find((c) => (c.geneId === decision.selectedGeneId
|
|
633
|
+
&& (decision.selectedAssetId === undefined || c.assetId === decision.selectedAssetId)))
|
|
634
|
+
: undefined;
|
|
635
|
+
const envFp = (this.deps.envFingerprint ?? captureEnvFingerprint)();
|
|
636
|
+
const event = await runPostSelectionStep('evolution_event_build', () => buildEvolutionEvent({
|
|
418
637
|
intent: cycleCategory, signals: cycleSignals, genesUsed: decision.selectedGeneId ? [decision.selectedGeneId] : [],
|
|
419
638
|
mutationId: mutation.id, blastRadius: sol.capsule.blast_radius, outcome: exec.outcome,
|
|
420
639
|
capsuleId: sol.capsule.asset_id, sourceType: decision.selectedGeneId ? 'reused' : 'generated',
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
640
|
+
selection: {
|
|
641
|
+
selectionStage: 'applied',
|
|
642
|
+
...(decision.selectedAssetId ? { selectedAssetId: decision.selectedAssetId } : {}),
|
|
643
|
+
...(selectedCandidate?.kautoMember === true ? { kautoMember: true } : {}),
|
|
644
|
+
...(envFp.evolver_version ? { evolverVersion: envFp.evolver_version } : {}),
|
|
645
|
+
},
|
|
646
|
+
}));
|
|
647
|
+
await runPostSelectionStep('evolution_event_store', () => store.put(event));
|
|
648
|
+
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 } }));
|
|
424
649
|
if (exec.outcome.status === 'failed') {
|
|
425
|
-
await ingestor.ingest({
|
|
650
|
+
await runPostSelectionStep('terminal_event', () => ingestor.ingest({
|
|
426
651
|
type: 'cycle.failed',
|
|
427
652
|
human: { title: `cycle ${input.cycleId} 失败` },
|
|
428
653
|
payload: {
|
|
@@ -443,15 +668,30 @@ export class CycleEngine {
|
|
|
443
668
|
: {}),
|
|
444
669
|
}, exec.sessionLog),
|
|
445
670
|
},
|
|
446
|
-
});
|
|
671
|
+
}));
|
|
447
672
|
advance('failed');
|
|
448
673
|
}
|
|
449
674
|
else {
|
|
450
|
-
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 } });
|
|
675
|
+
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 } }));
|
|
451
676
|
advance('solidified');
|
|
452
677
|
}
|
|
453
678
|
// 人格统计回写(用途②): 用本轮真实 outcome/score 回写当轮人格桶, 供下一轮自然选择靠拢. 未注入 ⇒ no-op.
|
|
454
679
|
await recordPersonalityOutcome(exec.outcome.status, exec.outcome.score);
|
|
455
|
-
return {
|
|
680
|
+
return {
|
|
681
|
+
cycleId: input.cycleId,
|
|
682
|
+
triggered: true,
|
|
683
|
+
finalStage: stage,
|
|
684
|
+
producedValue: sol.producedValue,
|
|
685
|
+
decision,
|
|
686
|
+
mutation,
|
|
687
|
+
capsule: sol.capsule,
|
|
688
|
+
event,
|
|
689
|
+
resolutionStatus: sol.resolutionStatus,
|
|
690
|
+
execution: exec,
|
|
691
|
+
...(exec.bindingCorrelation ? { bindingDigest: exec.bindingCorrelation.bindingDigest, runId: exec.bindingCorrelation.runId } : {}),
|
|
692
|
+
...(exec.executionTerminal ? { terminalDisposition: exec.executionTerminal.disposition } : {}),
|
|
693
|
+
...executionMetadata(exec),
|
|
694
|
+
reasons,
|
|
695
|
+
};
|
|
456
696
|
}
|
|
457
697
|
}
|
|
@@ -1,4 +1,19 @@
|
|
|
1
1
|
import { type EvolutionEvent, type GepCategory } from '../wire/index.js';
|
|
2
|
+
/** Selection-side lifecycle stamp carried in EvolutionEvent.meta (schema-allowed object). */
|
|
3
|
+
export type EvolutionSelectionStage = 'selected' | 'applied';
|
|
4
|
+
export interface EvolutionSelectionStamp {
|
|
5
|
+
/** Content asset_id of the selected Gene when known (sha256:…). */
|
|
6
|
+
selectedAssetId?: string;
|
|
7
|
+
/** True only when assembly stamped kautoMember on the selected candidate. */
|
|
8
|
+
kautoMember?: boolean;
|
|
9
|
+
/** Producer evolver version from env fingerprint, when known. */
|
|
10
|
+
evolverVersion?: string;
|
|
11
|
+
/**
|
|
12
|
+
* selected = gene was chosen for the cycle;
|
|
13
|
+
* applied = cycle produced an EvolutionEvent after execution (this builder always emits applied).
|
|
14
|
+
*/
|
|
15
|
+
selectionStage?: EvolutionSelectionStage;
|
|
16
|
+
}
|
|
2
17
|
export interface BuildEvolutionEventInput {
|
|
3
18
|
intent: GepCategory;
|
|
4
19
|
signals: readonly string[];
|
|
@@ -15,9 +30,20 @@ export interface BuildEvolutionEventInput {
|
|
|
15
30
|
capsuleId: string | null;
|
|
16
31
|
sourceType: 'generated' | 'reused' | 'reference';
|
|
17
32
|
parent?: string | null;
|
|
33
|
+
/** Optional selection lifecycle stamp; omitted keys stay absent (omit-not-null). */
|
|
34
|
+
selection?: EvolutionSelectionStamp;
|
|
18
35
|
}
|
|
36
|
+
/**
|
|
37
|
+
* Build the schema-allowed `meta` object for selection telemetry.
|
|
38
|
+
* Absent optional fields are omitted so they do not enter the asset_id hash as null.
|
|
39
|
+
*/
|
|
40
|
+
export declare function buildEvolutionSelectionMeta(selection: EvolutionSelectionStamp | undefined): Record<string, unknown> | undefined;
|
|
19
41
|
/**
|
|
20
42
|
* EvolutionEvent = 世代记录(outcome 真值在此, 硬化 A4). 单向引 Capsule(capsule_id);
|
|
21
43
|
* 写入序列: capsule asset_id 先算 → 填 capsule_id → 再算 event asset_id.
|
|
44
|
+
*
|
|
45
|
+
* Optional selection stamp lands in `meta` (schema-allowed) so production cohort
|
|
46
|
+
* collection can separate writer membership from selected/applied member share
|
|
47
|
+
* without inventing a LearningAsset bridge.
|
|
22
48
|
*/
|
|
23
49
|
export declare function buildEvolutionEvent(input: BuildEvolutionEventInput): EvolutionEvent;
|
|
@@ -1,10 +1,41 @@
|
|
|
1
1
|
import { ulid as makeUlid } from 'ulid';
|
|
2
2
|
import { computeAssetId, SCHEMA_VERSION } from '../wire/index.js';
|
|
3
|
+
function nonEmptyString(value) {
|
|
4
|
+
return typeof value === 'string' && value.trim().length > 0 ? value.trim() : undefined;
|
|
5
|
+
}
|
|
6
|
+
/**
|
|
7
|
+
* Build the schema-allowed `meta` object for selection telemetry.
|
|
8
|
+
* Absent optional fields are omitted so they do not enter the asset_id hash as null.
|
|
9
|
+
*/
|
|
10
|
+
export function buildEvolutionSelectionMeta(selection) {
|
|
11
|
+
if (!selection)
|
|
12
|
+
return undefined;
|
|
13
|
+
const selectedAssetId = nonEmptyString(selection.selectedAssetId);
|
|
14
|
+
const evolverVersion = nonEmptyString(selection.evolverVersion);
|
|
15
|
+
const selectionStage = selection.selectionStage === 'selected' || selection.selectionStage === 'applied'
|
|
16
|
+
? selection.selectionStage
|
|
17
|
+
: undefined;
|
|
18
|
+
const meta = {};
|
|
19
|
+
if (selectedAssetId)
|
|
20
|
+
meta['selected_asset_id'] = selectedAssetId;
|
|
21
|
+
if (selection.kautoMember === true)
|
|
22
|
+
meta['kauto_member'] = true;
|
|
23
|
+
if (evolverVersion)
|
|
24
|
+
meta['evolver_version'] = evolverVersion;
|
|
25
|
+
if (selectionStage)
|
|
26
|
+
meta['selection_stage'] = selectionStage;
|
|
27
|
+
return Object.keys(meta).length > 0 ? meta : undefined;
|
|
28
|
+
}
|
|
3
29
|
/**
|
|
4
30
|
* EvolutionEvent = 世代记录(outcome 真值在此, 硬化 A4). 单向引 Capsule(capsule_id);
|
|
5
31
|
* 写入序列: capsule asset_id 先算 → 填 capsule_id → 再算 event asset_id.
|
|
32
|
+
*
|
|
33
|
+
* Optional selection stamp lands in `meta` (schema-allowed) so production cohort
|
|
34
|
+
* collection can separate writer membership from selected/applied member share
|
|
35
|
+
* without inventing a LearningAsset bridge.
|
|
6
36
|
*/
|
|
7
37
|
export function buildEvolutionEvent(input) {
|
|
38
|
+
const meta = buildEvolutionSelectionMeta(input.selection);
|
|
8
39
|
const base = {
|
|
9
40
|
type: 'EvolutionEvent',
|
|
10
41
|
schema_version: SCHEMA_VERSION,
|
|
@@ -18,6 +49,7 @@ export function buildEvolutionEvent(input) {
|
|
|
18
49
|
outcome: input.outcome,
|
|
19
50
|
capsule_id: input.capsuleId,
|
|
20
51
|
source_type: input.sourceType,
|
|
52
|
+
...(meta ? { meta } : {}),
|
|
21
53
|
};
|
|
22
54
|
const asset_id = computeAssetId(base);
|
|
23
55
|
return { ...base, asset_id };
|
|
@@ -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
|
}
|