opencode-swarm 7.115.0 → 7.116.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/.opencode/skills/commit-pr/SKILL.md +60 -579
- package/README.md +3 -1
- package/dist/cli/{curator-drift-c0by7063.js → curator-drift-7p2ky1th.js} +2 -2
- package/dist/cli/{curator-llm-factory-6n9sgaj8.js → curator-llm-factory-9qn52fnv.js} +17 -16
- package/dist/cli/{curator-hpc4tsjv.js → curator-yxmtp8py.js} +17 -16
- package/dist/cli/{dispatch-mczsxy5s.js → dispatch-07jgc3hn.js} +1 -1
- package/dist/cli/{evidence-summary-service-fehwj116.js → evidence-summary-service-bwt6drb3.js} +7 -7
- package/dist/cli/{gate-evidence-vkpqt300.js → gate-evidence-j2m4cd85.js} +5 -4
- package/dist/cli/{guardrail-explain-hecpd738.js → guardrail-explain-xvyz2eag.js} +18 -17
- package/dist/cli/{hive-promoter-0kfb4vjk.js → hive-promoter-ykqc60w1.js} +17 -16
- package/dist/cli/{index-0p660a87.js → index-0pfqcjp6.js} +5 -1
- package/dist/cli/{index-g40r5d08.js → index-34g1mv3c.js} +1 -1
- package/dist/cli/{index-4sckv7y6.js → index-42cx6g2h.js} +9 -4
- package/dist/cli/index-4905hd2m.js +19 -0
- package/dist/cli/{index-80jkseqw.js → index-5bcrp3bc.js} +2 -2
- package/dist/cli/{index-rk6qhyng.js → index-5h7d37nf.js} +7 -7
- package/dist/cli/{index-m766gvdt.js → index-7kcpkm4y.js} +7016 -1994
- package/dist/cli/{index-4h2ea8h5.js → index-abjjn4yh.js} +2 -2
- package/dist/cli/{index-m0rce3x6.js → index-efdyjgeh.js} +2 -2
- package/dist/cli/{index-jtqkh8jf.js → index-fsrp8wp3.js} +10 -1
- package/dist/cli/{index-8f2270hc.js → index-g4p13g8t.js} +3 -3
- package/dist/cli/{index-wh8949ef.js → index-kzj3fp7c.js} +3 -3
- package/dist/cli/{index-ga4ta3cr.js → index-nprawcc0.js} +7 -1
- package/dist/cli/{index-2etc05tv.js → index-p9jf8gt8.js} +28 -20
- package/dist/cli/{index-pa6t33pt.js → index-q2rrv0cy.js} +11 -0
- package/dist/cli/{index-n18yy0z1.js → index-t5f01hnz.js} +3 -3
- package/dist/cli/{index-6f6y2rbp.js → index-wegc6xwr.js} +3 -3
- package/dist/cli/{index-x12hvfpf.js → index-wxkf3089.js} +3 -3
- package/dist/cli/{index-yrrqs4b0.js → index-xdsd2hma.js} +1164 -51
- package/dist/cli/index.js +17 -16
- package/dist/cli/{knowledge-escalator-5gwp1ar8.js → knowledge-escalator-tthw6c9q.js} +8 -8
- package/dist/cli/{knowledge-events-m304swh6.js → knowledge-events-hrbmwxxj.js} +6 -5
- package/dist/cli/{knowledge-store-9babt8rd.js → knowledge-store-5cns5sns.js} +6 -6
- package/dist/cli/{knowledge-validator-t9sym59g.js → knowledge-validator-xre0s774.js} +7 -7
- package/dist/cli/{pending-delegations-ddbkbgxy.js → pending-delegations-9cn17983.js} +4 -4
- package/dist/cli/{pr-subscriptions-vwv7zm2p.js → pr-subscriptions-8a6jwek2.js} +5 -5
- package/dist/cli/runtime-ykafcwc6.js +3397 -0
- package/dist/cli/{skill-generator-tav44xpm.js → skill-generator-22nk6c6a.js} +9 -9
- package/dist/cli/{telemetry-jny76r83.js → telemetry-qh78ymq4.js} +1 -1
- package/dist/commands/archive.d.ts +4 -4
- package/dist/commands/close.d.ts +12 -0
- package/dist/commands/command-dispatch.d.ts +2 -0
- package/dist/commands/gate-audit.d.ts +28 -0
- package/dist/commands/gate-stats.d.ts +1 -0
- package/dist/commands/index.d.ts +4 -0
- package/dist/commands/registry.d.ts +25 -8
- package/dist/commands/reset-backup.d.ts +32 -0
- package/dist/commands/reset-session.d.ts +2 -0
- package/dist/commands/reset.d.ts +9 -0
- package/dist/config/plan-schema.d.ts +7 -7
- package/dist/evaluation/contracts.d.ts +545 -0
- package/dist/evaluation/disposable-worktree.d.ts +38 -0
- package/dist/evaluation/fixtures.d.ts +8 -0
- package/dist/evaluation/gate-audit.d.ts +32 -0
- package/dist/evaluation/gate-ground-truth.d.ts +43 -0
- package/dist/evaluation/gate-stats.d.ts +47 -0
- package/dist/evaluation/hashing.d.ts +34 -0
- package/dist/evaluation/index.d.ts +17 -0
- package/dist/evaluation/model-dispatcher.d.ts +30 -0
- package/dist/evaluation/public-api.d.ts +87 -0
- package/dist/evaluation/retention.d.ts +56 -0
- package/dist/evaluation/runner.d.ts +48 -0
- package/dist/evaluation/statistics.d.ts +9 -0
- package/dist/evaluation/store.d.ts +26 -0
- package/dist/evidence/manager.d.ts +18 -0
- package/dist/evidence/task-file.d.ts +7 -0
- package/dist/index.d.ts +4 -0
- package/dist/index.js +468 -451
- package/dist/mutation/engine.d.ts +29 -5
- package/dist/telemetry.d.ts +11 -1
- package/dist/tools/plugin-registration.d.ts +2 -1
- package/dist/tools/sast-scan.d.ts +9 -0
- package/dist/tools/swarm-command.d.ts +2 -1
- package/dist/tools/update-task-status.d.ts +16 -4
- package/dist/utils/external-tool-runner.d.ts +2 -1
- package/dist/utils/swarm-artifact-cache.d.ts +13 -0
- package/evaluation-fixtures/tier1/boundary-error/environment/baseline.ts +3 -0
- package/evaluation-fixtures/tier1/boundary-error/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/boundary-error/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/boundary-error/instruction.md +1 -0
- package/evaluation-fixtures/tier1/boundary-error/manifest.json +1 -0
- package/evaluation-fixtures/tier1/branch-swap/environment/baseline.ts +3 -0
- package/evaluation-fixtures/tier1/branch-swap/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/branch-swap/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/branch-swap/instruction.md +1 -0
- package/evaluation-fixtures/tier1/branch-swap/manifest.json +1 -0
- package/evaluation-fixtures/tier1/curated-off-by-one/environment/baseline.ts +3 -0
- package/evaluation-fixtures/tier1/curated-off-by-one/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/curated-off-by-one/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/curated-off-by-one/instruction.md +1 -0
- package/evaluation-fixtures/tier1/curated-off-by-one/manifest.json +1 -0
- package/evaluation-fixtures/tier1/guard-removal/environment/baseline.ts +3 -0
- package/evaluation-fixtures/tier1/guard-removal/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/guard-removal/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/guard-removal/instruction.md +1 -0
- package/evaluation-fixtures/tier1/guard-removal/manifest.json +1 -0
- package/evaluation-fixtures/tier1/injection-prone-string/environment/baseline.ts +3 -0
- package/evaluation-fixtures/tier1/injection-prone-string/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/injection-prone-string/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/injection-prone-string/instruction.md +1 -0
- package/evaluation-fixtures/tier1/injection-prone-string/manifest.json +1 -0
- package/evaluation-fixtures/tier1/missing-auth-check/environment/baseline.ts +4 -0
- package/evaluation-fixtures/tier1/missing-auth-check/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/missing-auth-check/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/missing-auth-check/instruction.md +1 -0
- package/evaluation-fixtures/tier1/missing-auth-check/manifest.json +1 -0
- package/evaluation-fixtures/tier1/missing-await/environment/baseline.ts +7 -0
- package/evaluation-fixtures/tier1/missing-await/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/missing-await/environment/defect.ts +4 -0
- package/evaluation-fixtures/tier1/missing-await/instruction.md +1 -0
- package/evaluation-fixtures/tier1/missing-await/manifest.json +1 -0
- package/evaluation-fixtures/tier1/mutation-off-by-one/environment/baseline.ts +3 -0
- package/evaluation-fixtures/tier1/mutation-off-by-one/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/mutation-off-by-one/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/mutation-off-by-one/instruction.md +1 -0
- package/evaluation-fixtures/tier1/mutation-off-by-one/manifest.json +1 -0
- package/evaluation-fixtures/tier1/null-substitution/environment/baseline.ts +3 -0
- package/evaluation-fixtures/tier1/null-substitution/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/null-substitution/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/null-substitution/instruction.md +1 -0
- package/evaluation-fixtures/tier1/null-substitution/manifest.json +1 -0
- package/evaluation-fixtures/tier1/operator-swap/environment/baseline.ts +3 -0
- package/evaluation-fixtures/tier1/operator-swap/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/operator-swap/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/operator-swap/instruction.md +1 -0
- package/evaluation-fixtures/tier1/operator-swap/manifest.json +1 -0
- package/evaluation-fixtures/tier1/side-effect-deletion/environment/baseline.ts +4 -0
- package/evaluation-fixtures/tier1/side-effect-deletion/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/side-effect-deletion/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/side-effect-deletion/instruction.md +1 -0
- package/evaluation-fixtures/tier1/side-effect-deletion/manifest.json +1 -0
- package/evaluation-fixtures/tier1/swallowed-error/environment/baseline.ts +3 -0
- package/evaluation-fixtures/tier1/swallowed-error/environment/defect.test.ts +3 -0
- package/evaluation-fixtures/tier1/swallowed-error/environment/defect.ts +3 -0
- package/evaluation-fixtures/tier1/swallowed-error/instruction.md +1 -0
- package/evaluation-fixtures/tier1/swallowed-error/manifest.json +1 -0
- package/package.json +2 -1
|
@@ -12,10 +12,7 @@ import {
|
|
|
12
12
|
import {
|
|
13
13
|
readSwarmFileAsync,
|
|
14
14
|
validateSwarmPath
|
|
15
|
-
} from "./index-
|
|
16
|
-
import {
|
|
17
|
-
readCachedParsedFile
|
|
18
|
-
} from "./index-jtqkh8jf.js";
|
|
15
|
+
} from "./index-kzj3fp7c.js";
|
|
19
16
|
import {
|
|
20
17
|
criticalWarn,
|
|
21
18
|
error,
|
|
@@ -27,17 +24,23 @@ import {
|
|
|
27
24
|
readTaskEvidence,
|
|
28
25
|
readTaskEvidenceRaw,
|
|
29
26
|
sanitizeTaskId
|
|
30
|
-
} from "./index-
|
|
27
|
+
} from "./index-abjjn4yh.js";
|
|
28
|
+
import {
|
|
29
|
+
atomicWriteFile
|
|
30
|
+
} from "./index-0pfqcjp6.js";
|
|
31
31
|
import {
|
|
32
32
|
ZodError,
|
|
33
33
|
exports_external
|
|
34
34
|
} from "./index-293f68mj.js";
|
|
35
35
|
import {
|
|
36
36
|
withEvidenceLock
|
|
37
|
-
} from "./index-
|
|
37
|
+
} from "./index-42cx6g2h.js";
|
|
38
|
+
import {
|
|
39
|
+
readCachedParsedFile
|
|
40
|
+
} from "./index-fsrp8wp3.js";
|
|
38
41
|
import {
|
|
39
42
|
emit
|
|
40
|
-
} from "./index-
|
|
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
|
|
3878
|
+
import * as fs7 from "fs/promises";
|
|
3876
3879
|
import * as os4 from "os";
|
|
3877
|
-
import * as
|
|
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 =
|
|
4198
|
-
return resolved ===
|
|
5275
|
+
const resolved = realpathSync2(directory);
|
|
5276
|
+
return resolved === realpathSync2(os4.tmpdir()) || resolved === realpathSync2(os4.homedir());
|
|
4199
5277
|
} catch {
|
|
4200
|
-
const resolved =
|
|
4201
|
-
return resolved ===
|
|
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(
|
|
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 =
|
|
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 =
|
|
5311
|
+
const parent = path13.dirname(current);
|
|
4234
5312
|
if (parent === current)
|
|
4235
5313
|
break;
|
|
4236
|
-
if (
|
|
5314
|
+
if (path13.dirname(parent) === parent) {
|
|
4237
5315
|
current = parent;
|
|
4238
5316
|
continue;
|
|
4239
5317
|
}
|
|
4240
|
-
const parentSwarm =
|
|
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
|
-
|
|
5337
|
+
_internals10.validateProjectRoot(directory);
|
|
4260
5338
|
const sanitizedTaskId = sanitizeTaskId2(taskId);
|
|
4261
|
-
const relativePath =
|
|
5339
|
+
const relativePath = path13.join("evidence", sanitizedTaskId, "evidence.json");
|
|
4262
5340
|
validateSwarmPath(directory, relativePath);
|
|
4263
|
-
|
|
5341
|
+
_internals10.validateEvidence(evidence);
|
|
4264
5342
|
return withEvidenceLock(directory, relativePath, "evidence-manager", sanitizedTaskId, async () => {
|
|
4265
5343
|
const evidencePath = validateSwarmPath(directory, relativePath);
|
|
4266
|
-
const evidenceDir =
|
|
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 =
|
|
5387
|
+
const tempPath = path13.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
|
|
4310
5388
|
try {
|
|
4311
5389
|
await bunWrite(tempPath, bundleJson);
|
|
4312
|
-
await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
5452
|
+
const evidenceDir = path13.dirname(evidencePath);
|
|
4375
5453
|
const bundleJson = JSON.stringify(validated);
|
|
4376
|
-
const tempPath =
|
|
5454
|
+
const tempPath = path13.join(evidenceDir, `evidence.json.tmp.${Date.now()}.${process.pid}`);
|
|
4377
5455
|
try {
|
|
4378
5456
|
await bunWrite(tempPath, bundleJson);
|
|
4379
|
-
await
|
|
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 =
|
|
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 =
|
|
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 =
|
|
4456
|
-
const absolutePath =
|
|
5533
|
+
const relativePath = path13.join("evidence", `req-coverage-phase-${phase}.json`);
|
|
5534
|
+
const absolutePath = path13.resolve(directory, ".swarm", relativePath);
|
|
4457
5535
|
try {
|
|
4458
|
-
await
|
|
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
|
|
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
|
-
|
|
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
|
-
|
|
4478
|
-
|
|
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
|
-
|
|
4493
|
-
|
|
4494
|
-
archived.push(remainingBundles[i].taskId);
|
|
4495
|
-
}
|
|
5578
|
+
selected.add(remainingBundles[i].taskId);
|
|
5579
|
+
selectedByCount.add(remainingBundles[i].taskId);
|
|
4496
5580
|
}
|
|
4497
5581
|
}
|
|
4498
|
-
|
|
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
|
|
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 };
|