@evomap/evolver-core 2.0.0-beta.2 → 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.
Files changed (240) hide show
  1. package/assets/gep/genes.jsonl +5 -5
  2. package/dist/algo/bans.d.ts +10 -1
  3. package/dist/algo/bans.js +56 -6
  4. package/dist/algo/candidateAssembly.d.ts +25 -2
  5. package/dist/algo/candidateAssembly.js +171 -26
  6. package/dist/algo/capabilityCandidates.js +10 -0
  7. package/dist/algo/conversationSniffer.js +25 -8
  8. package/dist/algo/cycleEngine.d.ts +63 -5
  9. package/dist/algo/cycleEngine.js +320 -42
  10. package/dist/algo/cycleFailureClassifier.d.ts +1 -1
  11. package/dist/algo/evolutionEvent.d.ts +26 -0
  12. package/dist/algo/evolutionEvent.js +32 -0
  13. package/dist/algo/exploration.d.ts +7 -0
  14. package/dist/algo/exploration.js +16 -3
  15. package/dist/algo/geneHealth.d.ts +36 -3
  16. package/dist/algo/geneHealth.js +47 -4
  17. package/dist/algo/geneIntake.d.ts +46 -1
  18. package/dist/algo/geneIntake.js +121 -10
  19. package/dist/algo/geneSelection.d.ts +126 -6
  20. package/dist/algo/geneSelection.js +479 -36
  21. package/dist/algo/index.d.ts +6 -1
  22. package/dist/algo/index.js +6 -1
  23. package/dist/algo/kautoProjection.d.ts +41 -0
  24. package/dist/algo/kautoProjection.js +95 -0
  25. package/dist/algo/kautoValidator.d.ts +68 -0
  26. package/dist/algo/kautoValidator.js +256 -0
  27. package/dist/algo/memoryGraph.d.ts +62 -0
  28. package/dist/algo/memoryGraph.js +86 -0
  29. package/dist/algo/orchestrator.d.ts +17 -1
  30. package/dist/algo/orchestrator.js +30 -4
  31. package/dist/algo/publishEligibility.d.ts +34 -0
  32. package/dist/algo/publishEligibility.js +52 -0
  33. package/dist/algo/solidify.d.ts +11 -2
  34. package/dist/algo/solidify.js +37 -7
  35. package/dist/algo/ucb1.d.ts +53 -0
  36. package/dist/algo/ucb1.js +156 -0
  37. package/dist/assetrepair/hubRejection.d.ts +12 -0
  38. package/dist/assetrepair/hubRejection.js +109 -0
  39. package/dist/assetrepair/index.d.ts +2 -0
  40. package/dist/assetrepair/index.js +2 -0
  41. package/dist/assetrepair/repair.d.ts +33 -0
  42. package/dist/assetrepair/repair.js +155 -0
  43. package/dist/assetstore/assetSidecarRecords.d.ts +28 -0
  44. package/dist/assetstore/assetSidecarRecords.js +384 -0
  45. package/dist/assetstore/assetSidecarRecovery.d.ts +48 -0
  46. package/dist/assetstore/assetSidecarRecovery.js +288 -0
  47. package/dist/assetstore/assetStoreHealth.d.ts +81 -0
  48. package/dist/assetstore/assetStoreHealth.js +319 -0
  49. package/dist/assetstore/assetStoreLayout.d.ts +2 -0
  50. package/dist/assetstore/assetStoreLayout.js +6 -0
  51. package/dist/assetstore/assetStoreStorage.d.ts +42 -0
  52. package/dist/assetstore/assetStoreStorage.js +336 -0
  53. package/dist/assetstore/assetSyncLedger.d.ts +91 -1
  54. package/dist/assetstore/assetSyncLedger.js +718 -59
  55. package/dist/assetstore/foreignJsonlSource.d.ts +48 -0
  56. package/dist/assetstore/foreignJsonlSource.js +150 -0
  57. package/dist/assetstore/index.d.ts +5 -0
  58. package/dist/assetstore/index.js +5 -0
  59. package/dist/assetstore/learningHistory.js +3 -3
  60. package/dist/assetstore/localAssetStoreSnapshot.d.ts +51 -0
  61. package/dist/assetstore/localAssetStoreSnapshot.js +329 -0
  62. package/dist/assetstore/localJsonl.d.ts +12 -1
  63. package/dist/assetstore/localJsonl.js +251 -42
  64. package/dist/assetstore/provenance.d.ts +93 -4
  65. package/dist/assetstore/provenance.js +368 -84
  66. package/dist/assetstore/provider.d.ts +63 -0
  67. package/dist/assetstore/provider.js +97 -6
  68. package/dist/assetstore/reviewFilter.d.ts +19 -1
  69. package/dist/assetstore/reviewFilter.js +39 -1
  70. package/dist/assetstore/reviewLedger.d.ts +8 -2
  71. package/dist/assetstore/reviewLedger.js +71 -45
  72. package/dist/assetstore/unionReadStore.d.ts +25 -0
  73. package/dist/assetstore/unionReadStore.js +119 -0
  74. package/dist/benchmark/antiGeneBenchmark.d.ts +2 -0
  75. package/dist/benchmark/antiGeneBenchmark.js +4 -3
  76. package/dist/benchmark/antiGeneRollout.d.ts +2 -0
  77. package/dist/benchmark/antiGeneRollout.js +4 -3
  78. package/dist/benchmark/index.d.ts +3 -1
  79. package/dist/benchmark/index.js +3 -1
  80. package/dist/benchmark/selectionFlatAbstention.d.ts +152 -0
  81. package/dist/benchmark/selectionFlatAbstention.js +481 -0
  82. package/dist/benchmark/triggerShift.d.ts +62 -0
  83. package/dist/benchmark/triggerShift.js +106 -0
  84. package/dist/bootstrap/envFingerprint.d.ts +9 -0
  85. package/dist/bootstrap/envFingerprint.js +5 -0
  86. package/dist/bootstrap/index.d.ts +3 -1
  87. package/dist/bootstrap/index.js +3 -1
  88. package/dist/bootstrap/lifecycleBootstrap.d.ts +111 -0
  89. package/dist/bootstrap/lifecycleBootstrap.js +433 -0
  90. package/dist/bootstrap/v1EnvCompat.d.ts +113 -0
  91. package/dist/bootstrap/v1EnvCompat.js +303 -0
  92. package/dist/events/eventArchive.d.ts +2 -0
  93. package/dist/events/eventArchive.js +13 -3
  94. package/dist/events/eventSchema.d.ts +7 -7
  95. package/dist/events/eventStore.d.ts +2 -0
  96. package/dist/events/eventStore.js +5 -1
  97. package/dist/events/ingest.d.ts +2 -1
  98. package/dist/events/ingest.js +15 -0
  99. package/dist/events/paths.d.ts +12 -10
  100. package/dist/events/paths.js +24 -20
  101. package/dist/events/public.d.ts +2 -2
  102. package/dist/events/public.js +2 -2
  103. package/dist/events/reports.d.ts +2 -0
  104. package/dist/events/reports.js +4 -0
  105. package/dist/exec/autoExec.d.ts +68 -4
  106. package/dist/exec/autoExec.js +392 -33
  107. package/dist/exec/autonomousCycle.d.ts +31 -4
  108. package/dist/exec/autonomousCycle.js +71 -13
  109. package/dist/exec/claudeBridge.d.ts +80 -15
  110. package/dist/exec/claudeBridge.js +845 -76
  111. package/dist/exec/executionBinding.d.ts +414 -0
  112. package/dist/exec/executionBinding.js +588 -0
  113. package/dist/exec/index.d.ts +1 -0
  114. package/dist/exec/index.js +1 -0
  115. package/dist/exec/openPrRegistry.d.ts +8 -2
  116. package/dist/exec/openPrRegistry.js +32 -22
  117. package/dist/exec/prompt.js +14 -1
  118. package/dist/exec/proofOfWork.d.ts +1 -1
  119. package/dist/exec/proofOfWork.js +2 -2
  120. package/dist/exec/runnerRegistry.d.ts +153 -36
  121. package/dist/exec/runnerRegistry.js +848 -65
  122. package/dist/exec/selfPr.js +1 -7
  123. package/dist/feedback/envelope.d.ts +61 -0
  124. package/dist/feedback/envelope.js +168 -0
  125. package/dist/feedback/index.d.ts +1 -0
  126. package/dist/feedback/index.js +1 -0
  127. package/dist/hooks/hooks.js +1 -0
  128. package/dist/hub/assetCallLog.d.ts +35 -1
  129. package/dist/hub/assetCallLog.js +124 -1
  130. package/dist/hub/bindings.d.ts +8 -1
  131. package/dist/hub/bindings.js +29 -8
  132. package/dist/hub/capability.d.ts +130 -4
  133. package/dist/hub/conversationDistiller.d.ts +19 -0
  134. package/dist/hub/conversationDistiller.js +115 -37
  135. package/dist/hub/fake.d.ts +3 -2
  136. package/dist/hub/fake.js +2 -1
  137. package/dist/hub/index.d.ts +1 -0
  138. package/dist/hub/index.js +1 -0
  139. package/dist/hub/questionGenerator.d.ts +5 -1
  140. package/dist/hub/questionGenerator.js +8 -6
  141. package/dist/hub/recipeCompose.d.ts +27 -0
  142. package/dist/hub/recipeCompose.js +90 -0
  143. package/dist/hub/sanitize.js +122 -7
  144. package/dist/index.d.ts +6 -1
  145. package/dist/index.js +7 -1
  146. package/dist/issueReporter/index.d.ts +156 -0
  147. package/dist/issueReporter/index.js +1688 -0
  148. package/dist/mailbox/dispatch.d.ts +1 -1
  149. package/dist/mailbox/dispatch.js +22 -6
  150. package/dist/mailbox/envelope.d.ts +7 -1
  151. package/dist/mailbox/envelope.js +9 -2
  152. package/dist/mailbox/ipcServer.d.ts +12 -2
  153. package/dist/mailbox/ipcServer.js +183 -13
  154. package/dist/mailbox/store.d.ts +89 -3
  155. package/dist/mailbox/store.js +895 -41
  156. package/dist/modelCompatibility.d.ts +164 -0
  157. package/dist/modelCompatibility.js +309 -0
  158. package/dist/observers/valueDigestObserver.d.ts +9 -0
  159. package/dist/observers/valueDigestObserver.js +35 -2
  160. package/dist/ops/cleanup.js +1 -1
  161. package/dist/ops/evolutionGraphProjection.d.ts +20 -0
  162. package/dist/ops/evolutionGraphProjection.js +315 -0
  163. package/dist/ops/index.d.ts +2 -1
  164. package/dist/ops/index.js +2 -1
  165. package/dist/ops/savingsCore.js +1 -2
  166. package/dist/ops/selfUpdate.d.ts +18 -1
  167. package/dist/ops/selfUpdate.js +88 -23
  168. package/dist/ops/valueOutreach.d.ts +3 -1
  169. package/dist/ops/valueOutreach.js +5 -1
  170. package/dist/personality/schema.d.ts +24 -24
  171. package/dist/schema/evolutionGraph.d.ts +784 -0
  172. package/dist/schema/evolutionGraph.js +187 -0
  173. package/dist/schema/index.d.ts +1 -0
  174. package/dist/schema/index.js +1 -0
  175. package/dist/schema/proofOfWork.d.ts +125 -6
  176. package/dist/schema/proofOfWork.js +102 -4
  177. package/dist/schema/signal.d.ts +3 -3
  178. package/dist/schema/signal.js +1 -1
  179. package/dist/shadow/shadowHub.js +1 -0
  180. package/dist/signals/curriculum.d.ts +55 -0
  181. package/dist/signals/curriculum.js +202 -0
  182. package/dist/signals/cycleHistoryFromEvents.js +17 -8
  183. package/dist/signals/expand.d.ts +15 -1
  184. package/dist/signals/expand.js +169 -1
  185. package/dist/signals/extractor.d.ts +2 -2
  186. package/dist/signals/extractor.js +31 -6
  187. package/dist/signals/index.d.ts +4 -1
  188. package/dist/signals/index.js +4 -1
  189. package/dist/signals/metaSignals.d.ts +4 -0
  190. package/dist/signals/metaSignals.js +42 -0
  191. package/dist/signals/scopeVocabulary.d.ts +75 -0
  192. package/dist/signals/scopeVocabulary.js +91 -0
  193. package/dist/signals/signalGate.js +1 -1
  194. package/dist/signals/taskDomain.d.ts +22 -0
  195. package/dist/signals/taskDomain.js +43 -0
  196. package/dist/strategy/constraintAblation.d.ts +64 -0
  197. package/dist/strategy/constraintAblation.js +2820 -0
  198. package/dist/strategy/constraintAblationPredicates.d.ts +31 -0
  199. package/dist/strategy/constraintAblationPredicates.js +339 -0
  200. package/dist/strategy/index.d.ts +2 -1
  201. package/dist/strategy/index.js +2 -1
  202. package/dist/trace/index.d.ts +3 -1
  203. package/dist/trace/index.js +3 -1
  204. package/dist/trace/learningTrace.d.ts +216 -0
  205. package/dist/trace/learningTrace.js +298 -0
  206. package/dist/trace/proxyTurns.d.ts +31 -0
  207. package/dist/trace/proxyTurns.js +137 -0
  208. package/dist/trace/trajectory.d.ts +8 -0
  209. package/dist/trace/trajectory.js +14 -2
  210. package/dist/util/fetchPort.d.ts +1 -0
  211. package/dist/util/fetchPort.js +11 -0
  212. package/dist/util/fileLock.d.ts +113 -7
  213. package/dist/util/fileLock.js +1035 -80
  214. package/dist/util/index.d.ts +3 -1
  215. package/dist/util/index.js +2 -1
  216. package/dist/verify/index.d.ts +2 -1
  217. package/dist/verify/index.js +1 -1
  218. package/dist/verify/sandboxRunner.d.ts +30 -0
  219. package/dist/verify/sandboxRunner.js +370 -27
  220. package/dist/verify/sandboxedValidation.d.ts +16 -2
  221. package/dist/verify/sandboxedValidation.js +200 -22
  222. package/dist/verify/validation.d.ts +35 -4
  223. package/dist/verify/validation.js +164 -16
  224. package/dist/wire/geneHints.d.ts +88 -16
  225. package/dist/wire/geneHints.js +124 -15
  226. package/dist/wire/index.d.ts +8 -3
  227. package/dist/wire/index.js +2 -2
  228. package/dist/wire/schemaGate.d.ts +21 -0
  229. package/dist/wire/schemaGate.js +119 -10
  230. package/dist/workflow/dsl.d.ts +24 -3
  231. package/dist/workflow/dsl.js +4 -0
  232. package/dist/workflow/engine.d.ts +5 -1
  233. package/dist/workflow/engine.js +3 -0
  234. package/dist/workflow/index.d.ts +3 -1
  235. package/dist/workflow/index.js +3 -1
  236. package/dist/workflow/runtime.d.ts +110 -0
  237. package/dist/workflow/runtime.js +1298 -0
  238. package/dist/workflow/stateStore.d.ts +172 -0
  239. package/dist/workflow/stateStore.js +1044 -0
  240. package/package.json +12 -4
@@ -1,4 +1,87 @@
1
- import { computeAssetId, verifyAssetId } from '../wire/index.js';
1
+ import { canonicalize, computeAssetId, verifyAssetId } from '../wire/index.js';
2
+ export class FrozenAssetIdCollisionError extends Error {
3
+ assetId;
4
+ code = 'FROZEN_ASSET_ID_COLLISION';
5
+ constructor(assetId) {
6
+ super('frozen asset_id already exists with different content');
7
+ this.assetId = assetId;
8
+ this.name = 'FrozenAssetIdCollisionError';
9
+ }
10
+ }
11
+ export class InvalidFrozenPutResultError extends Error {
12
+ code = 'INVALID_FROZEN_PUT_RESULT';
13
+ constructor() {
14
+ super('invalid frozen put result');
15
+ this.name = 'InvalidFrozenPutResultError';
16
+ }
17
+ }
18
+ export function frozenAssetRecordsEqual(left, right) {
19
+ return canonicalize(left) === canonicalize(right);
20
+ }
21
+ export function validateFrozenPutResult(value, expectedAssetId) {
22
+ if (!value || typeof value !== 'object' || Array.isArray(value))
23
+ throw new InvalidFrozenPutResultError();
24
+ const result = value;
25
+ if (result['asset_id'] !== expectedAssetId
26
+ || typeof result['stored'] !== 'boolean'
27
+ || result['verified'] !== false) {
28
+ throw new InvalidFrozenPutResultError();
29
+ }
30
+ return value;
31
+ }
32
+ export class InvalidConditionalPutResultError extends Error {
33
+ reason;
34
+ code = 'INVALID_CONDITIONAL_PUT_RESULT';
35
+ constructor(reason) {
36
+ super(`invalid conditional put result: ${reason}`);
37
+ this.reason = reason;
38
+ this.name = 'InvalidConditionalPutResultError';
39
+ }
40
+ }
41
+ /** Validate an injected provider response before callers treat it as an explicit write/no-write decision. */
42
+ export function validateConditionalPutResult(value, expectedAssetId, options) {
43
+ if (!value || typeof value !== 'object' || Array.isArray(value)) {
44
+ throw new InvalidConditionalPutResultError('malformed_result');
45
+ }
46
+ const result = value;
47
+ const status = result['status'];
48
+ const collisionWithAssetId = result['collisionWithAssetId'];
49
+ if (typeof result['asset_id'] !== 'string'
50
+ || typeof result['stored'] !== 'boolean'
51
+ || typeof result['verified'] !== 'boolean'
52
+ || (status !== 'stored' && status !== 'already_exists' && status !== 'logical_collision')
53
+ || (result['logicalId'] !== undefined
54
+ && (typeof result['logicalId'] !== 'string'
55
+ || !result['logicalId'].trim()
56
+ || result['logicalId'] !== result['logicalId'].trim()))
57
+ || (collisionWithAssetId !== undefined
58
+ && (typeof collisionWithAssetId !== 'string'
59
+ || !collisionWithAssetId.trim()
60
+ || collisionWithAssetId !== collisionWithAssetId.trim()))) {
61
+ throw new InvalidConditionalPutResultError('malformed_result');
62
+ }
63
+ if (result['asset_id'] !== expectedAssetId) {
64
+ throw new InvalidConditionalPutResultError('asset_id_mismatch');
65
+ }
66
+ if (result['stored'] !== (status === 'stored')) {
67
+ throw new InvalidConditionalPutResultError('inconsistent_status');
68
+ }
69
+ const allowLogicalCollision = options?.allowLogicalCollision === true;
70
+ if (status === 'logical_collision'
71
+ && (!collisionWithAssetId || collisionWithAssetId === expectedAssetId || allowLogicalCollision)) {
72
+ throw new InvalidConditionalPutResultError('invalid_collision');
73
+ }
74
+ if (status === 'stored' && collisionWithAssetId && !allowLogicalCollision) {
75
+ throw new InvalidConditionalPutResultError('collision_bypass');
76
+ }
77
+ return value;
78
+ }
79
+ export function supportsAtomicConditionalPut(provider) {
80
+ return typeof provider.putConditional === 'function';
81
+ }
82
+ export function supportsAtomicFrozenConditionalPut(provider) {
83
+ return typeof provider.putFrozenConditional === 'function';
84
+ }
2
85
  export class AssetIdMismatchError extends Error {
3
86
  claimed;
4
87
  actual;
@@ -12,6 +95,18 @@ export class AssetIdMismatchError extends Error {
12
95
  export class CapsuleGeneBindingError extends Error {
13
96
  constructor() { super('Capsule.gene 必须非空或显式 "ad-hoc" 哨兵 (批注#28/M3-4)'); this.name = 'CapsuleGeneBindingError'; }
14
97
  }
98
+ /**
99
+ * M3-4 强绑定校验: Capsule.gene 必须非空(否则一条无来源基因的经验会污染选择池).
100
+ * 抽成独立 helper 以便 normalizeForPut(校验落库路径)与 ingestUnverified(冻结落库路径,
101
+ * 绕过 normalizeForPut)共用同一条不变量,不让降级路径把绑定校验漏掉.
102
+ */
103
+ export function assertCapsuleGeneBinding(asset) {
104
+ if (asset.type !== 'Capsule')
105
+ return;
106
+ const gene = asset.gene;
107
+ if (typeof gene !== 'string' || gene.length === 0)
108
+ throw new CapsuleGeneBindingError();
109
+ }
15
110
  /**
16
111
  * 落库前规范化(共享给各 provider): 计算/校验 asset_id + 强绑定校验.
17
112
  * - 缺 asset_id → 计算填入(verified=false 表示非入参自带).
@@ -19,11 +114,7 @@ export class CapsuleGeneBindingError extends Error {
19
114
  * - Capsule.gene 必须非空(M3-4 强绑定).
20
115
  */
21
116
  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
- }
117
+ assertCapsuleGeneBinding(asset);
27
118
  const actual = computeAssetId(asset);
28
119
  if (actual === null)
29
120
  throw new Error('computeAssetId 失败: 资产非对象');
@@ -1,5 +1,5 @@
1
1
  import type { AssetStoreProvider, AssetRecord } from './provider.js';
2
- import { ReviewLedger } from './reviewLedger.js';
2
+ import { ReviewLedger, type ReviewRecord } from './reviewLedger.js';
3
3
  import { ProvenanceStore } from './provenance.js';
4
4
  /**
5
5
  * The ReviewLedger CO-LOCATED with a store — its quarantine/approve records live in the SAME dir as the genes.
@@ -17,4 +17,22 @@ export declare function provenanceStoreForStore(store: AssetStoreProvider): Prov
17
17
  * quarantined or rejected draft is withheld. A gene with NO provenance record is trusted; hub-ingested genes are
18
18
  * withheld until promotion. This mirrors candidateAssembly's trust-first + review-first filters.
19
19
  */
20
+ /**
21
+ * Authoritative pending-review queue: quarantined ledger records, oldest first.
22
+ * Callers that display a human queue MUST start here instead of `store.list`, or a
23
+ * draft sitting past the newest-N cutoff is invisible even though it is still blocked.
24
+ */
25
+ export declare function pendingReviewRecords(review: ReviewLedger, excludeAssetIds?: ReadonlySet<string>): ReviewRecord[];
26
+ /**
27
+ * Pending Gene queue with the asset store as the type/existence authority.
28
+ *
29
+ * ReviewLedger is intentionally generic because the same sidecar also records
30
+ * AntiGene decisions. A quarantined row can also outlive its asset after a
31
+ * repair or a truncated migration. Human Gene queues must therefore resolve
32
+ * every row through the provider instead of treating the ledger alone as a
33
+ * complete inventory. The lookups are deliberately sequential so a malformed
34
+ * or unexpectedly large ledger cannot fan out an unbounded request burst to a
35
+ * remote provider.
36
+ */
37
+ export declare function pendingGeneReviewRecords(store: AssetStoreProvider, review: ReviewLedger): Promise<ReviewRecord[]>;
20
38
  export declare function listApprovedGenes(store: AssetStoreProvider, review: ReviewLedger, maxGenes: number, provenance?: ProvenanceStore): Promise<AssetRecord[]>;
@@ -28,14 +28,52 @@ export function provenanceStoreForStore(store) {
28
28
  * quarantined or rejected draft is withheld. A gene with NO provenance record is trusted; hub-ingested genes are
29
29
  * withheld until promotion. This mirrors candidateAssembly's trust-first + review-first filters.
30
30
  */
31
+ /**
32
+ * Authoritative pending-review queue: quarantined ledger records, oldest first.
33
+ * Callers that display a human queue MUST start here instead of `store.list`, or a
34
+ * draft sitting past the newest-N cutoff is invisible even though it is still blocked.
35
+ */
36
+ export function pendingReviewRecords(review, excludeAssetIds = new Set()) {
37
+ return review.records()
38
+ .filter((record) => record.state === 'quarantined' && !excludeAssetIds.has(record.assetId))
39
+ .sort((a, b) => a.at.localeCompare(b.at));
40
+ }
41
+ /**
42
+ * Pending Gene queue with the asset store as the type/existence authority.
43
+ *
44
+ * ReviewLedger is intentionally generic because the same sidecar also records
45
+ * AntiGene decisions. A quarantined row can also outlive its asset after a
46
+ * repair or a truncated migration. Human Gene queues must therefore resolve
47
+ * every row through the provider instead of treating the ledger alone as a
48
+ * complete inventory. The lookups are deliberately sequential so a malformed
49
+ * or unexpectedly large ledger cannot fan out an unbounded request burst to a
50
+ * remote provider.
51
+ */
52
+ export async function pendingGeneReviewRecords(store, review) {
53
+ const pending = pendingReviewRecords(review);
54
+ // LocalJsonlProvider 可直接使用内存索引精确回答,避免 sidecar 变大后逐行等待查询。
55
+ if (store instanceof LocalJsonlProvider) {
56
+ const geneIds = new Set(store.listAll('Gene').map((asset) => asset.asset_id));
57
+ return pending.filter((record) => geneIds.has(record.assetId));
58
+ }
59
+ const genes = [];
60
+ for (const record of pending) {
61
+ const asset = await store.get(record.assetId);
62
+ if (asset?.type === 'Gene')
63
+ genes.push(record);
64
+ }
65
+ return genes;
66
+ }
31
67
  export async function listApprovedGenes(store, review, maxGenes, provenance = provenanceStoreForStore(store)) {
32
68
  const all = await store.list('Gene', GENE_SCAN_LIMIT);
33
69
  const trust = provenance.snapshot();
70
+ const reviewed = review.snapshot();
34
71
  const approved = [];
35
72
  for (const g of all) {
36
73
  if (trust.get(String(g.asset_id))?.trusted === false)
37
74
  continue; // hub-untrusted → withhold until promoted
38
- if (!review.isApproved(String(g.asset_id)))
75
+ const reviewRecord = reviewed.get(String(g.asset_id));
76
+ if (reviewRecord !== undefined && reviewRecord.state !== 'approved')
39
77
  continue; // quarantined/rejected draft → withhold
40
78
  approved.push(g);
41
79
  if (approved.length >= maxGenes)
@@ -18,13 +18,17 @@ export interface ReviewRecord {
18
18
  export declare class ReviewLedger {
19
19
  private readonly now;
20
20
  private readonly path;
21
+ private readonly lockPath;
21
22
  private readonly index;
22
- private sig;
23
+ private fileState;
23
24
  constructor(baseDir: string, now?: () => number);
24
25
  private static isHuman;
25
26
  /** Which record wins for an asset_id: a human decision beats a quarantine; otherwise the later one wins. */
26
27
  private static keep;
27
- private load;
28
+ private rebuildIndex;
29
+ private refreshUnderLock;
30
+ private withFreshRead;
31
+ private appendUnderLock;
28
32
  /** Record a review-state for an asset_id (append-only; the JSONL history is the audit trail). */
29
33
  mark(rec: Omit<ReviewRecord, 'at'> & {
30
34
  at?: string;
@@ -50,6 +54,8 @@ export declare class ReviewLedger {
50
54
  * asset list, so a draft awaiting approval is never missed behind a store-list cutoff.
51
55
  */
52
56
  records(): ReviewRecord[];
57
+ /** One linearizable review snapshot for bounded batch readers. */
58
+ snapshot(): ReadonlyMap<string, ReviewRecord>;
53
59
  /** No record → approved (default eligible); a record → approved only when its state is 'approved'. */
54
60
  isApproved(assetId: string): boolean;
55
61
  /**
@@ -8,8 +8,12 @@
8
8
  //
9
9
  // Default (no record) = approved/eligible: the only writer of quarantine records is `--distill`; cycle-self-
10
10
  // produced and v1-migrated genes are never quarantined, so the explore→prove loop is untouched for them.
11
- import { appendFileSync, existsSync, readFileSync, mkdirSync, statSync } from 'node:fs';
12
11
  import { join, dirname } from 'node:path';
12
+ import { appendUtf8Durable, assertAssetStoreDirectory, ensureAssetStoreDirectory, readUtf8Regular, regularFileFingerprint, withAssetStoreLock, } from './assetStoreStorage.js';
13
+ import { assertTrustSidecarHealthy, parseReviewRecord, parseSidecarJsonl, } from './assetSidecarRecords.js';
14
+ function immutableRecord(record) {
15
+ return Object.freeze({ ...record });
16
+ }
13
17
  /**
14
18
  * Append-only JSONL sidecar at <baseDir>/review.jsonl. Default for an asset with NO record = approved (eligible):
15
19
  * only auto-distilled drafts are quarantined here; everything else is eligible by default.
@@ -22,11 +26,14 @@ import { join, dirname } from 'node:path';
22
26
  export class ReviewLedger {
23
27
  now;
24
28
  path;
29
+ lockPath;
25
30
  index = new Map();
26
- sig = ''; // `${mtimeMs}:${size}` of review.jsonl at the last index build; '' = never loaded / file absent
31
+ fileState = null;
27
32
  constructor(baseDir, now = Date.now) {
28
33
  this.now = now;
34
+ ensureAssetStoreDirectory(baseDir);
29
35
  this.path = join(baseDir, 'review.jsonl');
36
+ this.lockPath = join(baseDir, '.assetstore.lock');
30
37
  }
31
38
  static isHuman(s) { return s === 'approved' || s === 'rejected'; }
32
39
  /** Which record wins for an asset_id: a human decision beats a quarantine; otherwise the later one wins. */
@@ -37,43 +44,50 @@ export class ReviewLedger {
37
44
  return r; // human decision always wins (and later human beats earlier)
38
45
  return ReviewLedger.isHuman(existing.state) ? existing : r; // a quarantine replaces only a prior quarantine
39
46
  }
40
- // 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));
47
+ rebuildIndex(state) {
48
+ const next = new Map();
49
+ const raw = state === 'missing' ? null : readUtf8Regular(this.path);
50
+ if (raw !== null) {
51
+ const parsed = parseSidecarJsonl(raw, parseReviewRecord);
52
+ assertTrustSidecarHealthy('review', parsed);
53
+ for (const record of parsed.records) {
54
+ const frozen = immutableRecord(record);
55
+ next.set(record.assetId, ReviewLedger.keep(next.get(record.assetId), frozen));
62
56
  }
63
- catch { /* skip corrupt line */ }
64
57
  }
65
- this.sig = sig;
58
+ this.index.clear();
59
+ for (const [assetId, record] of next)
60
+ this.index.set(assetId, record);
61
+ this.fileState = state;
62
+ }
63
+ refreshUnderLock() {
64
+ const state = regularFileFingerprint(this.path);
65
+ if (state !== this.fileState)
66
+ this.rebuildIndex(state);
67
+ }
68
+ withFreshRead(read) {
69
+ assertAssetStoreDirectory(dirname(this.path));
70
+ return withAssetStoreLock(this.lockPath, () => {
71
+ this.refreshUnderLock();
72
+ return read(this.index);
73
+ });
74
+ }
75
+ appendUnderLock(full) {
76
+ appendUtf8Durable(this.path, `${JSON.stringify(full)}\n`);
77
+ const frozen = immutableRecord(full);
78
+ this.index.set(full.assetId, ReviewLedger.keep(this.index.get(full.assetId), frozen));
79
+ this.fileState = regularFileFingerprint(this.path);
80
+ return this.index.get(full.assetId);
66
81
  }
67
82
  /** Record a review-state for an asset_id (append-only; the JSONL history is the audit trail). */
68
83
  mark(rec) {
69
- this.load();
70
84
  const full = { ...rec, at: rec.at ?? new Date(this.now()).toISOString() };
71
- 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;
85
+ assertAssetStoreDirectory(dirname(this.path));
86
+ return withAssetStoreLock(this.lockPath, () => {
87
+ this.refreshUnderLock();
88
+ this.appendUnderLock(full);
89
+ return immutableRecord(full);
90
+ });
77
91
  }
78
92
  /** Quarantine an auto-distilled draft: its strategy must not enter a real run until reviewed. */
79
93
  quarantine(assetId, reason = 'auto-distilled — review before use') {
@@ -87,9 +101,19 @@ export class ReviewLedger {
87
101
  * approval is inert. Returns the surviving record (the existing decision, or the new quarantine).
88
102
  */
89
103
  quarantineIfAbsent(assetId, reason = 'auto-distilled — review before use') {
90
- this.load();
91
- const existing = this.index.get(assetId);
92
- return existing ?? this.quarantine(assetId, reason);
104
+ assertAssetStoreDirectory(dirname(this.path));
105
+ return withAssetStoreLock(this.lockPath, () => {
106
+ this.refreshUnderLock();
107
+ const existing = this.index.get(assetId);
108
+ if (existing)
109
+ return existing;
110
+ return this.appendUnderLock({
111
+ assetId,
112
+ state: 'quarantined',
113
+ reason,
114
+ at: new Date(this.now()).toISOString(),
115
+ });
116
+ });
93
117
  }
94
118
  /** Explicit, audited approval (who/why) — flips a draft to eligible. */
95
119
  approve(assetId, by, reason) {
@@ -100,8 +124,7 @@ export class ReviewLedger {
100
124
  return this.mark({ assetId, state: 'rejected', by, reason });
101
125
  }
102
126
  get(assetId) {
103
- this.load();
104
- return this.index.get(assetId) ?? null;
127
+ return this.withFreshRead((index) => index.get(assetId) ?? null);
105
128
  }
106
129
  /**
107
130
  * Every recorded review decision (resolved, reload-aware). The authoritative source of which assets are
@@ -109,14 +132,18 @@ export class ReviewLedger {
109
132
  * asset list, so a draft awaiting approval is never missed behind a store-list cutoff.
110
133
  */
111
134
  records() {
112
- this.load();
113
- return [...this.index.values()];
135
+ return [...this.snapshot().values()];
136
+ }
137
+ /** One linearizable review snapshot for bounded batch readers. */
138
+ snapshot() {
139
+ return this.withFreshRead((index) => new Map(index));
114
140
  }
115
141
  /** No record → approved (default eligible); a record → approved only when its state is 'approved'. */
116
142
  isApproved(assetId) {
117
- this.load();
118
- const r = this.index.get(assetId);
119
- return r ? r.state === 'approved' : true;
143
+ return this.withFreshRead((index) => {
144
+ const r = index.get(assetId);
145
+ return r ? r.state === 'approved' : true;
146
+ });
120
147
  }
121
148
  /**
122
149
  * True only when a human approval record exists. Safety-sensitive consumers such as AntiGene warning
@@ -124,7 +151,6 @@ export class ReviewLedger {
124
151
  * negative-memory asset must never inherit the ledger's backward-compatible "no record = eligible" default.
125
152
  */
126
153
  isExplicitlyApproved(assetId) {
127
- this.load();
128
- return this.index.get(assetId)?.state === 'approved';
154
+ return this.withFreshRead((index) => index.get(assetId)?.state === 'approved');
129
155
  }
130
156
  }
@@ -0,0 +1,25 @@
1
+ import type { AssetKind, AssetRecord, AssetStoreProvider, ConditionalPutOptions, ConditionalPutResult, PutResult, SearchQuery } from './provider.js';
2
+ export declare class UnionReadStore implements AssetStoreProvider {
3
+ private readonly primary;
4
+ private readonly readOnly;
5
+ readonly putConditional?: (asset: AssetRecord, options?: ConditionalPutOptions) => Promise<ConditionalPutResult>;
6
+ readonly putBundle?: (assets: readonly AssetRecord[]) => Promise<PutResult[]>;
7
+ readonly putFrozen?: (record: AssetRecord) => Promise<PutResult>;
8
+ readonly putFrozenConditional?: (record: AssetRecord, options?: ConditionalPutOptions) => Promise<ConditionalPutResult>;
9
+ readonly findByLogicalId?: (id: string, limit?: number, kind?: AssetKind) => Promise<AssetRecord[]>;
10
+ /**
11
+ * @param primary the writable pool this engine owns; every mutation lands here
12
+ * @param readOnly additional pools to read, in precedence order after `primary`
13
+ */
14
+ constructor(primary: AssetStoreProvider, readOnly: readonly AssetStoreProvider[]);
15
+ private get sources();
16
+ /**
17
+ * Merge one read across every source: deduplicate by asset_id with earlier sources winning, then fill
18
+ * the result by {@link interleave} so every source gets a fair share of the cap.
19
+ */
20
+ private merged;
21
+ put(asset: AssetRecord): Promise<PutResult>;
22
+ get(assetId: string): Promise<AssetRecord | null>;
23
+ list(kind?: AssetKind, limit?: number): Promise<AssetRecord[]>;
24
+ search(q: SearchQuery): Promise<AssetRecord[]>;
25
+ }
@@ -0,0 +1,119 @@
1
+ // Read-union over one writable pool plus any number of read-only ones.
2
+ //
3
+ // Why a provider and not a helper: every evidence consumer already takes an AssetStoreProvider and calls
4
+ // `search`/`list` on it — aggregateLearningHistory, assessGenePublishEvidence, candidateAssembly,
5
+ // probationWouldPromote. Widening what they can see by threading a second store through each of them
6
+ // would fork four call chains and leave the next consumer to remember. Composing at the provider seam
7
+ // instead means they are untouched: whoever builds the store decides how wide the evidence pool is.
8
+ //
9
+ // Writes go to the primary and ONLY the primary. Reads merge, deduplicating by asset_id — records are
10
+ // content-addressed, so the same asset present in two pools is the same asset, and primary wins so a
11
+ // locally-verified record is never shadowed by a foreign copy.
12
+ const DEFAULT_LIMIT = 1000;
13
+ /**
14
+ * Take from the queues one at a time in source order until `limit` is reached.
15
+ *
16
+ * Concatenating instead would let a mature primary fill every slot on its own — at the ~1000-record
17
+ * limits the real callers pass (candidateAssembly, recall, reviewFilter), a local pool that size returns
18
+ * ZERO foreign records and the union silently degrades back to the single-store blind spot it exists to
19
+ * fix. Interleaving also refills: a source that runs out yields its remaining slots to the others, so a
20
+ * small foreign pool never costs the result its cap.
21
+ */
22
+ function interleave(queues, limit) {
23
+ const out = [];
24
+ for (let cursor = 0; out.length < limit; cursor += 1) {
25
+ let advanced = false;
26
+ for (const queue of queues) {
27
+ const record = queue[cursor];
28
+ if (record === undefined)
29
+ continue;
30
+ advanced = true;
31
+ out.push(record);
32
+ if (out.length >= limit)
33
+ return out;
34
+ }
35
+ if (!advanced)
36
+ return out;
37
+ }
38
+ return out;
39
+ }
40
+ export class UnionReadStore {
41
+ primary;
42
+ readOnly;
43
+ // Mirrored as assigned-in-constructor properties rather than plain methods on purpose: the optional
44
+ // write capabilities are feature-DETECTED by callers (`supportsAtomicConditionalPut`, and
45
+ // `ingestUnverified`'s `putFrozen` probe). A method declared on the class is always present, so a
46
+ // union wrapping a primary that lacks the capability would answer "supported" and then throw — the
47
+ // undeclared-exception failure those probes exist to prevent. Assigning only when the primary has it
48
+ // keeps the union exactly as capable as the pool it writes to.
49
+ putConditional;
50
+ putBundle;
51
+ putFrozen;
52
+ putFrozenConditional;
53
+ // Conditional for the same reason, on the read side: cliContracts.findLogicalAsset and
54
+ // assetLineage.resolveAsset feature-detect this, and when it is absent fall back to a wide scan that
55
+ // REPORTS truncation. Answering on behalf of a source that cannot resolve logical ids would strand
56
+ // those callers on a narrow answer with no truncation signal — a silent "not found" where they would
57
+ // otherwise have said "lookup is truncated; use an exact asset_id".
58
+ findByLogicalId;
59
+ /**
60
+ * @param primary the writable pool this engine owns; every mutation lands here
61
+ * @param readOnly additional pools to read, in precedence order after `primary`
62
+ */
63
+ constructor(primary, readOnly) {
64
+ this.primary = primary;
65
+ this.readOnly = readOnly;
66
+ if ([primary, ...readOnly].every((source) => source.findByLogicalId)) {
67
+ this.findByLogicalId = (id, limit = DEFAULT_LIMIT, kind) => this.merged((source) => source.findByLogicalId(id, limit, kind), limit);
68
+ }
69
+ const { putBundle, putConditional, putFrozen, putFrozenConditional } = primary;
70
+ if (putBundle)
71
+ this.putBundle = (assets) => putBundle.call(primary, assets);
72
+ if (putConditional)
73
+ this.putConditional = (asset, options) => putConditional.call(primary, asset, options);
74
+ if (putFrozen)
75
+ this.putFrozen = (record) => putFrozen.call(primary, record);
76
+ if (putFrozenConditional) {
77
+ this.putFrozenConditional = (record, options) => putFrozenConditional.call(primary, record, options);
78
+ }
79
+ }
80
+ get sources() {
81
+ return [this.primary, ...this.readOnly];
82
+ }
83
+ /**
84
+ * Merge one read across every source: deduplicate by asset_id with earlier sources winning, then fill
85
+ * the result by {@link interleave} so every source gets a fair share of the cap.
86
+ */
87
+ async merged(read, limit) {
88
+ const seen = new Set();
89
+ const queues = [];
90
+ for (const source of this.sources) {
91
+ const unique = [];
92
+ for (const record of await read(source)) {
93
+ if (seen.has(record.asset_id))
94
+ continue;
95
+ seen.add(record.asset_id);
96
+ unique.push(record);
97
+ }
98
+ queues.push(unique);
99
+ }
100
+ return interleave(queues, limit);
101
+ }
102
+ async put(asset) {
103
+ return this.primary.put(asset);
104
+ }
105
+ async get(assetId) {
106
+ for (const source of this.sources) {
107
+ const found = await source.get(assetId);
108
+ if (found)
109
+ return found;
110
+ }
111
+ return null;
112
+ }
113
+ async list(kind, limit = DEFAULT_LIMIT) {
114
+ return this.merged((source) => source.list(kind, limit), limit);
115
+ }
116
+ async search(q) {
117
+ return this.merged((source) => source.search(q), q.limit ?? DEFAULT_LIMIT);
118
+ }
119
+ }
@@ -70,6 +70,8 @@ export interface AntiGeneBenchmarkOptions {
70
70
  minFailureDelta?: number;
71
71
  now?: () => number;
72
72
  eventsPath?: string;
73
+ /** Apply the pre-IDF semantic scorer consistently to both A/B arms. */
74
+ disableSemanticIdf?: boolean;
73
75
  }
74
76
  export interface AntiGeneBenchmarkDeps {
75
77
  store: AssetStoreProvider;
@@ -79,7 +79,7 @@ function executeFor(outcome) {
79
79
  },
80
80
  });
81
81
  }
82
- async function runOneArm(arm, task, source, sourceReview, rootDir, now) {
82
+ async function runOneArm(arm, task, source, sourceReview, rootDir, now, disableSemanticIdf) {
83
83
  const armDir = join(rootDir, arm, task.id);
84
84
  const store = new LocalJsonlProvider(join(armDir, 'assets'));
85
85
  const review = new ReviewLedger(join(armDir, 'assets'), now);
@@ -105,6 +105,7 @@ async function runOneArm(arm, task, source, sourceReview, rootDir, now) {
105
105
  execute: executeFor(spec),
106
106
  review,
107
107
  consumePendingSignals: false,
108
+ ...(disableSemanticIdf ? { disableSemanticIdf: true } : {}),
108
109
  };
109
110
  const result = await runEvolutionCycle(engine, store, opts);
110
111
  const observed = observedWarningIds(result.decision);
@@ -168,8 +169,8 @@ export async function runAntiGeneBenchmark(suite, deps, options = {}) {
168
169
  for (const task of suite.tasks) {
169
170
  taskResults.push({
170
171
  taskId: task.id,
171
- baseline: await runOneArm('baseline', task, deps.store, sourceReview, rootDir, now),
172
- antiGene: await runOneArm('antiGene', task, deps.store, sourceReview, rootDir, now),
172
+ baseline: await runOneArm('baseline', task, deps.store, sourceReview, rootDir, now, options.disableSemanticIdf === true),
173
+ antiGene: await runOneArm('antiGene', task, deps.store, sourceReview, rootDir, now, options.disableSemanticIdf === true),
173
174
  });
174
175
  }
175
176
  const report = buildReport(suite, taskResults, minSamples, minFailureDelta);
@@ -75,6 +75,8 @@ export interface AntiGeneRolloutOptions {
75
75
  minFailureDelta?: number;
76
76
  now?: () => number;
77
77
  eventsPath?: string;
78
+ /** Apply the pre-IDF semantic scorer consistently to both A/B arms. */
79
+ disableSemanticIdf?: boolean;
78
80
  }
79
81
  export declare function runAntiGeneRollout(suite: AntiGeneRolloutSuite, deps: AntiGeneRolloutDeps, options?: AntiGeneRolloutOptions): Promise<AntiGeneRolloutReport>;
80
82
  export declare function writeAntiGeneRolloutResult(report: AntiGeneRolloutReport, eventsPath: string, now?: () => number): Promise<void>;
@@ -82,7 +82,7 @@ function scoreFromResult(result, status) {
82
82
  const score = result.capsule?.outcome?.score;
83
83
  return typeof score === 'number' && Number.isFinite(score) ? score : status === 'success' ? 0.9 : 0.2;
84
84
  }
85
- async function runOneArm(arm, task, source, sourceReview, makeExecute, rootDir, now) {
85
+ async function runOneArm(arm, task, source, sourceReview, makeExecute, rootDir, now, disableSemanticIdf) {
86
86
  const armDir = join(rootDir, arm, task.id);
87
87
  const store = new LocalJsonlProvider(join(armDir, 'assets'));
88
88
  const review = new ReviewLedger(join(armDir, 'assets'), now);
@@ -107,6 +107,7 @@ async function runOneArm(arm, task, source, sourceReview, makeExecute, rootDir,
107
107
  execute: makeExecute({ arm, task, store, review, armDir }),
108
108
  review,
109
109
  consumePendingSignals: false,
110
+ ...(disableSemanticIdf ? { disableSemanticIdf: true } : {}),
110
111
  };
111
112
  const result = await runEvolutionCycle(engine, store, opts);
112
113
  const status = statusFromResult(result);
@@ -183,8 +184,8 @@ export async function runAntiGeneRollout(suite, deps, options = {}) {
183
184
  for (const task of suite.tasks) {
184
185
  taskResults.push({
185
186
  taskId: task.id,
186
- baseline: await runOneArm('baseline', task, deps.store, sourceReview, deps.makeExecute, rootDir, now),
187
- antiGene: await runOneArm('antiGene', task, deps.store, sourceReview, deps.makeExecute, rootDir, now),
187
+ baseline: await runOneArm('baseline', task, deps.store, sourceReview, deps.makeExecute, rootDir, now, options.disableSemanticIdf === true),
188
+ antiGene: await runOneArm('antiGene', task, deps.store, sourceReview, deps.makeExecute, rootDir, now, options.disableSemanticIdf === true),
188
189
  });
189
190
  }
190
191
  const report = buildReport(suite, taskResults, minSamples, minFailureDelta);
@@ -2,4 +2,6 @@ export * from './benchmark.js';
2
2
  export * from './thesis.js';
3
3
  export * from './evolutionThesisSolver.js';
4
4
  export * from './antiGeneBenchmark.js';
5
- export * from './antiGeneRollout.js';
5
+ export * from './antiGeneRollout.js';
6
+ export * from './triggerShift.js';
7
+ export * from './selectionFlatAbstention.js';
@@ -2,4 +2,6 @@ export * from './benchmark.js';
2
2
  export * from './thesis.js';
3
3
  export * from './evolutionThesisSolver.js';
4
4
  export * from './antiGeneBenchmark.js';
5
- export * from './antiGeneRollout.js';
5
+ export * from './antiGeneRollout.js';
6
+ export * from './triggerShift.js';
7
+ export * from './selectionFlatAbstention.js';