opencode-swarm 7.115.0 → 7.116.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.
Files changed (127) hide show
  1. package/README.md +3 -1
  2. package/dist/cli/{curator-hpc4tsjv.js → curator-7s5yw6hw.js} +15 -14
  3. package/dist/cli/{curator-llm-factory-6n9sgaj8.js → curator-llm-factory-x96dr0zf.js} +15 -14
  4. package/dist/cli/{dispatch-mczsxy5s.js → dispatch-07jgc3hn.js} +1 -1
  5. package/dist/cli/{evidence-summary-service-fehwj116.js → evidence-summary-service-ef6kgtev.js} +5 -5
  6. package/dist/cli/{gate-evidence-vkpqt300.js → gate-evidence-thqt0beq.js} +4 -4
  7. package/dist/cli/{guardrail-explain-hecpd738.js → guardrail-explain-kwqfkwch.js} +16 -15
  8. package/dist/cli/{hive-promoter-0kfb4vjk.js → hive-promoter-rkkx40qv.js} +15 -14
  9. package/dist/cli/{index-2etc05tv.js → index-3b8n3dbd.js} +26 -18
  10. package/dist/cli/{index-8f2270hc.js → index-3yh67t1h.js} +3 -3
  11. package/dist/cli/{index-4sckv7y6.js → index-42cx6g2h.js} +9 -4
  12. package/dist/cli/index-4905hd2m.js +19 -0
  13. package/dist/cli/{index-4h2ea8h5.js → index-81vzywh3.js} +2 -2
  14. package/dist/cli/{index-6f6y2rbp.js → index-8bvh0qfa.js} +3 -3
  15. package/dist/cli/{index-80jkseqw.js → index-a6f8b0vn.js} +1 -1
  16. package/dist/cli/{index-0p660a87.js → index-bbsaxnj5.js} +1 -1
  17. package/dist/cli/{index-yrrqs4b0.js → index-cnrhj56z.js} +1160 -47
  18. package/dist/cli/{index-n18yy0z1.js → index-jyetmzcn.js} +3 -3
  19. package/dist/cli/{index-rk6qhyng.js → index-k5ey429j.js} +4 -4
  20. package/dist/cli/{index-m766gvdt.js → index-me46ffm2.js} +6804 -1940
  21. package/dist/cli/{index-ga4ta3cr.js → index-nprawcc0.js} +7 -1
  22. package/dist/cli/{index-pa6t33pt.js → index-q2rrv0cy.js} +11 -0
  23. package/dist/cli/{index-g40r5d08.js → index-qgeeyy75.js} +1 -1
  24. package/dist/cli/{index-x12hvfpf.js → index-s385axt1.js} +3 -3
  25. package/dist/cli/{index-m0rce3x6.js → index-tvgvb5pa.js} +1 -1
  26. package/dist/cli/index.js +15 -14
  27. package/dist/cli/{knowledge-escalator-5gwp1ar8.js → knowledge-escalator-0bq62d34.js} +7 -7
  28. package/dist/cli/{knowledge-events-m304swh6.js → knowledge-events-cwwky393.js} +5 -5
  29. package/dist/cli/{knowledge-store-9babt8rd.js → knowledge-store-q8x4h82m.js} +5 -5
  30. package/dist/cli/{knowledge-validator-t9sym59g.js → knowledge-validator-zr0djm02.js} +6 -6
  31. package/dist/cli/{pending-delegations-ddbkbgxy.js → pending-delegations-rr9x47rd.js} +2 -2
  32. package/dist/cli/{pr-subscriptions-vwv7zm2p.js → pr-subscriptions-cny2jz2d.js} +3 -3
  33. package/dist/cli/runtime-ykafcwc6.js +3397 -0
  34. package/dist/cli/{skill-generator-tav44xpm.js → skill-generator-mz267btk.js} +8 -8
  35. package/dist/cli/{telemetry-jny76r83.js → telemetry-qh78ymq4.js} +1 -1
  36. package/dist/commands/archive.d.ts +4 -4
  37. package/dist/commands/command-dispatch.d.ts +2 -0
  38. package/dist/commands/gate-audit.d.ts +28 -0
  39. package/dist/commands/gate-stats.d.ts +1 -0
  40. package/dist/commands/index.d.ts +4 -0
  41. package/dist/commands/registry.d.ts +19 -2
  42. package/dist/config/plan-schema.d.ts +7 -7
  43. package/dist/evaluation/contracts.d.ts +545 -0
  44. package/dist/evaluation/disposable-worktree.d.ts +38 -0
  45. package/dist/evaluation/fixtures.d.ts +8 -0
  46. package/dist/evaluation/gate-audit.d.ts +32 -0
  47. package/dist/evaluation/gate-ground-truth.d.ts +43 -0
  48. package/dist/evaluation/gate-stats.d.ts +47 -0
  49. package/dist/evaluation/hashing.d.ts +34 -0
  50. package/dist/evaluation/index.d.ts +17 -0
  51. package/dist/evaluation/model-dispatcher.d.ts +30 -0
  52. package/dist/evaluation/public-api.d.ts +87 -0
  53. package/dist/evaluation/retention.d.ts +56 -0
  54. package/dist/evaluation/runner.d.ts +48 -0
  55. package/dist/evaluation/statistics.d.ts +9 -0
  56. package/dist/evaluation/store.d.ts +26 -0
  57. package/dist/evidence/manager.d.ts +18 -0
  58. package/dist/index.d.ts +4 -0
  59. package/dist/index.js +461 -447
  60. package/dist/mutation/engine.d.ts +29 -5
  61. package/dist/telemetry.d.ts +11 -1
  62. package/dist/tools/plugin-registration.d.ts +2 -1
  63. package/dist/tools/sast-scan.d.ts +9 -0
  64. package/dist/tools/swarm-command.d.ts +2 -1
  65. package/dist/tools/update-task-status.d.ts +16 -4
  66. package/dist/utils/external-tool-runner.d.ts +2 -1
  67. package/evaluation-fixtures/tier1/boundary-error/environment/baseline.ts +3 -0
  68. package/evaluation-fixtures/tier1/boundary-error/environment/defect.test.ts +3 -0
  69. package/evaluation-fixtures/tier1/boundary-error/environment/defect.ts +3 -0
  70. package/evaluation-fixtures/tier1/boundary-error/instruction.md +1 -0
  71. package/evaluation-fixtures/tier1/boundary-error/manifest.json +1 -0
  72. package/evaluation-fixtures/tier1/branch-swap/environment/baseline.ts +3 -0
  73. package/evaluation-fixtures/tier1/branch-swap/environment/defect.test.ts +3 -0
  74. package/evaluation-fixtures/tier1/branch-swap/environment/defect.ts +3 -0
  75. package/evaluation-fixtures/tier1/branch-swap/instruction.md +1 -0
  76. package/evaluation-fixtures/tier1/branch-swap/manifest.json +1 -0
  77. package/evaluation-fixtures/tier1/curated-off-by-one/environment/baseline.ts +3 -0
  78. package/evaluation-fixtures/tier1/curated-off-by-one/environment/defect.test.ts +3 -0
  79. package/evaluation-fixtures/tier1/curated-off-by-one/environment/defect.ts +3 -0
  80. package/evaluation-fixtures/tier1/curated-off-by-one/instruction.md +1 -0
  81. package/evaluation-fixtures/tier1/curated-off-by-one/manifest.json +1 -0
  82. package/evaluation-fixtures/tier1/guard-removal/environment/baseline.ts +3 -0
  83. package/evaluation-fixtures/tier1/guard-removal/environment/defect.test.ts +3 -0
  84. package/evaluation-fixtures/tier1/guard-removal/environment/defect.ts +3 -0
  85. package/evaluation-fixtures/tier1/guard-removal/instruction.md +1 -0
  86. package/evaluation-fixtures/tier1/guard-removal/manifest.json +1 -0
  87. package/evaluation-fixtures/tier1/injection-prone-string/environment/baseline.ts +3 -0
  88. package/evaluation-fixtures/tier1/injection-prone-string/environment/defect.test.ts +3 -0
  89. package/evaluation-fixtures/tier1/injection-prone-string/environment/defect.ts +3 -0
  90. package/evaluation-fixtures/tier1/injection-prone-string/instruction.md +1 -0
  91. package/evaluation-fixtures/tier1/injection-prone-string/manifest.json +1 -0
  92. package/evaluation-fixtures/tier1/missing-auth-check/environment/baseline.ts +4 -0
  93. package/evaluation-fixtures/tier1/missing-auth-check/environment/defect.test.ts +3 -0
  94. package/evaluation-fixtures/tier1/missing-auth-check/environment/defect.ts +3 -0
  95. package/evaluation-fixtures/tier1/missing-auth-check/instruction.md +1 -0
  96. package/evaluation-fixtures/tier1/missing-auth-check/manifest.json +1 -0
  97. package/evaluation-fixtures/tier1/missing-await/environment/baseline.ts +7 -0
  98. package/evaluation-fixtures/tier1/missing-await/environment/defect.test.ts +3 -0
  99. package/evaluation-fixtures/tier1/missing-await/environment/defect.ts +4 -0
  100. package/evaluation-fixtures/tier1/missing-await/instruction.md +1 -0
  101. package/evaluation-fixtures/tier1/missing-await/manifest.json +1 -0
  102. package/evaluation-fixtures/tier1/mutation-off-by-one/environment/baseline.ts +3 -0
  103. package/evaluation-fixtures/tier1/mutation-off-by-one/environment/defect.test.ts +3 -0
  104. package/evaluation-fixtures/tier1/mutation-off-by-one/environment/defect.ts +3 -0
  105. package/evaluation-fixtures/tier1/mutation-off-by-one/instruction.md +1 -0
  106. package/evaluation-fixtures/tier1/mutation-off-by-one/manifest.json +1 -0
  107. package/evaluation-fixtures/tier1/null-substitution/environment/baseline.ts +3 -0
  108. package/evaluation-fixtures/tier1/null-substitution/environment/defect.test.ts +3 -0
  109. package/evaluation-fixtures/tier1/null-substitution/environment/defect.ts +3 -0
  110. package/evaluation-fixtures/tier1/null-substitution/instruction.md +1 -0
  111. package/evaluation-fixtures/tier1/null-substitution/manifest.json +1 -0
  112. package/evaluation-fixtures/tier1/operator-swap/environment/baseline.ts +3 -0
  113. package/evaluation-fixtures/tier1/operator-swap/environment/defect.test.ts +3 -0
  114. package/evaluation-fixtures/tier1/operator-swap/environment/defect.ts +3 -0
  115. package/evaluation-fixtures/tier1/operator-swap/instruction.md +1 -0
  116. package/evaluation-fixtures/tier1/operator-swap/manifest.json +1 -0
  117. package/evaluation-fixtures/tier1/side-effect-deletion/environment/baseline.ts +4 -0
  118. package/evaluation-fixtures/tier1/side-effect-deletion/environment/defect.test.ts +3 -0
  119. package/evaluation-fixtures/tier1/side-effect-deletion/environment/defect.ts +3 -0
  120. package/evaluation-fixtures/tier1/side-effect-deletion/instruction.md +1 -0
  121. package/evaluation-fixtures/tier1/side-effect-deletion/manifest.json +1 -0
  122. package/evaluation-fixtures/tier1/swallowed-error/environment/baseline.ts +3 -0
  123. package/evaluation-fixtures/tier1/swallowed-error/environment/defect.test.ts +3 -0
  124. package/evaluation-fixtures/tier1/swallowed-error/environment/defect.ts +3 -0
  125. package/evaluation-fixtures/tier1/swallowed-error/instruction.md +1 -0
  126. package/evaluation-fixtures/tier1/swallowed-error/manifest.json +1 -0
  127. package/package.json +2 -1
@@ -27,17 +27,20 @@ import {
27
27
  readTaskEvidence,
28
28
  readTaskEvidenceRaw,
29
29
  sanitizeTaskId
30
- } from "./index-4h2ea8h5.js";
30
+ } from "./index-81vzywh3.js";
31
+ import {
32
+ atomicWriteFile
33
+ } from "./index-bbsaxnj5.js";
31
34
  import {
32
35
  ZodError,
33
36
  exports_external
34
37
  } from "./index-293f68mj.js";
35
38
  import {
36
39
  withEvidenceLock
37
- } from "./index-4sckv7y6.js";
40
+ } from "./index-42cx6g2h.js";
38
41
  import {
39
42
  emit
40
- } from "./index-pa6t33pt.js";
43
+ } from "./index-q2rrv0cy.js";
41
44
  import {
42
45
  bunHash,
43
46
  bunWrite,
@@ -3868,13 +3871,13 @@ function migrateLegacyPlan(planContent, swarmId) {
3868
3871
  import {
3869
3872
  mkdirSync as mkdirSync6,
3870
3873
  readdirSync as readdirSync3,
3871
- realpathSync,
3874
+ realpathSync as realpathSync2,
3872
3875
  rmSync,
3873
3876
  statSync as statSync2
3874
3877
  } from "fs";
3875
- import * as fs6 from "fs/promises";
3878
+ import * as fs7 from "fs/promises";
3876
3879
  import * as os4 from "os";
3877
- import * as path10 from "path";
3880
+ import * as path13 from "path";
3878
3881
 
3879
3882
  // src/config/evidence-schema.ts
3880
3883
  var EVIDENCE_MAX_JSON_BYTES = 500 * 1024;
@@ -4158,6 +4161,1081 @@ var EvidenceBundleSchema = exports_external.object({
4158
4161
  updated_at: exports_external.string().datetime()
4159
4162
  });
4160
4163
 
4164
+ // src/evaluation/retention.ts
4165
+ import * as fs6 from "fs/promises";
4166
+ import * as path12 from "path";
4167
+
4168
+ // src/evaluation/contracts.ts
4169
+ var IdentifierSchema = exports_external.string().min(1).max(160).regex(/^[A-Za-z0-9][A-Za-z0-9._-]*$/);
4170
+ var Sha256Schema = exports_external.string().regex(/^[a-f0-9]{64}$/);
4171
+ var IsoDateSchema = exports_external.iso.datetime({ offset: true });
4172
+ var RelativePathSchema = exports_external.string().min(1).max(1024).refine((value) => !value.includes("\x00"), "path contains a NUL byte").refine((value) => !/^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(value), {
4173
+ message: "path must be project-relative"
4174
+ }).refine((value) => !value.replace(/\\/g, "/").split("/").some((segment) => segment === ".."), "path must not traverse outside its root");
4175
+ var EvaluationSplitSchema = exports_external.enum(["train", "validation", "test"]);
4176
+ var CostRecordSchema = exports_external.object({
4177
+ source: exports_external.enum(["reported", "estimated", "unavailable"]),
4178
+ usd: exports_external.number().finite().nonnegative().optional()
4179
+ }).strict().superRefine((value, ctx) => {
4180
+ if (value.source === "unavailable" && value.usd !== undefined) {
4181
+ ctx.addIssue({
4182
+ code: "custom",
4183
+ path: ["usd"],
4184
+ message: "unavailable costs cannot declare a USD value"
4185
+ });
4186
+ }
4187
+ if (value.source !== "unavailable" && value.usd === undefined) {
4188
+ ctx.addIssue({
4189
+ code: "custom",
4190
+ path: ["usd"],
4191
+ message: "reported and estimated costs require a USD value"
4192
+ });
4193
+ }
4194
+ });
4195
+ var HumanReviewReceiptSchema = exports_external.object({
4196
+ reviewer: exports_external.string().min(1).max(200),
4197
+ reviewedAt: IsoDateSchema,
4198
+ instruction: exports_external.literal(true),
4199
+ fixture: exports_external.literal(true),
4200
+ scorer: exports_external.literal(true),
4201
+ secretsPrivacy: exports_external.literal(true),
4202
+ license: exports_external.literal(true),
4203
+ split: exports_external.literal(true)
4204
+ }).strict();
4205
+ var EvaluationTaskV1Schema = exports_external.object({
4206
+ v: exports_external.literal(1),
4207
+ id: IdentifierSchema,
4208
+ derivedFromTaskId: IdentifierSchema.optional(),
4209
+ source: exports_external.enum(["curated", "trace-proposal"]),
4210
+ split: EvaluationSplitSchema,
4211
+ category: IdentifierSchema,
4212
+ protected: exports_external.boolean().default(false),
4213
+ instructionPath: RelativePathSchema,
4214
+ environment: exports_external.discriminatedUnion("kind", [
4215
+ exports_external.object({ kind: exports_external.literal("fixture"), path: RelativePathSchema }).strict(),
4216
+ exports_external.object({ kind: exports_external.literal("container"), path: RelativePathSchema }).strict()
4217
+ ]),
4218
+ scorer: exports_external.object({
4219
+ kind: exports_external.enum(["builtin", "project"]),
4220
+ argv: exports_external.array(exports_external.string().min(1).max(4096)).min(1).max(64),
4221
+ timeoutMs: exports_external.number().int().min(100).max(600000),
4222
+ scoreRange: exports_external.tuple([exports_external.number().finite(), exports_external.number().finite()]).refine(([minimum, maximum]) => maximum > minimum, {
4223
+ message: "score range maximum must be greater than minimum"
4224
+ })
4225
+ }).strict(),
4226
+ provenance: exports_external.object({
4227
+ origin: exports_external.string().min(1).max(2048),
4228
+ license: exports_external.string().min(1).max(200),
4229
+ collectedAt: IsoDateSchema.optional(),
4230
+ review: HumanReviewReceiptSchema.optional()
4231
+ }).strict(),
4232
+ contentHash: Sha256Schema
4233
+ }).strict().superRefine((task, ctx) => {
4234
+ if (task.derivedFromTaskId === task.id) {
4235
+ ctx.addIssue({
4236
+ code: "custom",
4237
+ path: ["derivedFromTaskId"],
4238
+ message: "a derived task must reference a different parent task"
4239
+ });
4240
+ }
4241
+ if (task.source === "trace-proposal" && !task.provenance.review) {
4242
+ ctx.addIssue({
4243
+ code: "custom",
4244
+ path: ["provenance", "review"],
4245
+ message: "trace-derived tasks require an explicit human review receipt"
4246
+ });
4247
+ }
4248
+ if (task.scorer.kind === "project" && !RelativePathSchema.safeParse(task.scorer.argv[0]).success) {
4249
+ ctx.addIssue({
4250
+ code: "custom",
4251
+ path: ["scorer", "argv", 0],
4252
+ message: "project scorer executable must be project-relative"
4253
+ });
4254
+ }
4255
+ });
4256
+ var EvaluationCandidateV1Schema = exports_external.object({
4257
+ v: exports_external.literal(1),
4258
+ id: IdentifierSchema,
4259
+ kind: exports_external.enum(["baseline", "skill", "harness"]),
4260
+ payloadPath: RelativePathSchema,
4261
+ model: exports_external.string().min(1).max(300),
4262
+ agent: exports_external.string().min(1).max(160).optional(),
4263
+ contentHash: Sha256Schema
4264
+ }).strict();
4265
+ var EvaluationOutcomeSchema = exports_external.enum([
4266
+ "scored",
4267
+ "infrastructure_failure",
4268
+ "timeout",
4269
+ "cancelled",
4270
+ "malformed",
4271
+ "unsupported",
4272
+ "integrity_failure"
4273
+ ]);
4274
+ var EvaluationResultV1Schema = exports_external.object({
4275
+ v: exports_external.literal(1),
4276
+ taskId: IdentifierSchema,
4277
+ category: IdentifierSchema,
4278
+ protected: exports_external.boolean().default(false),
4279
+ repetition: exports_external.number().int().nonnegative(),
4280
+ candidateId: IdentifierSchema,
4281
+ seed: exports_external.string().min(1).max(500),
4282
+ outcome: EvaluationOutcomeSchema,
4283
+ score: exports_external.number().finite().optional(),
4284
+ scoreRange: exports_external.tuple([exports_external.number().finite(), exports_external.number().finite()]),
4285
+ cost: CostRecordSchema,
4286
+ durationMs: exports_external.number().int().nonnegative(),
4287
+ retries: exports_external.number().int().nonnegative().default(0),
4288
+ failureCode: exports_external.string().min(1).max(160).optional(),
4289
+ metadata: exports_external.record(exports_external.string(), exports_external.unknown()).optional()
4290
+ }).strict().superRefine((result, ctx) => {
4291
+ const [minimum, maximum] = result.scoreRange;
4292
+ if (maximum <= minimum) {
4293
+ ctx.addIssue({
4294
+ code: "custom",
4295
+ path: ["scoreRange"],
4296
+ message: "score range maximum must be greater than minimum"
4297
+ });
4298
+ }
4299
+ if (result.outcome === "scored") {
4300
+ if (result.score === undefined || result.score < minimum || result.score > maximum) {
4301
+ ctx.addIssue({
4302
+ code: "custom",
4303
+ path: ["score"],
4304
+ message: "scored outcomes require an in-range score"
4305
+ });
4306
+ }
4307
+ } else if (result.score !== undefined) {
4308
+ ctx.addIssue({
4309
+ code: "custom",
4310
+ path: ["score"],
4311
+ message: "non-score outcomes must not be imputed with a score"
4312
+ });
4313
+ }
4314
+ });
4315
+ var EvaluationBudgetsV1Schema = exports_external.object({
4316
+ maxTasks: exports_external.number().int().positive(),
4317
+ maxRepetitions: exports_external.number().int().positive(),
4318
+ maxConcurrency: exports_external.number().int().positive(),
4319
+ maxTaskTimeMs: exports_external.number().int().positive(),
4320
+ maxRetries: exports_external.number().int().nonnegative(),
4321
+ maxOutputBytes: exports_external.number().int().positive(),
4322
+ maxSpendUsd: exports_external.number().finite().nonnegative().optional()
4323
+ }).strict();
4324
+ var EnvironmentFingerprintV1Schema = exports_external.object({
4325
+ platform: exports_external.string().min(1),
4326
+ arch: exports_external.string().min(1),
4327
+ runtime: exports_external.string().min(1),
4328
+ baseSha: exports_external.string().regex(/^[a-f0-9]{40,64}$/),
4329
+ activeTreeHash: Sha256Schema,
4330
+ taskSetHash: Sha256Schema
4331
+ }).strict();
4332
+ var EvaluationRunV1Schema = exports_external.object({
4333
+ v: exports_external.literal(1),
4334
+ runId: IdentifierSchema,
4335
+ createdAt: IsoDateSchema,
4336
+ status: exports_external.enum(["complete", "inconclusive", "cancelled"]),
4337
+ baseline: EvaluationCandidateV1Schema,
4338
+ candidate: EvaluationCandidateV1Schema,
4339
+ taskSet: exports_external.object({
4340
+ id: IdentifierSchema,
4341
+ contentHash: Sha256Schema,
4342
+ taskIds: exports_external.array(IdentifierSchema).min(1)
4343
+ }).strict(),
4344
+ split: EvaluationSplitSchema,
4345
+ seed: exports_external.string().min(1).max(500),
4346
+ models: exports_external.array(exports_external.string().min(1).max(300)).min(1),
4347
+ environment: EnvironmentFingerprintV1Schema,
4348
+ budgets: EvaluationBudgetsV1Schema,
4349
+ results: exports_external.array(EvaluationResultV1Schema),
4350
+ cost: CostRecordSchema,
4351
+ integrityHash: Sha256Schema
4352
+ }).strict().superRefine((run, ctx) => {
4353
+ if (run.environment.taskSetHash !== run.taskSet.contentHash) {
4354
+ ctx.addIssue({
4355
+ code: "custom",
4356
+ path: ["environment", "taskSetHash"],
4357
+ message: "environment and task-set hashes must agree"
4358
+ });
4359
+ }
4360
+ const allowed = new Set([run.baseline.id, run.candidate.id]);
4361
+ if (allowed.size !== 2) {
4362
+ ctx.addIssue({
4363
+ code: "custom",
4364
+ path: ["candidate", "id"],
4365
+ message: "baseline and candidate ids must differ"
4366
+ });
4367
+ }
4368
+ if (run.results.some((result) => !allowed.has(result.candidateId))) {
4369
+ ctx.addIssue({
4370
+ code: "custom",
4371
+ path: ["results"],
4372
+ message: "results may only reference the declared baseline and candidate"
4373
+ });
4374
+ }
4375
+ const taskIds = new Set(run.taskSet.taskIds);
4376
+ if (run.results.some((result) => !taskIds.has(result.taskId))) {
4377
+ ctx.addIssue({
4378
+ code: "custom",
4379
+ path: ["results"],
4380
+ message: "results may only reference tasks in the frozen task set"
4381
+ });
4382
+ }
4383
+ const resultKeys = run.results.map((result) => `${result.candidateId}\x00${result.taskId}\x00${result.repetition}`);
4384
+ if (new Set(resultKeys).size !== resultKeys.length) {
4385
+ ctx.addIssue({
4386
+ code: "custom",
4387
+ path: ["results"],
4388
+ message: "run contains duplicate candidate/task/repetition results"
4389
+ });
4390
+ }
4391
+ if (run.results.some((result) => result.repetition >= run.budgets.maxRepetitions)) {
4392
+ ctx.addIssue({
4393
+ code: "custom",
4394
+ path: ["results"],
4395
+ message: "result repetition exceeds the declared run budget"
4396
+ });
4397
+ }
4398
+ });
4399
+ var PromotionPolicyV1Schema = exports_external.object({
4400
+ v: exports_external.literal(1),
4401
+ minValidPairs: exports_external.number().int().positive().default(6),
4402
+ minCoverage: exports_external.number().min(0).max(1).default(0.8),
4403
+ deadband: exports_external.number().min(0).max(1).default(0),
4404
+ confidence: exports_external.literal(0.95).default(0.95),
4405
+ bootstrapResamples: exports_external.literal(1e4).default(1e4),
4406
+ requireAvailableCosts: exports_external.boolean().default(true),
4407
+ protectedCategoryTolerances: exports_external.record(exports_external.string(), exports_external.number().min(0).max(1)).default({})
4408
+ }).strict();
4409
+ var PromotionComparisonSchema = exports_external.object({
4410
+ baselineRunId: IdentifierSchema,
4411
+ baselineCandidateId: IdentifierSchema,
4412
+ baselineMean: exports_external.number().min(0).max(1),
4413
+ candidateMean: exports_external.number().min(0).max(1),
4414
+ pairedDelta: exports_external.number().min(-1).max(1),
4415
+ confidenceInterval: exports_external.tuple([
4416
+ exports_external.number().min(-1).max(1),
4417
+ exports_external.number().min(-1).max(1)
4418
+ ]),
4419
+ validPairs: exports_external.number().int().nonnegative(),
4420
+ missingPairs: exports_external.number().int().nonnegative(),
4421
+ coverage: exports_external.number().min(0).max(1)
4422
+ }).strict();
4423
+ var PromotionDecisionV1Schema = exports_external.object({
4424
+ v: exports_external.literal(1),
4425
+ decisionId: IdentifierSchema,
4426
+ runId: IdentifierSchema,
4427
+ decidedAt: IsoDateSchema,
4428
+ status: exports_external.enum(["accept", "reject", "inconclusive"]),
4429
+ reasons: exports_external.array(exports_external.string().min(1).max(300)),
4430
+ baseline: PromotionComparisonSchema,
4431
+ historicalBest: PromotionComparisonSchema,
4432
+ deadband: exports_external.number().min(0).max(1),
4433
+ bootstrap: exports_external.object({
4434
+ resamples: exports_external.literal(1e4),
4435
+ confidence: exports_external.literal(0.95),
4436
+ seedHash: Sha256Schema
4437
+ }).strict(),
4438
+ categories: exports_external.array(exports_external.object({
4439
+ category: IdentifierSchema,
4440
+ protected: exports_external.boolean(),
4441
+ tolerance: exports_external.number().min(0).max(1),
4442
+ baselineMean: exports_external.number().min(0).max(1),
4443
+ candidateMean: exports_external.number().min(0).max(1),
4444
+ pairedDelta: exports_external.number().min(-1).max(1),
4445
+ regression: exports_external.boolean()
4446
+ }).strict()),
4447
+ lineage: exports_external.object({
4448
+ baselineRunId: IdentifierSchema,
4449
+ historicalBestRunId: IdentifierSchema,
4450
+ taskSetHash: Sha256Schema,
4451
+ baselineHash: Sha256Schema,
4452
+ candidateHash: Sha256Schema,
4453
+ historicalBestHash: Sha256Schema
4454
+ }).strict(),
4455
+ policyHash: Sha256Schema,
4456
+ unavailableQualityMetrics: exports_external.array(exports_external.enum(["complexity_delta", "public_api_delta"])).default(["complexity_delta", "public_api_delta"])
4457
+ }).strict();
4458
+ var GateNameSchema = exports_external.enum([
4459
+ "reviewer",
4460
+ "test-engineer",
4461
+ "sast",
4462
+ "mutation",
4463
+ "quality"
4464
+ ]);
4465
+ var GateAuditManifestV1Schema = exports_external.object({
4466
+ v: exports_external.literal(1),
4467
+ id: IdentifierSchema,
4468
+ createdAt: IsoDateSchema,
4469
+ taskIds: exports_external.array(IdentifierSchema).min(1),
4470
+ gates: exports_external.array(GateNameSchema).min(1),
4471
+ models: exports_external.array(exports_external.string().min(1).max(300)).min(1),
4472
+ preferredSwarm: IdentifierSchema.optional(),
4473
+ repetitions: exports_external.number().int().positive().max(100),
4474
+ seed: exports_external.string().min(1).max(500),
4475
+ maxConcurrency: exports_external.number().int().positive().max(64),
4476
+ maxRetries: exports_external.number().int().nonnegative().max(20),
4477
+ maxTimeMs: exports_external.number().int().positive(),
4478
+ maxCostUsd: exports_external.number().finite().nonnegative().optional(),
4479
+ contentHash: Sha256Schema
4480
+ }).strict();
4481
+ var GateAuditCellV1Schema = exports_external.object({
4482
+ v: exports_external.literal(1),
4483
+ taskId: IdentifierSchema,
4484
+ candidateId: IdentifierSchema.optional(),
4485
+ defectType: IdentifierSchema,
4486
+ gate: GateNameSchema,
4487
+ model: exports_external.string().min(1).max(300),
4488
+ repetition: exports_external.number().int().nonnegative(),
4489
+ outcome: exports_external.enum([
4490
+ "caught",
4491
+ "missed",
4492
+ "false_rejection",
4493
+ "unsupported",
4494
+ "infrastructure_failure"
4495
+ ]),
4496
+ retries: exports_external.number().int().nonnegative(),
4497
+ cost: CostRecordSchema,
4498
+ durationMs: exports_external.number().int().nonnegative(),
4499
+ failureClassification: exports_external.enum([
4500
+ "clean",
4501
+ "pre_existing_failure",
4502
+ "new_regression",
4503
+ "flaky_failure",
4504
+ "infrastructure_failure",
4505
+ "unknown_failure"
4506
+ ]).optional(),
4507
+ failureCode: exports_external.string().min(1).max(160).optional()
4508
+ }).strict();
4509
+ var GateAuditResultV1Schema = exports_external.object({
4510
+ v: exports_external.literal(1),
4511
+ runId: IdentifierSchema,
4512
+ manifestHash: Sha256Schema,
4513
+ createdAt: IsoDateSchema,
4514
+ status: exports_external.enum(["complete", "inconclusive", "cancelled"]),
4515
+ cells: exports_external.array(GateAuditCellV1Schema),
4516
+ cost: CostRecordSchema,
4517
+ qualityMetricAvailability: exports_external.object({
4518
+ complexity_delta: exports_external.literal("unavailable"),
4519
+ public_api_delta: exports_external.literal("unavailable")
4520
+ }).strict()
4521
+ }).strict();
4522
+ var TestConsumptionClaimV1Schema = exports_external.object({
4523
+ v: exports_external.literal(1),
4524
+ runId: IdentifierSchema,
4525
+ taskSetHash: Sha256Schema,
4526
+ baselineHash: Sha256Schema,
4527
+ candidateHash: Sha256Schema,
4528
+ claimedAt: IsoDateSchema
4529
+ }).strict();
4530
+ var TaskSetSnapshotV1Schema = exports_external.object({
4531
+ v: exports_external.literal(1),
4532
+ id: IdentifierSchema,
4533
+ split: EvaluationSplitSchema,
4534
+ tasks: exports_external.array(EvaluationTaskV1Schema).min(1),
4535
+ contentHash: Sha256Schema,
4536
+ createdAt: IsoDateSchema
4537
+ }).strict();
4538
+
4539
+ // src/evaluation/store.ts
4540
+ import { mkdir, readdir, readFile as readFile2 } from "fs/promises";
4541
+ import * as path11 from "path";
4542
+
4543
+ // src/evaluation/hashing.ts
4544
+ import { createHash as createHash3 } from "crypto";
4545
+ import { lstatSync, realpathSync } from "fs";
4546
+ import { lstat, opendir, readFile, realpath } from "fs/promises";
4547
+ import * as path10 from "path";
4548
+ function canonicalize(value, ancestors) {
4549
+ if (value === null || typeof value === "string" || typeof value === "boolean") {
4550
+ return value;
4551
+ }
4552
+ if (typeof value === "number") {
4553
+ if (!Number.isFinite(value))
4554
+ throw new TypeError("canonical JSON rejects non-finite numbers");
4555
+ return Object.is(value, -0) ? 0 : value;
4556
+ }
4557
+ if (value === undefined)
4558
+ return;
4559
+ if (typeof value !== "object") {
4560
+ throw new TypeError(`canonical JSON cannot encode ${typeof value}`);
4561
+ }
4562
+ if (ancestors.has(value))
4563
+ throw new TypeError("canonical JSON rejects cyclic values");
4564
+ ancestors.add(value);
4565
+ try {
4566
+ if (Array.isArray(value)) {
4567
+ return value.map((entry) => {
4568
+ const encoded = canonicalize(entry, ancestors);
4569
+ return encoded === undefined ? null : encoded;
4570
+ });
4571
+ }
4572
+ const record = value;
4573
+ const result = {};
4574
+ for (const key of Object.keys(record).sort()) {
4575
+ const encoded = canonicalize(record[key], ancestors);
4576
+ if (encoded !== undefined)
4577
+ result[key] = encoded;
4578
+ }
4579
+ return result;
4580
+ } finally {
4581
+ ancestors.delete(value);
4582
+ }
4583
+ }
4584
+ function canonicalJson(value) {
4585
+ return JSON.stringify(canonicalize(value, new Set));
4586
+ }
4587
+ function sha256(value) {
4588
+ return createHash3("sha256").update(value).digest("hex");
4589
+ }
4590
+ function canonicalHash(value) {
4591
+ return sha256(canonicalJson(value));
4592
+ }
4593
+ function contentHashWithout(value, keys) {
4594
+ const copy = { ...value };
4595
+ for (const key of keys)
4596
+ delete copy[key];
4597
+ return canonicalHash(copy);
4598
+ }
4599
+ function createHashBudget(limits) {
4600
+ return { entries: 0, bytes: 0, limits };
4601
+ }
4602
+ function chargeHashEntry(budget) {
4603
+ budget.entries++;
4604
+ if (budget.entries > budget.limits.maxFiles) {
4605
+ throw new Error("evaluation input exceeds the canonical hashing budget");
4606
+ }
4607
+ }
4608
+ async function closeDirectory(directory) {
4609
+ try {
4610
+ await directory.close();
4611
+ } catch (error2) {
4612
+ if (typeof error2 !== "object" || error2 === null || !("code" in error2) || error2.code !== "ERR_DIR_CLOSED") {
4613
+ throw error2;
4614
+ }
4615
+ }
4616
+ }
4617
+ function resolveTaskInputHashLimits(limits) {
4618
+ const bounded = {
4619
+ maxFiles: limits.maxFiles ?? 5000,
4620
+ maxBytes: limits.maxBytes ?? 50 * 1024 * 1024
4621
+ };
4622
+ if (!Number.isSafeInteger(bounded.maxFiles) || bounded.maxFiles <= 0 || !Number.isSafeInteger(bounded.maxBytes) || bounded.maxBytes <= 0) {
4623
+ throw new Error("task input hash limits must be positive safe integers");
4624
+ }
4625
+ return bounded;
4626
+ }
4627
+ async function hashTree(projectRoot, relativePath, budget) {
4628
+ const target = await resolveContainedExistingPathAsync(projectRoot, relativePath);
4629
+ const canonicalRoot = await _internals9.realpath(projectRoot);
4630
+ const entries = [];
4631
+ const visit = async (absolutePath, entryAlreadyCharged = false) => {
4632
+ const stat = await _internals9.lstat(absolutePath);
4633
+ if (stat.isSymbolicLink()) {
4634
+ throw new Error(`evaluation input contains a symlink or reparse point: ${relativePath}`);
4635
+ }
4636
+ if (!entryAlreadyCharged)
4637
+ chargeHashEntry(budget);
4638
+ const relative3 = path10.relative(canonicalRoot, absolutePath).replace(/\\/g, "/");
4639
+ if (stat.isDirectory()) {
4640
+ entries.push({ path: relative3, type: "directory" });
4641
+ const children = [];
4642
+ const directory = await _internals9.opendir(absolutePath);
4643
+ try {
4644
+ while (true) {
4645
+ const child = await directory.read();
4646
+ if (!child)
4647
+ break;
4648
+ chargeHashEntry(budget);
4649
+ children.push(child.name);
4650
+ }
4651
+ } finally {
4652
+ await closeDirectory(directory);
4653
+ }
4654
+ for (const child of children.sort()) {
4655
+ await visit(path10.join(absolutePath, child), true);
4656
+ }
4657
+ return;
4658
+ }
4659
+ if (!stat.isFile()) {
4660
+ throw new Error(`evaluation input must contain only regular files: ${relative3}`);
4661
+ }
4662
+ budget.bytes += stat.size;
4663
+ if (budget.bytes > budget.limits.maxBytes) {
4664
+ throw new Error("evaluation input exceeds the canonical hashing budget");
4665
+ }
4666
+ const content = await _internals9.readFile(absolutePath);
4667
+ entries.push({
4668
+ path: relative3,
4669
+ type: "file",
4670
+ bytes: content.byteLength,
4671
+ sha256: sha256(content)
4672
+ });
4673
+ };
4674
+ await visit(target);
4675
+ return entries;
4676
+ }
4677
+ async function computeTaskInputContentHash(projectRoot, task, limits = {}) {
4678
+ const bounded = resolveTaskInputHashLimits(limits);
4679
+ const budget = createHashBudget(bounded);
4680
+ const inputTrees = [
4681
+ ...await hashTree(projectRoot, task.instructionPath, budget),
4682
+ ...await hashTree(projectRoot, task.environment.path, budget)
4683
+ ];
4684
+ if (task.scorer.kind === "project" && task.scorer.argv[0]) {
4685
+ inputTrees.push(...await hashTree(projectRoot, task.scorer.argv[0], budget));
4686
+ }
4687
+ return canonicalHash({
4688
+ task: JSON.parse(canonicalJson({ ...task, contentHash: undefined })),
4689
+ inputs: inputTrees.sort((left, right) => `${left.path}\x00${left.type}`.localeCompare(`${right.path}\x00${right.type}`))
4690
+ });
4691
+ }
4692
+ async function computeTaskLineageInputHash(projectRoot, task, limits = {}) {
4693
+ const bounded = resolveTaskInputHashLimits(limits);
4694
+ const budget = createHashBudget(bounded);
4695
+ const normalizedTree = async (relativePath) => {
4696
+ const canonicalRoot = await _internals9.realpath(projectRoot);
4697
+ const target = await resolveContainedExistingPathAsync(projectRoot, relativePath);
4698
+ const targetRelative = path10.relative(canonicalRoot, target).replace(/\\/g, "/");
4699
+ return (await hashTree(projectRoot, relativePath, budget)).map((entry) => ({
4700
+ ...entry,
4701
+ path: path10.posix.relative(targetRelative, entry.path) || "."
4702
+ }));
4703
+ };
4704
+ const scorerArgv = [...task.scorer.argv];
4705
+ const projectScorer = task.scorer.kind === "project" && scorerArgv[0] ? await normalizedTree(scorerArgv[0]) : undefined;
4706
+ if (projectScorer)
4707
+ scorerArgv[0] = "<project-scorer>";
4708
+ return canonicalHash({
4709
+ instruction: await normalizedTree(task.instructionPath),
4710
+ environment: {
4711
+ kind: task.environment.kind,
4712
+ tree: await normalizedTree(task.environment.path)
4713
+ },
4714
+ scorer: {
4715
+ kind: task.scorer.kind,
4716
+ argv: scorerArgv,
4717
+ timeoutMs: task.scorer.timeoutMs,
4718
+ scoreRange: task.scorer.scoreRange,
4719
+ projectScorer
4720
+ }
4721
+ });
4722
+ }
4723
+ function computeTaskSetContentHash(snapshot) {
4724
+ return contentHashWithout(snapshot, ["contentHash", "createdAt"]);
4725
+ }
4726
+ function computeManifestContentHash(manifest) {
4727
+ return contentHashWithout(manifest, ["contentHash"]);
4728
+ }
4729
+ function isWithin(root, candidate) {
4730
+ const relative3 = path10.relative(root, candidate);
4731
+ return relative3 === "" || !relative3.startsWith("..") && !path10.isAbsolute(relative3);
4732
+ }
4733
+ function resolveContainedExistingPath(root, relativePath) {
4734
+ if (path10.isAbsolute(relativePath) || /^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(relativePath) || relativePath.includes("\x00")) {
4735
+ throw new Error("evaluation path must be a non-NUL relative path");
4736
+ }
4737
+ const segments = relativePath.replace(/\\/g, "/").split("/").filter(Boolean);
4738
+ if (segments.length === 0 || segments.some((segment) => segment === "..")) {
4739
+ throw new Error("evaluation path traversal is not allowed");
4740
+ }
4741
+ const canonicalRoot = realpathSync(root);
4742
+ let cursor = canonicalRoot;
4743
+ for (const segment of segments) {
4744
+ if (segment === ".")
4745
+ continue;
4746
+ cursor = path10.join(cursor, segment);
4747
+ const stat = lstatSync(cursor);
4748
+ if (stat.isSymbolicLink()) {
4749
+ throw new Error(`evaluation path contains a symlink or reparse point: ${relativePath}`);
4750
+ }
4751
+ }
4752
+ const canonicalCandidate = realpathSync(cursor);
4753
+ if (!isWithin(canonicalRoot, canonicalCandidate)) {
4754
+ throw new Error("evaluation path escapes its admitted root");
4755
+ }
4756
+ return canonicalCandidate;
4757
+ }
4758
+ async function resolveContainedExistingPathAsync(root, relativePath) {
4759
+ if (path10.isAbsolute(relativePath) || /^(?:[A-Za-z]:[\\/]|[\\/]{1,2})/.test(relativePath) || relativePath.includes("\x00")) {
4760
+ throw new Error("evaluation path must be a non-NUL relative path");
4761
+ }
4762
+ const segments = relativePath.replace(/\\/g, "/").split("/").filter(Boolean);
4763
+ if (segments.length === 0 || segments.some((segment) => segment === "..")) {
4764
+ throw new Error("evaluation path traversal is not allowed");
4765
+ }
4766
+ const canonicalRoot = await _internals9.realpath(root);
4767
+ let cursor = canonicalRoot;
4768
+ for (const segment of segments) {
4769
+ if (segment === ".")
4770
+ continue;
4771
+ cursor = path10.join(cursor, segment);
4772
+ const stat = await _internals9.lstat(cursor);
4773
+ if (stat.isSymbolicLink()) {
4774
+ throw new Error(`evaluation path contains a symlink or reparse point: ${relativePath}`);
4775
+ }
4776
+ }
4777
+ const canonicalCandidate = await _internals9.realpath(cursor);
4778
+ if (!isWithin(canonicalRoot, canonicalCandidate)) {
4779
+ throw new Error("evaluation path escapes its admitted root");
4780
+ }
4781
+ return canonicalCandidate;
4782
+ }
4783
+ var _internals9 = {
4784
+ lstat,
4785
+ opendir,
4786
+ readFile,
4787
+ realpath
4788
+ };
4789
+
4790
+ // src/evaluation/store.ts
4791
+ var AGENT = "evaluation-store";
4792
+ var TEST_LEDGER_RELATIVE = path11.join("evolution", "test-consumption.jsonl");
4793
+ var TASK_SPLIT_REGISTRY_RELATIVE = path11.join("evolution", "tasks", "split-registry.json");
4794
+
4795
+ class EvaluationConflictError extends Error {
4796
+ constructor(message) {
4797
+ super(message);
4798
+ this.name = "EvaluationConflictError";
4799
+ }
4800
+ }
4801
+ function swarmPath(directory, ...segments) {
4802
+ return path11.join(directory, ".swarm", ...segments);
4803
+ }
4804
+ function serialized(value) {
4805
+ return `${canonicalJson(value)}
4806
+ `;
4807
+ }
4808
+ function parseTaskSplitRegistry(content) {
4809
+ if (content === undefined)
4810
+ return { v: 1, entries: [] };
4811
+ let value;
4812
+ try {
4813
+ value = JSON.parse(content);
4814
+ } catch (error2) {
4815
+ throw new EvaluationConflictError(`task split registry is corrupt: ${String(error2)}`);
4816
+ }
4817
+ if (!value || typeof value !== "object" || Array.isArray(value)) {
4818
+ throw new EvaluationConflictError("task split registry is corrupt");
4819
+ }
4820
+ const record = value;
4821
+ if (record.v !== 1 || !Array.isArray(record.entries)) {
4822
+ throw new EvaluationConflictError("task split registry is corrupt");
4823
+ }
4824
+ const entries = record.entries.map((candidate) => {
4825
+ if (!candidate || typeof candidate !== "object" || Array.isArray(candidate)) {
4826
+ throw new EvaluationConflictError("task split registry is corrupt");
4827
+ }
4828
+ const entry = candidate;
4829
+ if (typeof entry.lineageHash !== "string" || !["train", "validation", "test"].includes(String(entry.split)) || !Array.isArray(entry.taskIds) || !entry.taskIds.every((id) => typeof id === "string") || !Array.isArray(entry.inputHashes) || !entry.inputHashes.every((hash) => typeof hash === "string")) {
4830
+ throw new EvaluationConflictError("task split registry is corrupt");
4831
+ }
4832
+ return {
4833
+ lineageHash: entry.lineageHash,
4834
+ split: entry.split,
4835
+ taskIds: [...new Set(entry.taskIds)].sort(),
4836
+ inputHashes: [...new Set(entry.inputHashes)].sort()
4837
+ };
4838
+ });
4839
+ return { v: 1, entries };
4840
+ }
4841
+ async function readOptional(filePath) {
4842
+ try {
4843
+ return await readFile2(filePath, "utf8");
4844
+ } catch (error2) {
4845
+ if (error2.code === "ENOENT")
4846
+ return;
4847
+ throw error2;
4848
+ }
4849
+ }
4850
+ async function writeImmutable(options) {
4851
+ await mkdir(path11.dirname(options.filePath), { recursive: true });
4852
+ return withEvidenceLock(options.directory, options.relativeLockPath, AGENT, options.taskId, async () => {
4853
+ const desired = serialized(options.value);
4854
+ const existing = await readOptional(options.filePath);
4855
+ if (existing !== undefined) {
4856
+ let parsed;
4857
+ try {
4858
+ parsed = options.parse(JSON.parse(existing));
4859
+ } catch (error2) {
4860
+ throw new EvaluationConflictError(`existing evaluation artifact is corrupt: ${options.filePath}: ${String(error2)}`);
4861
+ }
4862
+ if (serialized(parsed) === desired || options.isEquivalent?.(parsed, options.value)) {
4863
+ return parsed;
4864
+ }
4865
+ throw new EvaluationConflictError(`immutable evaluation artifact conflicts with existing content: ${options.filePath}`);
4866
+ }
4867
+ await atomicWriteFile(options.filePath, desired);
4868
+ return options.value;
4869
+ });
4870
+ }
4871
+ function validateTaskInputs(directory, task) {
4872
+ resolveContainedExistingPath(directory, task.instructionPath);
4873
+ const environmentRoot = resolveContainedExistingPath(directory, task.environment.path);
4874
+ if (task.scorer.kind === "project") {
4875
+ const executable = task.scorer.argv[0];
4876
+ if (!executable)
4877
+ throw new Error("project scorer requires an executable");
4878
+ const scorerPath = resolveContainedExistingPath(directory, executable);
4879
+ const relative4 = path11.relative(environmentRoot, scorerPath);
4880
+ if (!relative4 || relative4 === ".." || relative4.startsWith(`..${path11.sep}`) || path11.isAbsolute(relative4)) {
4881
+ throw new Error("project scorer executable must be inside the admitted task environment");
4882
+ }
4883
+ }
4884
+ }
4885
+ async function admitEvaluationTask(directory, input, inputRoot = directory) {
4886
+ const task = EvaluationTaskV1Schema.parse(input);
4887
+ if (await computeTaskInputContentHash(inputRoot, task) !== task.contentHash) {
4888
+ throw new Error(`task ${task.id} content hash does not match its canonical inputs`);
4889
+ }
4890
+ validateTaskInputs(inputRoot, task);
4891
+ const lineageInputHash = await computeTaskLineageInputHash(inputRoot, task);
4892
+ const relative4 = path11.join("evolution", "tasks", task.split, `${task.id}.json`);
4893
+ const admissionLock = TASK_SPLIT_REGISTRY_RELATIVE;
4894
+ const filePath = swarmPath(directory, relative4);
4895
+ await mkdir(path11.dirname(filePath), { recursive: true });
4896
+ return withEvidenceLock(directory, admissionLock, AGENT, task.id, async () => {
4897
+ const registryPath = swarmPath(directory, TASK_SPLIT_REGISTRY_RELATIVE);
4898
+ const registry = parseTaskSplitRegistry(await readOptional(registryPath));
4899
+ const existingTaskEntry = registry.entries.find((entry) => entry.taskIds.includes(task.id));
4900
+ if (existingTaskEntry && existingTaskEntry.split !== task.split) {
4901
+ throw new EvaluationConflictError(`task ${task.id} is already admitted to a different split`);
4902
+ }
4903
+ const parentEntry = task.derivedFromTaskId ? registry.entries.find((entry) => entry.taskIds.includes(task.derivedFromTaskId)) : undefined;
4904
+ if (task.derivedFromTaskId && !parentEntry) {
4905
+ throw new EvaluationConflictError(`derived task ${task.id} references an unadmitted parent ${task.derivedFromTaskId}`);
4906
+ }
4907
+ const lineageEntry = parentEntry ?? registry.entries.find((entry) => entry.lineageHash === lineageInputHash || entry.inputHashes.includes(lineageInputHash));
4908
+ if (lineageEntry && lineageEntry.split !== task.split) {
4909
+ throw new EvaluationConflictError(`task ${task.id} lineage is already admitted to ${lineageEntry.split}; derived and aliased tasks must inherit that split`);
4910
+ }
4911
+ for (const split of ["train", "validation", "test"]) {
4912
+ if (split === task.split)
4913
+ continue;
4914
+ if (await readOptional(swarmPath(directory, "evolution", "tasks", split, `${task.id}.json`))) {
4915
+ throw new EvaluationConflictError(`task ${task.id} is already admitted to a different split`);
4916
+ }
4917
+ }
4918
+ const desired = serialized(task);
4919
+ const existing = await readOptional(filePath);
4920
+ if (existing !== undefined) {
4921
+ const parsed = EvaluationTaskV1Schema.parse(JSON.parse(existing));
4922
+ if (serialized(parsed) === desired) {
4923
+ if (lineageEntry) {
4924
+ lineageEntry.taskIds = [
4925
+ ...new Set([...lineageEntry.taskIds, task.id])
4926
+ ].sort();
4927
+ lineageEntry.inputHashes = [
4928
+ ...new Set([...lineageEntry.inputHashes, lineageInputHash])
4929
+ ].sort();
4930
+ } else {
4931
+ registry.entries.push({
4932
+ lineageHash: lineageInputHash,
4933
+ split: task.split,
4934
+ taskIds: [task.id],
4935
+ inputHashes: [lineageInputHash]
4936
+ });
4937
+ }
4938
+ registry.entries.sort((left, right) => left.lineageHash.localeCompare(right.lineageHash));
4939
+ await atomicWriteFile(registryPath, serialized(registry));
4940
+ return parsed;
4941
+ }
4942
+ if (task.split !== "train") {
4943
+ throw new EvaluationConflictError(`${task.split} task ${task.id} is immutable after admission`);
4944
+ }
4945
+ }
4946
+ await atomicWriteFile(filePath, desired);
4947
+ if (lineageEntry) {
4948
+ lineageEntry.taskIds = [
4949
+ ...new Set([...lineageEntry.taskIds, task.id])
4950
+ ].sort();
4951
+ lineageEntry.inputHashes = [
4952
+ ...new Set([...lineageEntry.inputHashes, lineageInputHash])
4953
+ ].sort();
4954
+ } else {
4955
+ registry.entries.push({
4956
+ lineageHash: lineageInputHash,
4957
+ split: task.split,
4958
+ taskIds: [task.id],
4959
+ inputHashes: [lineageInputHash]
4960
+ });
4961
+ }
4962
+ registry.entries.sort((left, right) => left.lineageHash.localeCompare(right.lineageHash));
4963
+ await atomicWriteFile(registryPath, serialized(registry));
4964
+ return task;
4965
+ });
4966
+ }
4967
+ async function saveTaskSetSnapshot(directory, input, inputRoot = directory) {
4968
+ const snapshot = TaskSetSnapshotV1Schema.parse(input);
4969
+ const ids = snapshot.tasks.map((task) => task.id);
4970
+ if (new Set(ids).size !== ids.length)
4971
+ throw new Error("task-set contains duplicate task ids");
4972
+ if (snapshot.tasks.some((task) => task.split !== snapshot.split)) {
4973
+ throw new Error("task-set tasks must all belong to the declared split");
4974
+ }
4975
+ for (const task of snapshot.tasks) {
4976
+ if (await computeTaskInputContentHash(inputRoot, task) !== task.contentHash) {
4977
+ throw new Error(`task ${task.id} content hash is invalid`);
4978
+ }
4979
+ }
4980
+ if (computeTaskSetContentHash(snapshot) !== snapshot.contentHash) {
4981
+ throw new Error("task-set content hash does not match its canonical inputs");
4982
+ }
4983
+ const relative4 = path11.join("evolution", "task-sets", `${snapshot.contentHash}.json`);
4984
+ return writeImmutable({
4985
+ directory,
4986
+ relativeLockPath: relative4,
4987
+ filePath: swarmPath(directory, relative4),
4988
+ taskId: snapshot.id,
4989
+ value: snapshot,
4990
+ parse: (value) => TaskSetSnapshotV1Schema.parse(value),
4991
+ isEquivalent: (existing, desired) => existing.contentHash === desired.contentHash
4992
+ });
4993
+ }
4994
+ async function saveGateAuditResult(directory, input) {
4995
+ const result = GateAuditResultV1Schema.parse(input);
4996
+ const relative4 = path11.join("evidence", "gate-audit", result.runId, "results.json");
4997
+ return writeImmutable({
4998
+ directory,
4999
+ relativeLockPath: relative4,
5000
+ filePath: swarmPath(directory, relative4),
5001
+ taskId: result.runId,
5002
+ value: result,
5003
+ parse: (value) => GateAuditResultV1Schema.parse(value)
5004
+ });
5005
+ }
5006
+ async function saveGateAuditManifest(directory, input) {
5007
+ const manifest = GateAuditManifestV1Schema.parse(input);
5008
+ const relative4 = path11.join("evolution", "gate-audit-manifests", `${manifest.id}.json`);
5009
+ return writeImmutable({
5010
+ directory,
5011
+ relativeLockPath: relative4,
5012
+ filePath: swarmPath(directory, relative4),
5013
+ taskId: manifest.id,
5014
+ value: manifest,
5015
+ parse: (value) => GateAuditManifestV1Schema.parse(value)
5016
+ });
5017
+ }
5018
+ async function readGateAuditManifest(directory, runId) {
5019
+ const content = await readOptional(swarmPath(directory, "evolution", "gate-audit-manifests", `${runId}.json`));
5020
+ return content === undefined ? undefined : GateAuditManifestV1Schema.parse(JSON.parse(content));
5021
+ }
5022
+ async function readGateAuditResult(directory, runId) {
5023
+ const content = await readOptional(swarmPath(directory, "evidence", "gate-audit", runId, "results.json"));
5024
+ return content === undefined ? undefined : GateAuditResultV1Schema.parse(JSON.parse(content));
5025
+ }
5026
+ async function listGateAuditResults(directory) {
5027
+ const root = swarmPath(directory, "evidence", "gate-audit");
5028
+ let entries = [];
5029
+ try {
5030
+ entries = await readdir(root, { withFileTypes: true });
5031
+ } catch (error2) {
5032
+ if (error2.code === "ENOENT") {
5033
+ return { results: [], corruptRunIds: [] };
5034
+ }
5035
+ throw error2;
5036
+ }
5037
+ const results = [];
5038
+ const corruptRunIds = [];
5039
+ for (const entry of entries.sort((left, right) => left.name.localeCompare(right.name))) {
5040
+ if (!entry.isDirectory() || !/^[A-Za-z0-9][A-Za-z0-9._-]{0,159}$/.test(entry.name)) {
5041
+ continue;
5042
+ }
5043
+ try {
5044
+ const parsed = GateAuditResultV1Schema.parse(JSON.parse(await readFile2(path11.join(root, entry.name, "results.json"), "utf8")));
5045
+ if (parsed.runId !== entry.name)
5046
+ throw new Error("run id/path mismatch");
5047
+ results.push(parsed);
5048
+ } catch {
5049
+ corruptRunIds.push(entry.name);
5050
+ }
5051
+ }
5052
+ return { results, corruptRunIds };
5053
+ }
5054
+ function parseTestLedger(content) {
5055
+ return content.split(/\r?\n/).filter((line) => line.trim().length > 0).map((line, index) => {
5056
+ try {
5057
+ return TestConsumptionClaimV1Schema.parse(JSON.parse(line));
5058
+ } catch (error2) {
5059
+ throw new EvaluationConflictError(`test-consumption ledger is corrupt at line ${index + 1}: ${String(error2)}`);
5060
+ }
5061
+ });
5062
+ }
5063
+ async function getProtectedEvaluationRunIds(directory) {
5064
+ const protectedIds = new Set;
5065
+ const ledger = await readOptional(swarmPath(directory, TEST_LEDGER_RELATIVE));
5066
+ for (const claim of parseTestLedger(ledger ?? ""))
5067
+ protectedIds.add(claim.runId);
5068
+ const decisionsDirectory = swarmPath(directory, "evolution", "decisions");
5069
+ let files = [];
5070
+ try {
5071
+ files = await readdir(decisionsDirectory);
5072
+ } catch (error2) {
5073
+ if (error2.code !== "ENOENT")
5074
+ throw error2;
5075
+ }
5076
+ for (const filename of files.filter((value) => value.endsWith(".json")).sort()) {
5077
+ const decision = PromotionDecisionV1Schema.parse(JSON.parse(await readFile2(path11.join(decisionsDirectory, filename), "utf8")));
5078
+ protectedIds.add(decision.runId);
5079
+ protectedIds.add(decision.lineage.baselineRunId);
5080
+ if (decision.lineage.historicalBestRunId !== "unavailable") {
5081
+ protectedIds.add(decision.lineage.historicalBestRunId);
5082
+ }
5083
+ }
5084
+ return protectedIds;
5085
+ }
5086
+
5087
+ // src/evaluation/retention.ts
5088
+ function artifactKey(artifact) {
5089
+ return `${artifact.namespace}/${artifact.id}`;
5090
+ }
5091
+ function validId(value) {
5092
+ return /^[A-Za-z0-9][A-Za-z0-9._-]{0,159}$/.test(value);
5093
+ }
5094
+ async function evaluationRunCandidates(directory) {
5095
+ const root = path12.join(directory, ".swarm", "evolution", "runs");
5096
+ let entries = [];
5097
+ try {
5098
+ entries = await fs6.readdir(root, { withFileTypes: true });
5099
+ } catch (error2) {
5100
+ if (error2.code === "ENOENT") {
5101
+ return { candidates: [], corrupt: [] };
5102
+ }
5103
+ throw error2;
5104
+ }
5105
+ const candidates = [];
5106
+ const corrupt = [];
5107
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
5108
+ if (!entry.isFile() || !entry.name.endsWith(".json"))
5109
+ continue;
5110
+ const id = entry.name.slice(0, -".json".length);
5111
+ if (!validId(id))
5112
+ continue;
5113
+ const artifact = { namespace: "evaluation-run", id };
5114
+ const target = path12.join(root, entry.name);
5115
+ try {
5116
+ const run = EvaluationRunV1Schema.parse(JSON.parse(await fs6.readFile(target, "utf8")));
5117
+ if (run.runId !== id)
5118
+ throw new Error("run id/path mismatch");
5119
+ candidates.push({
5120
+ artifact,
5121
+ createdAt: run.createdAt,
5122
+ target,
5123
+ kind: "file"
5124
+ });
5125
+ } catch {
5126
+ corrupt.push(artifact);
5127
+ }
5128
+ }
5129
+ return { candidates, corrupt };
5130
+ }
5131
+ async function gateAuditCandidates(directory) {
5132
+ const root = path12.join(directory, ".swarm", "evidence", "gate-audit");
5133
+ let entries = [];
5134
+ try {
5135
+ entries = await fs6.readdir(root, { withFileTypes: true });
5136
+ } catch (error2) {
5137
+ if (error2.code === "ENOENT") {
5138
+ return { candidates: [], corrupt: [] };
5139
+ }
5140
+ throw error2;
5141
+ }
5142
+ const candidates = [];
5143
+ const corrupt = [];
5144
+ for (const entry of entries.sort((a, b) => a.name.localeCompare(b.name))) {
5145
+ if (!entry.isDirectory() || !validId(entry.name))
5146
+ continue;
5147
+ const artifact = { namespace: "gate-audit", id: entry.name };
5148
+ const target = path12.join(root, entry.name);
5149
+ try {
5150
+ const result = GateAuditResultV1Schema.parse(JSON.parse(await fs6.readFile(path12.join(target, "results.json"), "utf8")));
5151
+ if (result.runId !== entry.name)
5152
+ throw new Error("run id/path mismatch");
5153
+ candidates.push({
5154
+ artifact,
5155
+ createdAt: result.createdAt,
5156
+ target,
5157
+ kind: "directory"
5158
+ });
5159
+ } catch {
5160
+ corrupt.push(artifact);
5161
+ }
5162
+ }
5163
+ return { candidates, corrupt };
5164
+ }
5165
+ async function retentionCandidates(directory) {
5166
+ const [runs, audits] = await Promise.all([
5167
+ evaluationRunCandidates(directory),
5168
+ gateAuditCandidates(directory)
5169
+ ]);
5170
+ return {
5171
+ candidates: [...runs.candidates, ...audits.candidates].sort((a, b) => artifactKey(a.artifact).localeCompare(artifactKey(b.artifact))),
5172
+ corrupt: [...runs.corrupt, ...audits.corrupt].sort((a, b) => artifactKey(a).localeCompare(artifactKey(b)))
5173
+ };
5174
+ }
5175
+ async function protectedArtifacts(directory) {
5176
+ const ids = await getProtectedEvaluationRunIds(directory);
5177
+ return new Set([...ids].map((id) => `evaluation-run/${id}`));
5178
+ }
5179
+ async function removeCandidate(candidate) {
5180
+ if (candidate.kind === "directory") {
5181
+ await fs6.rm(candidate.target, { recursive: true, force: true });
5182
+ return;
5183
+ }
5184
+ await fs6.unlink(candidate.target);
5185
+ }
5186
+ var _retentionInternals = {
5187
+ protectedArtifacts,
5188
+ removeCandidate
5189
+ };
5190
+ async function archiveEvaluationArtifacts(args) {
5191
+ return withEvidenceLock(args.directory, path12.join("evolution", "retention-index"), "evaluation-retention", "archive", async () => {
5192
+ const inventory = await retentionCandidates(args.directory);
5193
+ let protectedKeys = await _retentionInternals.protectedArtifacts(args.directory);
5194
+ const protectedRefs = inventory.candidates.filter((candidate) => protectedKeys.has(artifactKey(candidate.artifact))).map((candidate) => candidate.artifact);
5195
+ const eligible = inventory.candidates.filter((candidate) => !protectedKeys.has(artifactKey(candidate.artifact)));
5196
+ const now = args.now ?? new Date;
5197
+ const cutoff = new Date(now.getTime() - args.maxAgeDays * 86400000).toISOString();
5198
+ const selectedKeys = new Set(eligible.filter((candidate) => candidate.createdAt < cutoff).map((candidate) => artifactKey(candidate.artifact)));
5199
+ const remaining = eligible.filter((candidate) => !selectedKeys.has(artifactKey(candidate.artifact))).sort((a, b) => a.createdAt === b.createdAt ? artifactKey(a.artifact).localeCompare(artifactKey(b.artifact)) : a.createdAt.localeCompare(b.createdAt));
5200
+ if (args.maxBundles !== undefined && remaining.length > args.maxBundles) {
5201
+ for (const candidate of remaining.slice(0, remaining.length - args.maxBundles)) {
5202
+ selectedKeys.add(artifactKey(candidate.artifact));
5203
+ }
5204
+ }
5205
+ const selectedCandidates = eligible.filter((candidate) => selectedKeys.has(artifactKey(candidate.artifact))).sort((a, b) => artifactKey(a.artifact).localeCompare(artifactKey(b.artifact)));
5206
+ const archived = [];
5207
+ const failed = [];
5208
+ if (!args.dryRun) {
5209
+ for (const candidate of selectedCandidates) {
5210
+ protectedKeys = await _retentionInternals.protectedArtifacts(args.directory);
5211
+ if (protectedKeys.has(artifactKey(candidate.artifact))) {
5212
+ protectedRefs.push(candidate.artifact);
5213
+ continue;
5214
+ }
5215
+ try {
5216
+ await _retentionInternals.removeCandidate(candidate);
5217
+ archived.push(candidate.artifact);
5218
+ } catch (error2) {
5219
+ failed.push({
5220
+ artifact: candidate.artifact,
5221
+ error: error2 instanceof Error ? error2.message : String(error2)
5222
+ });
5223
+ }
5224
+ }
5225
+ }
5226
+ return {
5227
+ inventory: inventory.candidates.map((candidate) => candidate.artifact),
5228
+ selected: selectedCandidates.map((candidate) => candidate.artifact),
5229
+ archived,
5230
+ corrupt: inventory.corrupt,
5231
+ protected: [
5232
+ ...new Map(protectedRefs.map((artifact) => [artifactKey(artifact), artifact])).values()
5233
+ ].sort((a, b) => artifactKey(a).localeCompare(artifactKey(b))),
5234
+ failed
5235
+ };
5236
+ });
5237
+ }
5238
+
4161
5239
  // src/evidence/manager.ts
4162
5240
  var VALID_EVIDENCE_TYPES = [
4163
5241
  "review",
@@ -4194,11 +5272,11 @@ var PROJECT_INDICATORS = [
4194
5272
  ];
4195
5273
  function isWeakConfigContainerRoot(directory) {
4196
5274
  try {
4197
- const resolved = realpathSync(directory);
4198
- return resolved === realpathSync(os4.tmpdir()) || resolved === realpathSync(os4.homedir());
5275
+ const resolved = realpathSync2(directory);
5276
+ return resolved === realpathSync2(os4.tmpdir()) || resolved === realpathSync2(os4.homedir());
4199
5277
  } catch {
4200
- const resolved = path10.resolve(directory);
4201
- return resolved === path10.resolve(os4.tmpdir()) || resolved === path10.resolve(os4.homedir());
5278
+ const resolved = path13.resolve(directory);
5279
+ return resolved === path13.resolve(os4.tmpdir()) || resolved === path13.resolve(os4.homedir());
4202
5280
  }
4203
5281
  }
4204
5282
  function hasAccessibleProjectIndicator(directory, options = {}) {
@@ -4208,7 +5286,7 @@ function hasAccessibleProjectIndicator(directory, options = {}) {
4208
5286
  continue;
4209
5287
  }
4210
5288
  try {
4211
- const indicatorStat = statSync2(path10.join(directory, indicator));
5289
+ const indicatorStat = statSync2(path13.join(directory, indicator));
4212
5290
  if (indicatorStat.isFile() || indicatorStat.isDirectory()) {
4213
5291
  return true;
4214
5292
  }
@@ -4219,7 +5297,7 @@ function hasAccessibleProjectIndicator(directory, options = {}) {
4219
5297
  function validateProjectRoot(directory) {
4220
5298
  let resolved;
4221
5299
  try {
4222
- resolved = realpathSync(directory);
5300
+ resolved = realpathSync2(directory);
4223
5301
  } catch {
4224
5302
  warn(`[evidence] Cannot canonicalize directory "${directory}" \u2014 failing closed`);
4225
5303
  throw new Error(`Cannot verify project root for "${directory}" \u2014 directory may not exist or is inaccessible`);
@@ -4230,14 +5308,14 @@ function validateProjectRoot(directory) {
4230
5308
  if (depth >= MAX_DEPTH)
4231
5309
  break;
4232
5310
  depth++;
4233
- const parent = path10.dirname(current);
5311
+ const parent = path13.dirname(current);
4234
5312
  if (parent === current)
4235
5313
  break;
4236
- if (path10.dirname(parent) === parent) {
5314
+ if (path13.dirname(parent) === parent) {
4237
5315
  current = parent;
4238
5316
  continue;
4239
5317
  }
4240
- const parentSwarm = path10.join(parent, ".swarm");
5318
+ const parentSwarm = path13.join(parent, ".swarm");
4241
5319
  try {
4242
5320
  if (statSync2(parentSwarm).isDirectory()) {
4243
5321
  if (hasAccessibleProjectIndicator(parent, {
@@ -4256,14 +5334,14 @@ function validateProjectRoot(directory) {
4256
5334
  }
4257
5335
  }
4258
5336
  async function saveEvidence(directory, taskId, evidence) {
4259
- _internals9.validateProjectRoot(directory);
5337
+ _internals10.validateProjectRoot(directory);
4260
5338
  const sanitizedTaskId = sanitizeTaskId2(taskId);
4261
- const relativePath = path10.join("evidence", sanitizedTaskId, "evidence.json");
5339
+ const relativePath = path13.join("evidence", sanitizedTaskId, "evidence.json");
4262
5340
  validateSwarmPath(directory, relativePath);
4263
- _internals9.validateEvidence(evidence);
5341
+ _internals10.validateEvidence(evidence);
4264
5342
  return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
4265
5343
  const evidencePath = validateSwarmPath(directory, relativePath);
4266
- const evidenceDir = path10.dirname(evidencePath);
5344
+ const evidenceDir = path13.dirname(evidencePath);
4267
5345
  let bundle;
4268
5346
  const existingContent = await readSwarmFileAsync(directory, relativePath);
4269
5347
  if (existingContent !== null) {
@@ -4306,10 +5384,10 @@ async function saveEvidence(directory, taskId, evidence) {
4306
5384
  throw new Error(`Evidence bundle size (${bundleJson.length} bytes) exceeds maximum (${EVIDENCE_MAX_JSON_BYTES} bytes)`);
4307
5385
  }
4308
5386
  mkdirSync6(evidenceDir, { recursive: true });
4309
- const tempPath = path10.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
5387
+ const tempPath = path13.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4310
5388
  try {
4311
5389
  await bunWrite(tempPath, bundleJson);
4312
- await fs6.rename(tempPath, evidencePath);
5390
+ await fs7.rename(tempPath, evidencePath);
4313
5391
  } catch (error2) {
4314
5392
  try {
4315
5393
  rmSync(tempPath, { force: true });
@@ -4350,7 +5428,7 @@ function wrapFlatRetrospective(flatEntry, taskId) {
4350
5428
  }
4351
5429
  async function loadEvidence(directory, taskId) {
4352
5430
  const sanitizedTaskId = sanitizeTaskId2(taskId);
4353
- const relativePath = path10.join("evidence", sanitizedTaskId, "evidence.json");
5431
+ const relativePath = path13.join("evidence", sanitizedTaskId, "evidence.json");
4354
5432
  if (relativePath.length > 4096) {
4355
5433
  return { status: "not_found" };
4356
5434
  }
@@ -4366,17 +5444,17 @@ async function loadEvidence(directory, taskId) {
4366
5444
  return { status: "invalid_schema", errors: ["Invalid JSON"] };
4367
5445
  }
4368
5446
  if (isFlatRetrospective(parsed)) {
4369
- const wrappedBundle = _internals9.wrapFlatRetrospective(parsed, sanitizedTaskId);
5447
+ const wrappedBundle = _internals10.wrapFlatRetrospective(parsed, sanitizedTaskId);
4370
5448
  try {
4371
5449
  const validated = EvidenceBundleSchema.parse(wrappedBundle);
4372
5450
  try {
4373
5451
  await withEvidenceLock(directory, relativePath, "evidence-loader", sanitizedTaskId, async () => {
4374
- const evidenceDir = path10.dirname(evidencePath);
5452
+ const evidenceDir = path13.dirname(evidencePath);
4375
5453
  const bundleJson = JSON.stringify(validated);
4376
- const tempPath = path10.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
5454
+ const tempPath = path13.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
4377
5455
  try {
4378
5456
  await bunWrite(tempPath, bundleJson);
4379
- await fs6.rename(tempPath, evidencePath);
5457
+ await fs7.rename(tempPath, evidencePath);
4380
5458
  } catch (writeError) {
4381
5459
  try {
4382
5460
  rmSync(tempPath, { force: true });
@@ -4418,7 +5496,7 @@ async function listEvidenceTaskIds(directory) {
4418
5496
  }
4419
5497
  const taskIds = [];
4420
5498
  for (const entry of entries) {
4421
- const entryPath = path10.join(evidenceBasePath, entry);
5499
+ const entryPath = path13.join(evidenceBasePath, entry);
4422
5500
  try {
4423
5501
  const stats = statSync2(entryPath);
4424
5502
  if (!stats.isDirectory()) {
@@ -4436,7 +5514,7 @@ async function listEvidenceTaskIds(directory) {
4436
5514
  }
4437
5515
  async function deleteEvidence(directory, taskId) {
4438
5516
  const sanitizedTaskId = sanitizeTaskId2(taskId);
4439
- const relativePath = path10.join("evidence", sanitizedTaskId);
5517
+ const relativePath = path13.join("evidence", sanitizedTaskId);
4440
5518
  const evidenceDir = validateSwarmPath(directory, relativePath);
4441
5519
  try {
4442
5520
  statSync2(evidenceDir);
@@ -4452,32 +5530,40 @@ async function deleteEvidence(directory, taskId) {
4452
5530
  }
4453
5531
  }
4454
5532
  async function checkRequirementCoverage(phase, directory) {
4455
- const relativePath = path10.join("evidence", `req-coverage-phase-${phase}.json`);
4456
- const absolutePath = path10.resolve(directory, ".swarm", relativePath);
5533
+ const relativePath = path13.join("evidence", `req-coverage-phase-${phase}.json`);
5534
+ const absolutePath = path13.resolve(directory, ".swarm", relativePath);
4457
5535
  try {
4458
- await fs6.access(absolutePath);
5536
+ await fs7.access(absolutePath);
4459
5537
  return { exists: true, path: absolutePath };
4460
5538
  } catch {
4461
5539
  return { exists: false, path: absolutePath };
4462
5540
  }
4463
5541
  }
4464
- async function archiveEvidence(directory, maxAgeDays, maxBundles) {
4465
- const taskIds = await _internals9.listEvidenceTaskIds(directory);
4466
- const cutoffDate = new Date;
5542
+ async function archiveEvidence(directory, maxAgeDays, maxBundles, options) {
5543
+ const taskIds = await _internals10.listEvidenceTaskIds(directory);
5544
+ const cutoffDate = options?.now ? new Date(options.now) : _internals10.now();
4467
5545
  cutoffDate.setDate(cutoffDate.getDate() - maxAgeDays);
4468
5546
  const cutoffIso = cutoffDate.toISOString();
5547
+ const selected = new Set;
5548
+ const selectedByAge = new Set;
5549
+ const selectedByCount = new Set;
4469
5550
  const archived = [];
5551
+ const failed = [];
4470
5552
  const remainingBundles = [];
4471
5553
  for (const taskId of taskIds) {
4472
- const result = await _internals9.loadEvidence(directory, taskId);
5554
+ let result;
5555
+ try {
5556
+ result = await _internals10.loadEvidence(directory, taskId);
5557
+ } catch {
5558
+ warn("archive: skipping corrupt or unreadable evidence for task", taskId);
5559
+ continue;
5560
+ }
4473
5561
  if (result.status !== "found") {
4474
5562
  continue;
4475
5563
  }
4476
5564
  if (result.bundle.updated_at < cutoffIso) {
4477
- const deleted = await deleteEvidence(directory, taskId);
4478
- if (deleted) {
4479
- archived.push(taskId);
4480
- }
5565
+ selected.add(taskId);
5566
+ selectedByAge.add(taskId);
4481
5567
  } else {
4482
5568
  remainingBundles.push({
4483
5569
  taskId,
@@ -4489,24 +5575,51 @@ async function archiveEvidence(directory, maxAgeDays, maxBundles) {
4489
5575
  remainingBundles.sort((a, b) => a.updatedAt.localeCompare(b.updatedAt));
4490
5576
  const toDelete = remainingBundles.length - maxBundles;
4491
5577
  for (let i = 0;i < toDelete; i++) {
4492
- const deleted = await deleteEvidence(directory, remainingBundles[i].taskId);
4493
- if (deleted) {
4494
- archived.push(remainingBundles[i].taskId);
4495
- }
5578
+ selected.add(remainingBundles[i].taskId);
5579
+ selectedByCount.add(remainingBundles[i].taskId);
4496
5580
  }
4497
5581
  }
4498
- return archived;
5582
+ if (!options?.dryRun) {
5583
+ for (const taskId of [...selected].sort()) {
5584
+ const deleted = await _internals10.deleteEvidence(directory, taskId);
5585
+ if (deleted)
5586
+ archived.push(taskId);
5587
+ else
5588
+ failed.push(taskId);
5589
+ }
5590
+ }
5591
+ const evaluation = await archiveEvaluationArtifacts({
5592
+ directory,
5593
+ maxAgeDays,
5594
+ maxBundles,
5595
+ dryRun: options?.dryRun,
5596
+ now: options?.now
5597
+ });
5598
+ if (options?.report) {
5599
+ return {
5600
+ inventoryEvidence: [...taskIds].sort(),
5601
+ selectedEvidence: [...selected].sort(),
5602
+ selectedEvidenceByAge: [...selectedByAge].sort(),
5603
+ selectedEvidenceByCount: [...selectedByCount].sort(),
5604
+ archivedEvidence: archived.sort(),
5605
+ failedEvidence: failed.sort(),
5606
+ evaluation
5607
+ };
5608
+ }
5609
+ return archived.sort();
4499
5610
  }
4500
5611
  function validateEvidence(evidence) {
4501
5612
  return EvidenceSchema.parse(evidence);
4502
5613
  }
4503
- var _internals9 = {
5614
+ var _internals10 = {
4504
5615
  wrapFlatRetrospective,
4505
5616
  loadEvidence,
4506
5617
  listEvidenceTaskIds,
4507
5618
  validateProjectRoot,
4508
5619
  validateEvidence,
4509
- saveEvidence
5620
+ saveEvidence,
5621
+ deleteEvidence,
5622
+ now: () => new Date
4510
5623
  };
4511
5624
 
4512
5625
  // src/evidence/gate-bridge.ts
@@ -4619,4 +5732,4 @@ function mergeDurableGateEntriesFromEvidence(taskId, entries, evidence) {
4619
5732
  return merged;
4620
5733
  }
4621
5734
 
4622
- export { PlanSchema, getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, resetToRemoteBranch, resetToMainAfterMerge, initDurableStatusPath, getAllWorktreeMergeFailures, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, derivePlanId, computePlanHash, initLedger, appendLedgerEvent, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, RetrospectiveEvidenceSchema, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };
5735
+ export { PlanSchema, getExecutor, init_executor, MAX_TRANSIENT_RETRIES, isTransientSpawnError, transientBackoff, getGitRepositoryStatus, isGitRepo, getDefaultBaseBranch, resetToRemoteBranch, resetToMainAfterMerge, initDurableStatusPath, getAllWorktreeMergeFailures, isStateUnreadable, loadEpicSessionState, isEpicModeActive, enableEpicMode, disableEpicMode, normalizePath, pathsConflict, isGlobalFile, isProtectedPath, readTaskScopes, computeSpecHash, derivePlanId, computePlanHash, initLedger, appendLedgerEvent, loadPlanJsonOnly, loadPlan, savePlan, closePlanTerminalState, derivePlanMarkdown, RetrospectiveEvidenceSchema, IdentifierSchema, EvaluationTaskV1Schema, GateNameSchema, GateAuditManifestV1Schema, GateAuditResultV1Schema, canonicalJson, canonicalHash, computeTaskInputContentHash, computeTaskSetContentHash, computeManifestContentHash, resolveContainedExistingPath, admitEvaluationTask, saveTaskSetSnapshot, saveGateAuditResult, saveGateAuditManifest, readGateAuditManifest, readGateAuditResult, listGateAuditResults, isValidEvidenceType, sanitizeTaskId2 as sanitizeTaskId, validateProjectRoot, saveEvidence, loadEvidence, listEvidenceTaskIds, checkRequirementCoverage, archiveEvidence, readDurableGateEvidence, getDurableGateEvidenceStatus, getDurableGateEvidenceStatusForTask, mergeDurableGateEntriesFromEvidence };