@agentskit/harness 0.1.0 → 0.4.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 (42) hide show
  1. package/CHANGELOG.md +33 -32
  2. package/CONTRIBUTING.md +60 -12
  3. package/MANIFESTO.md +23 -0
  4. package/README.md +276 -144
  5. package/capabilities/public-surface.json +668 -0
  6. package/compatibility/manifest.json +17 -0
  7. package/compatibility/migration.md +10 -0
  8. package/compatibility/report.json +23 -0
  9. package/compatibility/report.md +22 -0
  10. package/compatibility/rollback.md +8 -0
  11. package/dist/cli.js +958 -239
  12. package/dist/cli.js.map +1 -1
  13. package/dist/index.d.ts +1338 -122
  14. package/dist/index.js +2273 -353
  15. package/dist/index.js.map +1 -1
  16. package/docs/ADR-0025-portable-orchestration-controls.md +27 -0
  17. package/docs/ADR-0026-kernel-adapters-boundary.md +82 -0
  18. package/docs/GETTING-STARTED.md +18 -0
  19. package/docs/MODULE-BOUNDARIES.md +143 -0
  20. package/docs/ORGANIZATION.md +46 -0
  21. package/docs/TROUBLESHOOTING.md +24 -0
  22. package/examples/minimum-profile.mjs +27 -0
  23. package/package.json +52 -34
  24. package/release/manifest.json +14 -0
  25. package/release/notes.md +10 -0
  26. package/release/qualification.json +14 -0
  27. package/docs/ADR-0025-ci-dogfood.md +0 -22
  28. package/docs/ADR-0026-ci-evidence-artifact.md +0 -22
  29. package/docs/ADR-0027-portable-evidence.md +0 -19
  30. package/docs/ADR-0028-effective-metrics.md +0 -20
  31. package/docs/ADR-0029-honest-ci-preparation.md +0 -20
  32. package/docs/ADR-0030-agentskit-os-benchmark-bridge.md +0 -20
  33. package/docs/ADR-0031-real-provider-baseline.md +0 -18
  34. package/docs/ADR-0032-harness-equivalent-benchmark.md +0 -25
  35. package/docs/ADR-0033-portable-agent-gate.md +0 -25
  36. package/docs/ADR-0034-measurement-quality-gates.md +0 -25
  37. package/docs/ADR-0035-reproducible-benchmark-samples.md +0 -20
  38. package/docs/ADR-0036-comparable-baseline-samples.md +0 -20
  39. package/docs/ADR-0037-replicated-baseline-collection.md +0 -27
  40. package/docs/ADR-0038-end-to-end-benchmark-boundary.md +0 -28
  41. package/docs/ADR-0039-artifact-and-protocol-metrics.md +0 -39
  42. package/docs/ADR-0040-benchmark-corpus-surfaces.md +0 -32
package/dist/cli.js CHANGED
@@ -1,13 +1,13 @@
1
1
  #!/usr/bin/env node
2
- import { createPrivateKey, createPublicKey, sign, verify, createHash } from 'crypto';
3
- import { readFileSync, mkdirSync, writeFileSync, existsSync, openSync, writeSync, appendFileSync, closeSync, unlinkSync, mkdtempSync, renameSync, rmSync, lstatSync, readdirSync } from 'fs';
2
+ import { createHash, randomUUID, createPrivateKey, createPublicKey, sign, verify } from 'crypto';
3
+ import { readFileSync, mkdirSync, existsSync, unlinkSync, openSync, writeFileSync, closeSync, readdirSync, writeSync, appendFileSync, mkdtempSync, renameSync, rmSync } from 'fs';
4
4
  import { Command } from 'commander';
5
- import { resolve, dirname, relative, join, sep } from 'path';
5
+ import { resolve, dirname, relative, basename, join, extname, sep } from 'path';
6
6
  import { execFile, spawn } from 'child_process';
7
7
  import { promisify } from 'util';
8
- import { tmpdir } from 'os';
8
+ import { tmpdir, cpus, loadavg, freemem, totalmem } from 'os';
9
9
 
10
- // src/constants.ts
10
+ // src/kernel/constants.ts
11
11
  var STATES = [
12
12
  "CLARIFYING",
13
13
  "PLANNED",
@@ -25,7 +25,7 @@ var LEGAL_TRANSITIONS = {
25
25
  CLARIFYING: ["PLANNED", "BLOCKED", "CANCELLED"],
26
26
  PLANNED: ["IMPLEMENTING", "CLARIFYING", "STALE", "CANCELLED"],
27
27
  IMPLEMENTING: ["VERIFYING", "CLARIFYING", "STALE", "CANCELLED"],
28
- VERIFYING: ["AWAITING_HUMAN_APPROVAL", "BLOCKED", "STALE", "CANCELLED"],
28
+ VERIFYING: ["AWAITING_HUMAN_APPROVAL", "COMPLETE", "BLOCKED", "STALE", "CANCELLED"],
29
29
  AWAITING_HUMAN_APPROVAL: ["AWAITING_AUTHORIZATION", "COMPLETE", "BLOCKED", "IMPLEMENTING", "STALE", "CANCELLED"],
30
30
  AWAITING_AUTHORIZATION: ["COMPLETE", "BLOCKED", "IMPLEMENTING", "STALE", "CANCELLED"],
31
31
  COMPLETE: ["STALE", "SUPERSEDED"],
@@ -37,7 +37,7 @@ var LEGAL_TRANSITIONS = {
37
37
  var REAL_CATEGORIES = /* @__PURE__ */ new Set(["endpoint", "database", "cli", "mcp", "ui"]);
38
38
  var DECISIONS = /* @__PURE__ */ new Set(["approved", "approve", "yes", "ok", "rejected", "reject", "no"]);
39
39
 
40
- // src/errors.ts
40
+ // src/kernel/errors.ts
41
41
  var HarnessError = class extends Error {
42
42
  code;
43
43
  constructor(message, code = "HARNESS_ERROR") {
@@ -50,7 +50,7 @@ var fail = (message, code = "HARNESS_ERROR") => {
50
50
  throw new HarnessError(message, code);
51
51
  };
52
52
 
53
- // src/profiles.ts
53
+ // src/profiles/index.ts
54
54
  var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
55
55
  var record = (value, label) => {
56
56
  if (!isRecord(value)) fail(`${label} must be an object.`, "INVALID_CONFIG");
@@ -63,9 +63,11 @@ var id = (value, label) => {
63
63
  var parents = (value, label) => value === void 0 ? [] : Array.isArray(value) ? value.map((item, index2) => id(item, `${label}[${index2}]`)) : [id(value, label)];
64
64
  var merge = (base, overlay) => {
65
65
  const result = { ...base };
66
- for (const key of ["surfaces", "budget", "cleanup"]) {
66
+ for (const key of ["surfaces", "budget", "cleanup", "runtime", "verification"]) {
67
67
  if (overlay[key] !== void 0) result[key] = { ...isRecord(result[key]) ? result[key] : {}, ...record(overlay[key], `profile.${key}`) };
68
68
  }
69
+ if (overlay["autonomy"] !== void 0) result["autonomy"] = overlay["autonomy"];
70
+ if (Array.isArray(overlay["checks"])) result["checks"] = overlay["checks"];
69
71
  if (overlay["checkOverrides"] !== void 0) {
70
72
  if (!Array.isArray(overlay["checkOverrides"])) fail("profile.checkOverrides must be an array.", "INVALID_CONFIG");
71
73
  const checks = Array.isArray(result["checks"]) ? [...result["checks"]] : [];
@@ -138,7 +140,7 @@ var cleanConfiguredArtifacts = (loaded) => {
138
140
  };
139
141
  var fileContents = (path) => readFileSync(path, "utf8");
140
142
 
141
- // src/types.ts
143
+ // src/kernel/types.ts
142
144
  var SURFACE_NAMES = ["logic", "endpoint", "database", "cli", "mcp", "ui", "docs"];
143
145
  var CHECK_CATEGORIES = ["build", "test", "lint", ...SURFACE_NAMES, "custom"];
144
146
  var RUN_STATES = [
@@ -155,7 +157,7 @@ var RUN_STATES = [
155
157
  "SUPERSEDED"
156
158
  ];
157
159
 
158
- // src/config.ts
160
+ // src/execution/config.ts
159
161
  var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
160
162
  var stringValue = (value, label) => {
161
163
  if (typeof value !== "string") return fail(`${label} is required.`, "INVALID_CONFIG");
@@ -183,18 +185,20 @@ var surface = (value, name) => {
183
185
  var parseCheck = (value, index2) => {
184
186
  const record3 = asRecord(value, `checks[${index2}]`);
185
187
  const id2 = stringValue(record3["id"], `checks[${index2}].id`);
186
- const category = stringValue(record3["category"], `checks[${index2}].category`);
187
- if (!CHECK_CATEGORIES.includes(category)) fail(`checks[${index2}].category is invalid.`, "INVALID_CONFIG");
188
+ const category2 = stringValue(record3["category"], `checks[${index2}].category`);
189
+ if (!CHECK_CATEGORIES.includes(category2)) fail(`checks[${index2}].category is invalid.`, "INVALID_CONFIG");
188
190
  const command = stringValue(record3["command"], `checks[${index2}].command`);
189
- if (REAL_CATEGORIES.has(category) && record3["execution"] !== "real") fail(`checks[${index2}] must declare execution: real.`, "INVALID_CONFIG");
191
+ if (REAL_CATEGORIES.has(category2) && record3["execution"] !== "real") fail(`checks[${index2}] must declare execution: real.`, "INVALID_CONFIG");
190
192
  if (record3["evidence"] !== "structured") fail(`checks[${index2}] must declare evidence: structured.`, "INVALID_CONFIG");
191
193
  if (record3["capabilities"] !== void 0 && (!Array.isArray(record3["capabilities"]) || !record3["capabilities"].every((item) => typeof item === "string"))) fail(`checks[${index2}].capabilities must be strings.`, "INVALID_CONFIG");
192
194
  const capabilities = Array.isArray(record3["capabilities"]) ? record3["capabilities"].filter((item) => typeof item === "string") : void 0;
193
- if (category === "ui" && !capabilities?.includes("real-browser")) fail(`checks[${index2}] must declare real-browser.`, "INVALID_CONFIG");
194
- if (category === "ui" && !capabilities?.includes("screenshot")) fail(`checks[${index2}] must declare screenshot.`, "INVALID_CONFIG");
195
+ if (category2 === "ui" && !capabilities?.includes("real-browser")) fail(`checks[${index2}] must declare real-browser.`, "INVALID_CONFIG");
196
+ if (category2 === "ui" && !capabilities?.includes("screenshot")) fail(`checks[${index2}] must declare screenshot.`, "INVALID_CONFIG");
195
197
  if (record3["required"] !== void 0 && typeof record3["required"] !== "boolean") fail(`checks[${index2}].required must be boolean.`, "INVALID_CONFIG");
198
+ if (record3["required"] === false && typeof record3["reason"] !== "string") fail(`checks[${index2}].reason is required when required is false.`, "INVALID_CONFIG");
196
199
  if (record3["timeoutMs"] !== void 0 && (!Number.isInteger(record3["timeoutMs"]) || typeof record3["timeoutMs"] !== "number" || record3["timeoutMs"] < 1)) fail(`checks[${index2}].timeoutMs must be positive.`, "INVALID_CONFIG");
197
- return { id: id2, category, command, required: record3["required"] !== false, timeoutMs: typeof record3["timeoutMs"] === "number" ? record3["timeoutMs"] : 12e4, ...record3["execution"] === "real" ? { execution: "real" } : {}, ...capabilities ? { capabilities } : {}, evidence: "structured" };
200
+ const dependsOn = record3["dependsOn"] === void 0 ? void 0 : stringArray(record3["dependsOn"], `checks[${index2}].dependsOn`);
201
+ return { id: id2, category: category2, command, required: record3["required"] !== false, ...typeof record3["reason"] === "string" ? { reason: record3["reason"] } : {}, timeoutMs: typeof record3["timeoutMs"] === "number" ? record3["timeoutMs"] : 12e4, ...record3["execution"] === "real" ? { execution: "real" } : {}, ...capabilities ? { capabilities } : {}, ...dependsOn ? { dependsOn: [...new Set(dependsOn)] } : {}, evidence: "structured" };
198
202
  };
199
203
  var parseOutcome = (value, index2, checks) => {
200
204
  const record3 = asRecord(value, `contract.outcomes[${index2}]`);
@@ -208,10 +212,28 @@ var validateConfig = (rawValue) => {
208
212
  const raw = resolveProfile(asRecord(rawValue, "verification config"));
209
213
  if (raw["schemaVersion"] !== 1) fail("verification config schemaVersion must be 1.", "INVALID_CONFIG");
210
214
  const project = stringValue(raw["project"], "verification config project");
215
+ const runtimeRaw = asRecord(raw["runtime"] ?? { kind: "process" }, "runtime");
216
+ if (runtimeRaw["kind"] !== "process" && runtimeRaw["kind"] !== "docker") fail("runtime.kind must be process or docker.", "INVALID_CONFIG");
217
+ const runtime = { kind: runtimeRaw["kind"] };
218
+ if (raw["autonomy"] !== void 0 && raw["autonomy"] !== "controlled" && raw["autonomy"] !== "yolo") fail("autonomy must be controlled or yolo.", "INVALID_CONFIG");
219
+ const autonomy = raw["autonomy"] ?? "controlled";
211
220
  const contractRaw = asRecord(raw["contract"], "contract");
212
221
  const rawChecks = raw["checks"];
213
222
  const checks = Array.isArray(rawChecks) ? rawChecks.map(parseCheck) : fail("checks must be a non-empty array.", "INVALID_CONFIG");
214
223
  if (!checks.length || new Set(checks.map((check) => check.id)).size !== checks.length) fail("check ids must be unique.", "INVALID_CONFIG");
224
+ const checkIds = new Set(checks.map((check) => check.id));
225
+ for (const check of checks) for (const dependency of check.dependsOn ?? []) if (!checkIds.has(dependency) || dependency === check.id) fail(`check ${check.id} has an invalid dependency: ${dependency}.`, "INVALID_CONFIG");
226
+ const visiting = /* @__PURE__ */ new Set();
227
+ const visited = /* @__PURE__ */ new Set();
228
+ const visit = (id2) => {
229
+ if (visiting.has(id2)) fail(`check dependency cycle includes ${id2}.`, "INVALID_CONFIG");
230
+ if (visited.has(id2)) return;
231
+ visiting.add(id2);
232
+ for (const dependency of checks.find((check) => check.id === id2)?.dependsOn ?? []) visit(dependency);
233
+ visiting.delete(id2);
234
+ visited.add(id2);
235
+ };
236
+ for (const check of checks) visit(check.id);
215
237
  const scopeRaw = asRecord(contractRaw["scope"], "contract.scope");
216
238
  const scope = { inScope: stringArray(scopeRaw["inScope"], "contract.scope.inScope"), outOfScope: stringArray(scopeRaw["outOfScope"], "contract.scope.outOfScope") };
217
239
  const ambiguities = stringArray(contractRaw["ambiguities"], "contract.ambiguities");
@@ -228,13 +250,15 @@ var validateConfig = (rawValue) => {
228
250
  if (trackingRaw["required"] !== true && typeof trackingRaw["reason"] !== "string") fail("tracking.reason is required when tracking is disabled.", "INVALID_CONFIG");
229
251
  const budgetRaw = raw["budget"] === void 0 ? void 0 : asRecord(raw["budget"], "budget");
230
252
  if (budgetRaw && budgetRaw["maxDurationMs"] !== void 0 && (!Number.isInteger(budgetRaw["maxDurationMs"]) || typeof budgetRaw["maxDurationMs"] !== "number" || budgetRaw["maxDurationMs"] < 1)) fail("budget.maxDurationMs must be positive.", "INVALID_CONFIG");
253
+ const verificationRaw = raw["verification"] === void 0 ? void 0 : asRecord(raw["verification"], "verification");
254
+ if (verificationRaw && verificationRaw["maxConcurrency"] !== void 0 && (!Number.isInteger(verificationRaw["maxConcurrency"]) || typeof verificationRaw["maxConcurrency"] !== "number" || verificationRaw["maxConcurrency"] < 1)) fail("verification.maxConcurrency must be a positive integer.", "INVALID_CONFIG");
231
255
  const cleanupRaw = raw["cleanup"] === void 0 ? void 0 : asRecord(raw["cleanup"], "cleanup");
232
256
  const cleanup = cleanupRaw ? { roots: cleanupRaw["roots"] === void 0 ? void 0 : stringArray(cleanupRaw["roots"], "cleanup.roots") } : void 0;
233
257
  const benchmarkRaw = raw["benchmark"] === void 0 ? void 0 : asRecord(raw["benchmark"], "benchmark");
234
258
  const benchmark2 = benchmarkRaw ? { suiteId: stringValue(benchmarkRaw["suiteId"], "benchmark.suiteId"), taskId: stringValue(benchmarkRaw["taskId"], "benchmark.taskId"), mode: benchmarkRaw["mode"] === "harness" ? "harness" : fail("benchmark.mode must be harness.", "INVALID_CONFIG") } : void 0;
235
259
  const contract = { intent: stringValue(contractRaw["intent"], "contract.intent"), scope, ambiguities, outcomes };
236
260
  const tracking = { required: trackingRaw["required"] === true, ...typeof trackingRaw["target"] === "string" ? { target: trackingRaw["target"] } : {}, ...typeof trackingRaw["reason"] === "string" ? { reason: trackingRaw["reason"] } : {} };
237
- return { schemaVersion: 1, project, ...typeof raw["root"] === "string" ? { root: raw["root"] } : {}, ...typeof raw["stateDir"] === "string" ? { stateDir: raw["stateDir"] } : {}, profile: typeof raw["profile"] === "string" ? raw["profile"] : "strict", contract, surfaces, checks, tracking, ...budgetRaw ? { budget: { maxDurationMs: budgetRaw["maxDurationMs"] } } : {}, ...cleanup ? { cleanup } : {}, ...benchmark2 ? { benchmark: benchmark2 } : {} };
261
+ return { schemaVersion: 1, project, ...typeof raw["root"] === "string" ? { root: raw["root"] } : {}, ...typeof raw["stateDir"] === "string" ? { stateDir: raw["stateDir"] } : {}, profile: typeof raw["profile"] === "string" ? raw["profile"] : "strict", runtime, autonomy, contract, surfaces, checks, tracking, ...verificationRaw ? { verification: { maxConcurrency: verificationRaw["maxConcurrency"] } } : {}, ...budgetRaw ? { budget: { maxDurationMs: budgetRaw["maxDurationMs"] } } : {}, ...cleanup ? { cleanup } : {}, ...benchmark2 ? { benchmark: benchmark2 } : {} };
238
262
  };
239
263
  var loadConfig = (configPath = ".codex/verification.json") => {
240
264
  const absolute = resolve(configPath);
@@ -242,12 +266,12 @@ var loadConfig = (configPath = ".codex/verification.json") => {
242
266
  const rawRecord = asRecord(raw, "verification config");
243
267
  const root = resolve(dirname(absolute), typeof rawRecord["root"] === "string" ? rawRecord["root"] : ".");
244
268
  const stateDir = resolve(root, typeof rawRecord["stateDir"] === "string" ? rawRecord["stateDir"] : ".codex/verification");
245
- if (!pathInside(root, stateDir)) fail("stateDir must be inside the project root.", "INVALID_CONFIG");
269
+ if (stateDir === root) fail("stateDir must be separate from the project root.", "INVALID_CONFIG");
246
270
  const config = validateConfig(raw);
247
271
  return { absolute, root, stateDir, config, configHash: hashJson(config) };
248
272
  };
249
273
 
250
- // src/state-machine.ts
274
+ // src/kernel/state-machine.ts
251
275
  var transition = (run, to, reason, actor = "harness") => {
252
276
  if (!STATES.includes(to)) fail(`Unknown state ${to}.`, "INVALID_STATE");
253
277
  if (run.state !== to && !LEGAL_TRANSITIONS[run.state].some((state) => state === to)) fail(`Illegal transition ${run.state} -> ${to}.`, "INVALID_STATE");
@@ -263,7 +287,7 @@ var approvedDecision = (decision) => {
263
287
  };
264
288
  var HARNESS_EVENT_SCHEMA_VERSION = 1;
265
289
  var EVENT_LOG_GENESIS = "GENESIS";
266
- var HARNESS_EVENT_TYPES = ["run.created", "state.transitioned", "context.attached", "verification.completed", "approval.recorded", "authorization.recorded", "session.started", "session.resumed", "agent.turn.started", "policy.evaluated", "tool.approval.requested", "tool.approval.recorded", "tool.requested", "tool.execution.started", "tool.recovery.recorded", "tool.blocked", "tool.completed", "tool.failed", "session.ended"];
290
+ var HARNESS_EVENT_TYPES = ["run.created", "state.transitioned", "context.attached", "verification.completed", "artifact.recorded", "approval.recorded", "authorization.recorded", "session.started", "session.resumed", "agent.turn.started", "policy.evaluated", "tool.approval.requested", "tool.approval.recorded", "tool.requested", "tool.execution.started", "tool.recovery.recorded", "tool.blocked", "tool.completed", "tool.failed", "session.ended"];
267
291
  var SESSION_EVENT_TYPES = /* @__PURE__ */ new Set(["session.started", "session.resumed", "agent.turn.started", "policy.evaluated", "tool.approval.requested", "tool.approval.recorded", "tool.requested", "tool.execution.started", "tool.recovery.recorded", "tool.blocked", "tool.completed", "tool.failed", "session.ended"]);
268
292
  var eventPath = (stateDir, runId) => join(stateDir, "runs", runId, "events.ndjson");
269
293
  var lockPath = (stateDir, runId) => `${eventPath(stateDir, runId)}.lock`;
@@ -291,7 +315,9 @@ var eventDigest = (event) => sha256(JSON.stringify(eventBody(event)));
291
315
  var parseEvent = (value, expectedSequence) => {
292
316
  if (typeof value !== "object" || value === null || Array.isArray(value)) fail("Event log contains a non-object record.", "HARNESS_ERROR");
293
317
  const record3 = value;
294
- if (record3["schemaVersion"] !== HARNESS_EVENT_SCHEMA_VERSION || typeof record3["runId"] !== "string" || typeof record3["sequence"] !== "number" || record3["sequence"] !== expectedSequence || typeof record3["at"] !== "string" || typeof record3["sourceRevision"] !== "string" || typeof record3["configHash"] !== "string" || !isEventType(record3["type"]) || typeof record3["payload"] !== "object" || record3["payload"] === null || record3["sessionId"] !== void 0 && (typeof record3["sessionId"] !== "string" || !record3["sessionId"].trim()) || SESSION_EVENT_TYPES.has(record3["type"]) && typeof record3["sessionId"] !== "string") fail("Event log is invalid or out of order.", "HARNESS_ERROR");
318
+ const context2 = record3["correlation"];
319
+ const validContext = context2 === void 0 || typeof context2 === "object" && context2 !== null && !Array.isArray(context2) && Object.entries(context2).every(([key, value2]) => ["operationId", "runId", "sessionId", "turnId", "actionId", "traceId"].includes(key) && typeof value2 === "string" && /^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(value2)) && typeof context2["operationId"] === "string";
320
+ if (record3["schemaVersion"] !== HARNESS_EVENT_SCHEMA_VERSION || typeof record3["runId"] !== "string" || typeof record3["sequence"] !== "number" || record3["sequence"] !== expectedSequence || typeof record3["at"] !== "string" || typeof record3["sourceRevision"] !== "string" || typeof record3["configHash"] !== "string" || !isEventType(record3["type"]) || typeof record3["payload"] !== "object" || record3["payload"] === null || record3["sessionId"] !== void 0 && (typeof record3["sessionId"] !== "string" || !record3["sessionId"].trim()) || SESSION_EVENT_TYPES.has(record3["type"]) && typeof record3["sessionId"] !== "string" || !validContext) fail("Event log is invalid or out of order.", "HARNESS_ERROR");
295
321
  const hasPreviousHash = record3["previousHash"] !== void 0;
296
322
  const hasEventHash = record3["eventHash"] !== void 0;
297
323
  if (hasPreviousHash !== hasEventHash || hasPreviousHash && (typeof record3["previousHash"] !== "string" || record3["previousHash"] !== EVENT_LOG_GENESIS && !digest(record3["previousHash"]) || typeof record3["eventHash"] !== "string" || !digest(record3["eventHash"]))) fail("Event log integrity metadata is invalid.", "HARNESS_ERROR");
@@ -320,6 +346,7 @@ var FileEventStore = class {
320
346
  if (!isEventType(event.type)) fail("Event type is invalid.", "INVALID_INPUT");
321
347
  if (SESSION_EVENT_TYPES.has(event.type) && (!event.sessionId || !event.sessionId.trim())) fail("Session events require a sessionId.", "INVALID_INPUT");
322
348
  if (event.sessionId !== void 0 && !event.sessionId.trim()) fail("Event sessionId cannot be empty.", "INVALID_INPUT");
349
+ if (event.correlation !== void 0 && (!event.correlation.operationId || !event.correlation.operationId.trim())) fail("Event correlation operationId is required.", "INVALID_INPUT");
323
350
  const path = eventPath(this.stateDir, event.runId);
324
351
  const lock = lockPath(this.stateDir, event.runId);
325
352
  mkdirSync(join(this.stateDir, "runs", event.runId), { recursive: true });
@@ -334,7 +361,7 @@ var FileEventStore = class {
334
361
  try {
335
362
  const events2 = this.readUnlocked(event.runId);
336
363
  const previous = events2.at(-1);
337
- const body2 = { schemaVersion: HARNESS_EVENT_SCHEMA_VERSION, sequence: events2.length + 1, at: (/* @__PURE__ */ new Date()).toISOString(), runId: event.runId, sourceRevision: event.sourceRevision, configHash: event.configHash, ...event.sessionId ? { sessionId: event.sessionId } : {}, ...previous?.eventHash ? { previousHash: previous.eventHash } : events2.length ? {} : { previousHash: EVENT_LOG_GENESIS }, type: event.type, payload: event.payload };
364
+ const body2 = { schemaVersion: HARNESS_EVENT_SCHEMA_VERSION, sequence: events2.length + 1, at: (/* @__PURE__ */ new Date()).toISOString(), runId: event.runId, sourceRevision: event.sourceRevision, configHash: event.configHash, ...event.correlation ? { correlation: event.correlation } : {}, ...event.sessionId ? { sessionId: event.sessionId } : {}, ...previous?.eventHash ? { previousHash: previous.eventHash } : events2.length ? {} : { previousHash: EVENT_LOG_GENESIS }, type: event.type, payload: event.payload };
338
365
  const record3 = events2.length && !previous?.eventHash ? body2 : { ...body2, eventHash: eventDigest(body2) };
339
366
  appendFileSync(path, `${JSON.stringify(record3)}
340
367
  `, "utf8");
@@ -389,13 +416,16 @@ var recoverEventLogLock = ({ stateDir, runId, actor, maxAgeMs = 3e5 }) => {
389
416
  return fail("Event log lock owner is still alive.", "HARNESS_ERROR");
390
417
  };
391
418
 
392
- // src/plugins.ts
419
+ // src/kernel/plugins.ts
393
420
  var createPluginSlot = (id2) => {
394
421
  if (!id2.trim()) fail("Plugin slot id is required.", "INVALID_INPUT");
395
- return { id: id2 };
422
+ return { id: id2.trim() };
396
423
  };
397
424
 
398
- // src/context.ts
425
+ // src/kernel/adapter-contract.ts
426
+ var ASSURANCE_LEVELS = ["unverified", "contract-tested", "runtime-attested"];
427
+
428
+ // src/context/index.ts
399
429
  var hashContextSnapshot = ({ providerId, query, references, sourceHash: sourceHash2 }) => hashJson({ providerId, query, references, sourceHash: sourceHash2 });
400
430
  var hashContextSnapshots = (snapshots) => hashJson(snapshots.map(({ providerId, query, references, sourceHash: sourceHash2, snapshotHash }) => ({ providerId, query, references, sourceHash: sourceHash2, snapshotHash })));
401
431
  var record2 = (value, label) => {
@@ -418,17 +448,23 @@ var validateContextSnapshot = (value, index2 = 0) => {
418
448
  uri: requiredString(rawReference["uri"], `context snapshot ${index2}.references[${referenceIndex}].uri`),
419
449
  ...typeof rawReference["title"] === "string" ? { title: rawReference["title"] } : {},
420
450
  ...typeof rawReference["version"] === "string" ? { version: rawReference["version"] } : {},
421
- ...typeof rawReference["contentHash"] === "string" ? { contentHash: rawReference["contentHash"] } : {}
451
+ ...typeof rawReference["contentHash"] === "string" ? { contentHash: rawReference["contentHash"] } : {},
452
+ ...rawReference["relevance"] === void 0 ? {} : typeof rawReference["relevance"] === "number" && rawReference["relevance"] >= 0 && rawReference["relevance"] <= 1 ? { relevance: rawReference["relevance"] } : fail(`context snapshot ${index2}.references[${referenceIndex}].relevance must be between 0 and 1.`, "INVALID_INPUT")
422
453
  };
423
454
  });
424
455
  const scope = rawQuery["scope"] === void 0 ? void 0 : Array.isArray(rawQuery["scope"]) && rawQuery["scope"].every((item) => typeof item === "string") ? rawQuery["scope"] : fail(`context snapshot ${index2}.query.scope must be an array of strings.`, "INVALID_INPUT");
456
+ const assurance = raw["assurance"] === void 0 ? void 0 : ASSURANCE_LEVELS.includes(raw["assurance"]) ? raw["assurance"] : fail(`context snapshot ${index2}.assurance is invalid.`, "INVALID_INPUT");
457
+ const telemetry = raw["telemetry"] === void 0 ? void 0 : record2(raw["telemetry"], `context snapshot ${index2}.telemetry`);
458
+ if (telemetry && telemetry["status"] !== "measured" && telemetry["status"] !== "unknown") fail(`context snapshot ${index2}.telemetry.status is invalid.`, "INVALID_INPUT");
425
459
  const snapshot = {
426
460
  providerId: requiredString(raw["providerId"], `context snapshot ${index2}.providerId`),
427
461
  query: { query: requiredString(rawQuery["query"], `context snapshot ${index2}.query.query`), ...scope ? { scope } : {}, ...typeof rawQuery["sourceRevision"] === "string" ? { sourceRevision: rawQuery["sourceRevision"] } : {} },
428
462
  references,
429
463
  sourceHash: requiredString(raw["sourceHash"], `context snapshot ${index2}.sourceHash`),
430
464
  snapshotHash: requiredString(raw["snapshotHash"], `context snapshot ${index2}.snapshotHash`),
431
- resolvedAt: requiredString(raw["resolvedAt"], `context snapshot ${index2}.resolvedAt`)
465
+ resolvedAt: requiredString(raw["resolvedAt"], `context snapshot ${index2}.resolvedAt`),
466
+ ...assurance === void 0 ? {} : { assurance },
467
+ ...telemetry === void 0 ? {} : { telemetry }
432
468
  };
433
469
  if (snapshot.snapshotHash !== hashContextSnapshot(snapshot)) fail(`context snapshot ${index2}.snapshotHash does not match its contents.`, "INVALID_INPUT");
434
470
  return snapshot;
@@ -440,7 +476,7 @@ var readContextSnapshots = (path) => {
440
476
  var validateContextSnapshots = (snapshots) => snapshots.map((snapshot, index2) => validateContextSnapshot(snapshot, index2));
441
477
  createPluginSlot("context.provider");
442
478
 
443
- // src/runs.ts
479
+ // src/execution/runs.ts
444
480
  var now = () => (/* @__PURE__ */ new Date()).toISOString();
445
481
  var newRunId = () => `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
446
482
  var saveRun2 = (stateDir, run) => {
@@ -459,8 +495,7 @@ var saveRun2 = (stateDir, run) => {
459
495
  store.append({ runId: run.runId, sourceRevision: run.sourceRevision, configHash: run.configHash, type: "context.attached", payload: { providerId: snapshot.providerId, sourceHash: snapshot.sourceHash, snapshotHash: snapshot.snapshotHash, query: snapshot.query } });
460
496
  }
461
497
  };
462
- var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized, contextSnapshots = [], planner = "human" }) => {
463
- const contractHash = hashJson(loaded.config.contract);
498
+ var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized, contextSnapshots = [] }) => {
464
499
  const run = {
465
500
  type: "agentskit-harness-run",
466
501
  schemaVersion: 1,
@@ -468,17 +503,18 @@ var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized,
468
503
  project: loaded.config.project,
469
504
  state: "PLANNED",
470
505
  configHash: loaded.configHash,
471
- contractHash,
506
+ contractHash: hashJson(loaded.config.contract),
472
507
  sourceRevision: baseline.revision,
473
508
  sourceStatusHash: baseline.statusHash,
474
509
  baseline,
475
- ...planner === "human" ? { contractApproval: { actor: "human", at: now(), contractHash } } : { contractPreparation: { actor: "ci", at: now(), contractHash } },
476
- checks: loaded.config.checks.map(({ id: id2, category }) => ({ id: id2, category, status: "pending" })),
510
+ autonomy: loaded.config.autonomy,
511
+ contractApproval: { actor: "human", at: now(), contractHash: hashJson(loaded.config.contract) },
512
+ checks: loaded.config.checks.map(({ id: id2, category: category2 }) => ({ id: id2, category: category2, status: "pending" })),
477
513
  contextSnapshots,
478
514
  ...contextSnapshots.length ? { contextHash: hashContextSnapshots(contextSnapshots) } : {},
479
515
  ...loaded.config.benchmark ? { benchmark: loaded.config.benchmark } : {},
480
516
  outcomes: loaded.config.contract.outcomes.map(({ id: id2, statement, checks }) => ({ id: id2, statement, checks, status: "pending" })),
481
- transitions: [{ from: null, to: "PLANNED", at: now(), actor: planner }],
517
+ transitions: [{ from: null, to: "PLANNED", at: now(), actor: "human" }],
482
518
  evidenceReferences: [],
483
519
  ...supersedes ? { supersedes } : {},
484
520
  ...dirtyBaselineAuthorized ? { dirtyBaselineAuthorized: true } : {}
@@ -507,8 +543,8 @@ var validateEvidence = (root, check, evidence, outcomeIds) => {
507
543
  if (evidence.capability !== "real-browser") failures.push("UI evidence must declare capability real-browser");
508
544
  if (!Array.isArray(evidence.artifacts) || evidence.artifacts.length === 0) failures.push("UI evidence requires screenshot artifacts");
509
545
  }
510
- const artifacts = Array.isArray(evidence.artifacts) ? evidence.artifacts : [];
511
- for (const artifactValue of artifacts) {
546
+ const artifacts2 = Array.isArray(evidence.artifacts) ? evidence.artifacts : [];
547
+ for (const artifactValue of artifacts2) {
512
548
  if (!isRecord3(artifactValue) || typeof artifactValue["path"] !== "string" || typeof artifactValue["sha256"] !== "string") {
513
549
  failures.push("artifact requires string path and sha256");
514
550
  continue;
@@ -531,25 +567,100 @@ var git = async (root, args) => {
531
567
  };
532
568
  var sourceSnapshot = async (root, stateDir) => {
533
569
  const revision = await git(root, ["rev-parse", "HEAD"]);
570
+ if (!revision) fail("Current-source evidence requires a Git repository with a committed HEAD.", "GIT_REQUIRED");
534
571
  const stateRelative = relative(root, stateDir).replaceAll("\\", "/");
535
572
  const pathspec = ["--", "."];
536
573
  if (stateRelative && stateRelative !== ".." && !stateRelative.startsWith("../")) pathspec.push(`:(exclude)${stateRelative}`);
537
- const status = await git(root, ["status", "--porcelain=v1", "--untracked-files=all", ...pathspec]);
574
+ const status2 = await git(root, ["status", "--porcelain=v1", "--untracked-files=all", ...pathspec]);
538
575
  const diff = await git(root, ["diff", "--no-ext-diff", "--binary", "HEAD", ...pathspec]);
539
576
  const untrackedPaths = (await git(root, ["ls-files", "--others", "--exclude-standard", "-z"])).split("\0").filter(Boolean).filter((path) => !stateRelative || path !== stateRelative && !path.startsWith(`${stateRelative}/`));
540
- const untracked = untrackedPaths.flatMap((path) => {
541
- const absolute = resolve(root, path);
542
- try {
543
- return lstatSync(absolute).isFile() ? [{ path, hash: sha256(readFileSync(absolute)) }] : [];
544
- } catch {
545
- return [];
577
+ const untracked = untrackedPaths.map((path) => ({ path, hash: sha256(readFileSync(resolve(root, path))) }));
578
+ const fingerprint = { revision, status: status2, diff, untracked };
579
+ return { revision, status: status2, statusHash: hashJson(fingerprint) };
580
+ };
581
+ var thresholds = (value = {}) => {
582
+ const result = { warningPercent: value.warningPercent ?? 75, criticalPercent: value.criticalPercent ?? 90 };
583
+ if (![result.warningPercent, result.criticalPercent].every((item) => Number.isFinite(item) && item >= 0 && item <= 100) || result.warningPercent > result.criticalPercent) fail("Machine thresholds must be between 0 and 100 and warning must not exceed critical.", "INVALID_INPUT");
584
+ return result;
585
+ };
586
+ var linuxSwap = () => {
587
+ if (process.platform !== "linux" || !existsSync("/proc/meminfo")) return void 0;
588
+ const values = Object.fromEntries(readFileSync("/proc/meminfo", "utf8").split(/\r?\n/).flatMap((line) => {
589
+ const match = line.match(/^(SwapTotal|SwapFree):\s+(\d+)\s+kB$/);
590
+ return match ? [[match[1], Number(match[2])]] : [];
591
+ }));
592
+ if (!values["SwapTotal"]) return void 0;
593
+ return Number(((1 - (values["SwapFree"] ?? 0) / values["SwapTotal"]) * 100).toFixed(2));
594
+ };
595
+ var sampleMachine = () => {
596
+ const cpus$1 = Math.max(1, cpus().length);
597
+ const load1 = Math.max(0, loadavg()[0] ?? 0);
598
+ const memory = Math.max(0, Math.min(100, (1 - freemem() / Math.max(1, totalmem())) * 100));
599
+ const swapUsedPercent = linuxSwap();
600
+ return {
601
+ at: (/* @__PURE__ */ new Date()).toISOString(),
602
+ cpus: cpus$1,
603
+ load1: Number(load1.toFixed(4)),
604
+ load1PerCpuPercent: Number(Math.min(100, load1 / cpus$1 * 100).toFixed(2)),
605
+ memoryUsedPercent: Number(memory.toFixed(2)),
606
+ rssBytes: process.memoryUsage().rss,
607
+ ...swapUsedPercent === void 0 ? {} : { swapUsedPercent }
608
+ };
609
+ };
610
+ var summarizeMachine = (samples, sampleIntervalMs = 5e3, limits = {}) => {
611
+ const limit = thresholds(limits);
612
+ const load = samples.map((sample) => sample.load1PerCpuPercent);
613
+ const memory = samples.map((sample) => sample.memoryUsedPercent);
614
+ const rss = samples.map((sample) => sample.rssBytes);
615
+ return {
616
+ sampleIntervalMs,
617
+ samples,
618
+ peakLoad1PerCpuPercent: Number(Math.max(...load, 0).toFixed(2)),
619
+ peakMemoryUsedPercent: Number(Math.max(...memory, 0).toFixed(2)),
620
+ peakRssBytes: Math.max(...rss, 0),
621
+ pressureEvents: samples.filter((sample) => sample.load1PerCpuPercent >= limit.criticalPercent || sample.memoryUsedPercent >= limit.criticalPercent || (sample.swapUsedPercent ?? 0) >= limit.criticalPercent || sample.memoryPressure === "critical").length,
622
+ throttleEvents: 0,
623
+ minimumEffectiveConcurrency: 0
624
+ };
625
+ };
626
+ var adaptiveConcurrency = (configured, sample, limits = {}) => {
627
+ if (!Number.isInteger(configured) || configured < 1) throw new Error("configured concurrency must be a positive integer.");
628
+ const limit = thresholds(limits);
629
+ const critical = sample.load1PerCpuPercent >= limit.criticalPercent || sample.memoryUsedPercent >= limit.criticalPercent || (sample.swapUsedPercent ?? 0) >= limit.criticalPercent || sample.memoryPressure === "critical";
630
+ const warning = sample.load1PerCpuPercent >= limit.warningPercent || sample.memoryUsedPercent >= limit.warningPercent || (sample.swapUsedPercent ?? 0) >= limit.warningPercent || sample.memoryPressure === "warning";
631
+ if (critical) return 1;
632
+ if (warning) return Math.min(configured, 2);
633
+ return configured;
634
+ };
635
+ var createMachineMonitor = (sampleIntervalMs = 5e3, options2 = {}) => {
636
+ const sampler = options2.sample ?? sampleMachine;
637
+ const limits = thresholds(options2.thresholds);
638
+ const samples = [sampler()];
639
+ let throttleEvents = 0;
640
+ const effectiveConcurrency = [];
641
+ const record3 = () => {
642
+ const sample = sampler();
643
+ samples.push(sample);
644
+ return sample;
645
+ };
646
+ const timer = setInterval(record3, sampleIntervalMs);
647
+ timer.unref();
648
+ return {
649
+ sample: record3,
650
+ observeConcurrency: (value) => effectiveConcurrency.push(value),
651
+ markThrottle: () => {
652
+ throttleEvents += 1;
653
+ },
654
+ stop: () => {
655
+ clearInterval(timer);
656
+ record3();
657
+ const summary = summarizeMachine(samples, sampleIntervalMs, limits);
658
+ return { ...summary, throttleEvents, minimumEffectiveConcurrency: effectiveConcurrency.length ? Math.min(...effectiveConcurrency) : 0 };
546
659
  }
547
- });
548
- const fingerprint = { revision, status, diff, untracked };
549
- return { revision: revision || `content:${hashJson(fingerprint)}`, status, statusHash: hashJson(fingerprint) };
660
+ };
550
661
  };
551
662
 
552
- // src/verification.ts
663
+ // src/execution/verification.ts
553
664
  var now2 = () => (/* @__PURE__ */ new Date()).toISOString();
554
665
  var requireRun = (run) => run ?? fail("No verification run exists.", "NO_RUN");
555
666
  var verificationProjection = (run) => ({ checks: run.checks, outcomes: run.outcomes, metrics: run.metrics });
@@ -575,6 +686,12 @@ var runCommand = (check, cwd) => new Promise((resolveResult) => {
575
686
  resolveResult({ exitCode: exitCode ?? 1, timedOut, stdout, stderr, durationMs: Date.now() - started });
576
687
  });
577
688
  });
689
+ var executeCheck = async (check, cwd, checkDir, outcomes) => {
690
+ const result = await runCommand(check, cwd);
691
+ const evidence = parseStructuredEvidence(result.stdout);
692
+ const failures = result.exitCode === 0 && !result.timedOut && evidence ? validateEvidence(cwd, check, evidence, outcomes) : [result.timedOut ? "check timed out" : result.exitCode !== 0 ? `exit code ${result.exitCode}` : "missing final structured evidence"];
693
+ return { check: { id: check.id, category: check.category, status: failures.length ? "failed" : "passed", exitCode: result.exitCode, durationMs: result.durationMs, ...evidence ? { evidence } : {}, ...failures.length ? { failures } : {} }, stdout: result.stdout, stderr: result.stderr, durationMs: result.durationMs };
694
+ };
578
695
  var currentBinding = async (loaded) => ({ source: await sourceSnapshot(loaded.root, loaded.stateDir), configHash: loaded.configHash });
579
696
  var staleRun = (loaded, run, reason) => {
580
697
  const stale = transition(run, "STALE", reason);
@@ -587,11 +704,8 @@ var isFresh = async (loaded, run) => {
587
704
  return current.configHash === run.configHash && current.source.revision === run.sourceRevision && current.source.statusHash === run.sourceStatusHash;
588
705
  };
589
706
  var planRun = async ({ configPath, decision, actor = "human", allowDirty = false, contextSnapshots = [] }) => {
590
- const automatedPreparation = actor === "ci" && decision === "prepared";
591
- if (!automatedPreparation) {
592
- assertHuman(actor);
593
- if (!approvedDecision(decision)) fail("Contract was not approved.", "CLARIFYING");
594
- }
707
+ assertHuman(actor);
708
+ if (!approvedDecision(decision)) fail("Contract was not approved.", "CLARIFYING");
595
709
  const loaded = loadConfig(configPath);
596
710
  if (loaded.config.contract.ambiguities.length) fail(`Unresolved ambiguities remain: ${loaded.config.contract.ambiguities.join(" | ")}`, "CLARIFYING");
597
711
  const validatedContextSnapshots = validateContextSnapshots(contextSnapshots);
@@ -603,7 +717,7 @@ ${meaningful.join("\n")}
603
717
  Use --allow-dirty only with explicit human authorization.`, "WORKTREE_DIRTY");
604
718
  const previous = loadLatestRun(loaded.stateDir);
605
719
  if (previous && !["STALE", "SUPERSEDED"].includes(previous.state)) fail(`An active run already exists: ${previous.runId} (${previous.state}).`, "ACTIVE_RUN");
606
- return createRun({ loaded, baseline, supersedes: previous?.runId, dirtyBaselineAuthorized: allowDirty, contextSnapshots: validatedContextSnapshots, planner: automatedPreparation ? "ci" : "human" });
720
+ return createRun({ loaded, baseline, supersedes: previous?.runId, dirtyBaselineAuthorized: allowDirty, contextSnapshots: validatedContextSnapshots });
607
721
  };
608
722
  var startRun = (loaded) => {
609
723
  const run = requireRun(loadLatestRun(loaded.stateDir));
@@ -623,42 +737,81 @@ var cancelRun = async ({ configPath, runId, reason = "Run cancelled by a human."
623
737
  };
624
738
  var verifyRun = async ({ configPath }) => {
625
739
  const loaded = loadConfig(configPath);
740
+ const machineMonitor = createMachineMonitor();
626
741
  const run = requireRun(loadLatestRun(loaded.stateDir));
627
742
  if (!["IMPLEMENTING", "VERIFYING"].includes(run.state)) {
628
743
  if (["AWAITING_HUMAN_APPROVAL", "AWAITING_AUTHORIZATION", "COMPLETE"].includes(run.state) && !await isFresh(loaded, run)) staleRun(loaded, run, "Run is stale because source or contract changed.");
629
744
  fail(`Cannot verify from ${run.state}.`, "INVALID_STATE");
630
745
  }
631
746
  if (run.configHash !== loaded.configHash) staleRun(loaded, run, "Run is stale because the verification contract changed.");
632
- const binding = await currentBinding(loaded);
633
- let current = { ...transition(run, "VERIFYING", "Verification started.", "agent"), sourceRevision: binding.source.revision, sourceStatusHash: binding.source.statusHash };
747
+ const binding2 = await currentBinding(loaded);
748
+ let current = { ...transition(run, "VERIFYING", "Verification started.", "agent"), sourceRevision: binding2.source.revision, sourceStatusHash: binding2.source.statusHash };
634
749
  saveRun2(loaded.stateDir, current);
635
750
  const checkDir = join(loaded.stateDir, "runs", current.runId, "checks");
636
751
  mkdirSync(checkDir, { recursive: true });
637
752
  const outcomesByCheck = new Map(loaded.config.checks.map((check) => [check.id, loaded.config.contract.outcomes.filter((outcome) => outcome.checks.includes(check.id)).map((outcome) => outcome.id)]));
638
753
  let totalDurationMs = 0;
639
- for (const check of loaded.config.checks) {
640
- const result = await runCommand(check, loaded.root);
641
- totalDurationMs += result.durationMs;
642
- const stdoutPath = join(checkDir, `${check.id}.stdout`);
643
- const stderrPath = join(checkDir, `${check.id}.stderr`);
644
- writeFileSync(stdoutPath, result.stdout, "utf8");
645
- writeFileSync(stderrPath, result.stderr, "utf8");
646
- const evidence = parseStructuredEvidence(result.stdout);
647
- const failures = result.exitCode === 0 && !result.timedOut && evidence ? validateEvidence(loaded.root, check, evidence, outcomesByCheck.get(check.id) ?? []) : [result.timedOut ? "check timed out" : result.exitCode !== 0 ? `exit code ${result.exitCode}` : "missing final structured evidence"];
648
- const nextCheck = { id: check.id, category: check.category, status: failures.length ? "failed" : "passed", exitCode: result.exitCode, durationMs: result.durationMs, ...evidence ? { evidence } : {}, ...failures.length ? { failures } : {} };
649
- current = { ...current, evidenceReferences: [...current.evidenceReferences, { checkId: check.id, stdout: relative(loaded.stateDir, stdoutPath), stderr: relative(loaded.stateDir, stderrPath) }], checks: current.checks.map((item) => item.id === check.id ? nextCheck : item) };
650
- saveRun2(loaded.stateDir, current);
754
+ let activeChecks = 0;
755
+ let observedPeakConcurrency = 0;
756
+ const verificationStarted = Date.now();
757
+ const maxConcurrency = loaded.config.verification?.maxConcurrency ?? 1;
758
+ const requiredChecks = loaded.config.checks.filter((check) => check.required);
759
+ for (const check of loaded.config.checks.filter((item) => !item.required)) {
760
+ const nextCheck = { id: check.id, category: check.category, status: "not-applicable", failures: [check.reason ?? "Not required by the selected profile."] };
761
+ current = { ...current, checks: current.checks.map((item) => item.id === check.id ? nextCheck : item) };
762
+ }
763
+ const remaining = new Set(requiredChecks.map((check) => check.id));
764
+ const completed = new Set(loaded.config.checks.filter((check) => !check.required).map((check) => check.id));
765
+ while (remaining.size) {
766
+ const ready = requiredChecks.filter((check) => remaining.has(check.id) && (check.dependsOn ?? []).every((dependency) => completed.has(dependency)));
767
+ if (!ready.length) fail("Check dependency graph contains an unknown dependency or cycle.", "INVALID_CONFIG");
768
+ const buildChecks = ready.filter((check) => check.category === "build");
769
+ const queue = buildChecks.length ? buildChecks : ready;
770
+ let offset = 0;
771
+ while (offset < queue.length) {
772
+ const effectiveConcurrency = buildChecks.length ? 1 : adaptiveConcurrency(maxConcurrency, machineMonitor.sample());
773
+ machineMonitor.observeConcurrency(effectiveConcurrency);
774
+ if (effectiveConcurrency < maxConcurrency) machineMonitor.markThrottle();
775
+ const batch = queue.slice(offset, offset + effectiveConcurrency);
776
+ const executed = await Promise.all(batch.map(async (check) => {
777
+ activeChecks += 1;
778
+ observedPeakConcurrency = Math.max(observedPeakConcurrency, activeChecks);
779
+ try {
780
+ return await executeCheck(check, loaded.root, checkDir, outcomesByCheck.get(check.id) ?? []);
781
+ } finally {
782
+ activeChecks -= 1;
783
+ }
784
+ }));
785
+ for (const item of executed) {
786
+ totalDurationMs += item.durationMs;
787
+ const stdoutPath = join(checkDir, `${item.check.id}.stdout`);
788
+ const stderrPath = join(checkDir, `${item.check.id}.stderr`);
789
+ writeFileSync(stdoutPath, item.stdout, "utf8");
790
+ writeFileSync(stderrPath, item.stderr, "utf8");
791
+ current = { ...current, evidenceReferences: [...current.evidenceReferences, { checkId: item.check.id, stdout: relative(loaded.stateDir, stdoutPath), stderr: relative(loaded.stateDir, stderrPath) }], checks: current.checks.map((check) => check.id === item.check.id ? item.check : check) };
792
+ }
793
+ saveRun2(loaded.stateDir, current);
794
+ for (const check of batch) {
795
+ remaining.delete(check.id);
796
+ completed.add(check.id);
797
+ }
798
+ offset += batch.length;
799
+ }
651
800
  }
652
801
  const statuses = new Map(current.checks.map((check) => [check.id, check.status]));
653
802
  const budgetExceeded = loaded.config.budget?.maxDurationMs !== void 0 && totalDurationMs > loaded.config.budget.maxDurationMs;
654
- const allPassed = loaded.config.checks.every((check) => statuses.get(check.id) === "passed") && !budgetExceeded;
655
- current = { ...current, outcomes: current.outcomes.map((outcome) => ({ ...outcome, status: outcome.checks.every((id2) => statuses.get(id2) === "passed") ? "passed" : "failed" })), metrics: { totalDurationMs, budgetExceeded } };
656
- const digest2 = verificationDigest(current);
657
- current = { ...current, verificationDigest: digest2 };
803
+ const allPassed = loaded.config.checks.every((check) => !check.required || statuses.get(check.id) === "passed") && !budgetExceeded;
804
+ current = { ...current, outcomes: current.outcomes.map((outcome) => {
805
+ const required8 = outcome.checks.filter((id2) => loaded.config.checks.find((check) => check.id === id2)?.required);
806
+ return { ...outcome, status: required8.length === 0 ? "not-applicable" : required8.every((id2) => statuses.get(id2) === "passed") ? "passed" : "failed" };
807
+ }), metrics: { totalDurationMs, wallDurationMs: Date.now() - verificationStarted, peakConcurrency: observedPeakConcurrency, budgetExceeded, machine: machineMonitor.stop() } };
808
+ const digest4 = verificationDigest(current);
809
+ current = { ...current, verificationDigest: digest4 };
658
810
  saveRun2(loaded.stateDir, current);
659
- new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest: digest2, checkCount: current.checks.length, outcomeCount: current.outcomes.length, totalDurationMs, budgetExceeded } });
660
- const nextState = allPassed ? "AWAITING_HUMAN_APPROVAL" : "BLOCKED";
661
- current = { ...transition(current, nextState, allPassed ? "All configured checks passed; human approval is required." : budgetExceeded ? "Verification budget was exceeded." : "A configured check failed or lacked structured evidence.", "harness") };
811
+ new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest: digest4, checkCount: current.checks.length, outcomeCount: current.outcomes.length, totalDurationMs, budgetExceeded } });
812
+ const automatic = allPassed && current.autonomy === "yolo" && !loaded.config.tracking.required && loaded.config.contract.ambiguities.length === 0;
813
+ const nextState = allPassed ? automatic ? "COMPLETE" : "AWAITING_HUMAN_APPROVAL" : "BLOCKED";
814
+ current = { ...transition(current, nextState, automatic ? "All applicable checks passed; YOLO policy permits automatic completion." : allPassed ? "All configured checks passed; human approval is required." : budgetExceeded ? "Verification budget was exceeded." : "A configured check failed or lacked structured evidence.", "harness") };
662
815
  saveRun2(loaded.stateDir, current);
663
816
  setLatest(loaded.stateDir, current);
664
817
  return current;
@@ -672,11 +825,6 @@ var assertVerificationAttestation = (loaded, run) => {
672
825
  const event = new FileEventStore(loaded.stateDir).read(run.runId).filter((item) => item.type === "verification.completed").at(-1);
673
826
  if (!event || event.payload.verificationDigest !== expected || event.sourceRevision !== run.sourceRevision || event.configHash !== run.configHash) fail("Verification projection attestation is missing from the event log.", "HARNESS_ERROR");
674
827
  };
675
- var plannerForRetry = (loaded, run) => {
676
- if (run.contractPreparation) return "ci";
677
- if (!run.supersedes) return "human";
678
- return plannerForRetry(loaded, readRun(loaded.stateDir, run.supersedes));
679
- };
680
828
  var recordDecision = (loaded, run, type, payload) => {
681
829
  new FileEventStore(loaded.stateDir).append({ runId: run.runId, sourceRevision: run.sourceRevision, configHash: run.configHash, type, payload: type === "authorization.recorded" ? { ...payload, target: payload.target ?? fail("tracking.target is required when authorizing.", "INVALID_CONFIG") } : payload });
682
830
  };
@@ -696,7 +844,7 @@ var reconcileRun = async ({ configPath, runId }) => {
696
844
  if (eventLog.status !== "verified") fail("Terminal run requires a verified event log.", "HARNESS_ERROR");
697
845
  assertVerificationAttestation(loaded, run);
698
846
  }
699
- if (run.state === "AWAITING_AUTHORIZATION" || run.state === "COMPLETE") {
847
+ if ((run.state === "AWAITING_AUTHORIZATION" || run.state === "COMPLETE") && run.autonomy !== "yolo") {
700
848
  const approval = events2.filter((event) => event.type === "approval.recorded").at(-1) ?? fail("Terminal run is missing its human approval event.", "HARNESS_ERROR");
701
849
  assertDecisionProjection(run, approval.payload, run.state === "COMPLETE" && !loaded.config.tracking.required ? "COMPLETE" : "AWAITING_AUTHORIZATION");
702
850
  if (!run.humanApproval || run.humanApproval.actor !== "human" || run.humanApproval.verificationDigest !== run.verificationDigest || run.humanApproval.sourceRevision !== run.sourceRevision || run.humanApproval.contractHash !== run.contractHash) fail("Human approval projection is inconsistent with its audit event.", "HARNESS_ERROR");
@@ -758,7 +906,7 @@ var retryRun = async ({ configPath }) => {
758
906
  const baseline = await sourceSnapshot(loaded.root, loaded.stateDir);
759
907
  const superseded = transition(previousRun, "SUPERSEDED", "Retry superseded the previous run.", "harness");
760
908
  saveRun2(loaded.stateDir, superseded);
761
- const run = await createRun({ loaded, baseline, supersedes: previousRun.runId, dirtyBaselineAuthorized: previousRun.dirtyBaselineAuthorized, planner: plannerForRetry(loaded, previousRun) });
909
+ const run = await createRun({ loaded, baseline, supersedes: previousRun.runId, dirtyBaselineAuthorized: previousRun.dirtyBaselineAuthorized });
762
910
  const next = transition(run, "IMPLEMENTING", "Retry started after a previous attempt.", "agent");
763
911
  saveRun2(loaded.stateDir, next);
764
912
  setLatest(loaded.stateDir, next);
@@ -778,21 +926,436 @@ var createDocBridgeContextProvider = ({ root, indexPath = ".doc-bridge/index.jso
778
926
  id: "doc-bridge",
779
927
  version: "1.0.0",
780
928
  resolve: async (query) => {
929
+ const started = Date.now();
781
930
  const document = index(root, indexPath);
782
931
  const contentHash = sourceHash(document);
783
932
  const entries = Array.isArray(document.knowledge) ? document.knowledge.filter((value) => typeof value === "object" && value !== null && !Array.isArray(value)).filter((entry) => matches(entry, query)).sort((left, right) => String(left.id ?? "").localeCompare(String(right.id ?? ""))).slice(0, 8) : [];
784
- const references = entries.flatMap((entry) => typeof entry.id === "string" && typeof entry.path === "string" ? [{ id: entry.id, uri: `doc-bridge://${entry.path}`, ...typeof entry.title === "string" ? { title: entry.title } : {}, contentHash }] : []);
785
- return { providerId: "doc-bridge", query, references, sourceHash: contentHash, snapshotHash: hashContextSnapshot({ providerId: "doc-bridge", query, references, sourceHash: contentHash }), resolvedAt: (/* @__PURE__ */ new Date()).toISOString() };
933
+ const references = entries.flatMap((entry) => typeof entry.id === "string" && typeof entry.path === "string" ? [{ id: entry.id, uri: `doc-bridge://${entry.path}`, ...typeof entry.title === "string" ? { title: entry.title } : {}, contentHash: typeof entry.contentHash === "string" ? entry.contentHash : contentHash, relevance: 1 }] : []);
934
+ const telemetry = { status: "measured", durationMs: Date.now() - started, contextReferences: references.length, contextCostTokens: Math.max(1, Math.ceil(JSON.stringify(references).length / 4)) };
935
+ return { providerId: "doc-bridge", query, references, sourceHash: contentHash, snapshotHash: hashContextSnapshot({ providerId: "doc-bridge", query, references, sourceHash: contentHash }), resolvedAt: (/* @__PURE__ */ new Date()).toISOString(), assurance: "contract-tested", telemetry };
786
936
  }
787
937
  });
938
+
939
+ // src/kernel/discovery.ts
940
+ var required = (value, label) => {
941
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
942
+ return value.trim();
943
+ };
944
+ var unique = (values, label) => {
945
+ if (new Set(values).size !== values.length) fail(`${label} must be unique.`, "INVALID_INPUT");
946
+ };
947
+ var validate = (input) => {
948
+ required(input.issueId, "issueId");
949
+ required(input.sourceRevision, "sourceRevision");
950
+ required(input.contractHash, "contractHash");
951
+ if (!Array.isArray(input.ambiguities)) fail("ambiguities must be an array.", "INVALID_INPUT");
952
+ unique(input.ambiguities.map((item) => required(item.id, "ambiguity.id")), "ambiguity ids");
953
+ const assumptions = /* @__PURE__ */ new Map();
954
+ for (const assumption of input.approvedAssumptions ?? []) {
955
+ const id2 = required(assumption.id, "assumption.id");
956
+ if (assumptions.has(id2)) fail("assumption ids must be unique.", "INVALID_INPUT");
957
+ assumptions.set(id2, { id: id2, policyId: required(assumption.policyId, "assumption.policyId"), resolution: required(assumption.resolution, "assumption.resolution") });
958
+ }
959
+ for (const ambiguity of input.ambiguities) {
960
+ required(ambiguity.question, "ambiguity.question");
961
+ if (typeof ambiguity.material !== "boolean") fail("ambiguity.material must be boolean.", "INVALID_INPUT");
962
+ if (!Array.isArray(ambiguity.options) || ambiguity.options.length < 2 || ambiguity.options.length > 4) fail("ambiguity.options must contain 2 to 4 options.", "INVALID_INPUT");
963
+ unique(ambiguity.options.map((option) => required(option.id, "option.id")), "option ids");
964
+ for (const option of ambiguity.options) {
965
+ required(option.summary, "option.summary");
966
+ required(option.impact, "option.impact");
967
+ }
968
+ if (!ambiguity.options.some((option) => option.id === ambiguity.recommendedOptionId)) fail("recommendedOptionId must identify an option.", "INVALID_INPUT");
969
+ if (!ambiguity.material && (!ambiguity.assumptionId || !assumptions.has(ambiguity.assumptionId))) fail("non-material ambiguity requires an approved assumption.", "INVALID_INPUT");
970
+ }
971
+ return { assumptions };
972
+ };
973
+ var digest2 = (result) => hashJson(result);
974
+ var assessDiscovery = (input) => {
975
+ const { assumptions } = validate(input);
976
+ const human = input.ambiguities.filter((ambiguity) => ambiguity.material);
977
+ const decisionLog = input.ambiguities.map((ambiguity) => {
978
+ if (ambiguity.material) return { ambiguityId: ambiguity.id, kind: "human-decision-required", detail: `Recommendation: ${ambiguity.recommendedOptionId}.` };
979
+ const assumption = assumptions.get(ambiguity.assumptionId);
980
+ return { ambiguityId: ambiguity.id, kind: "approved-assumption", detail: assumption.resolution, policyId: assumption.policyId };
981
+ });
982
+ const base = {
983
+ version: 1,
984
+ issueId: input.issueId,
985
+ sourceRevision: input.sourceRevision,
986
+ contractHash: input.contractHash,
987
+ ...input.contextHash ? { contextHash: input.contextHash } : {},
988
+ status: human.length ? "awaiting-decision" : "ready",
989
+ ...human.length ? { packet: {
990
+ issueId: input.issueId,
991
+ contractHash: input.contractHash,
992
+ sourceRevision: input.sourceRevision,
993
+ ...input.contextHash ? { contextHash: input.contextHash } : {},
994
+ decisions: human.map((ambiguity) => ({ id: ambiguity.id, question: ambiguity.question, options: ambiguity.options, recommendedOptionId: ambiguity.recommendedOptionId }))
995
+ } } : {},
996
+ decisionLog
997
+ };
998
+ return { ...base, digest: digest2(base) };
999
+ };
1000
+
1001
+ // src/kernel/wip.ts
1002
+ var WIP_STATES = ["ready", "implementing", "blocked", "awaiting-decision", "awaiting-acceptance", "done", "cancelled"];
1003
+ var terminal = /* @__PURE__ */ new Set(["done", "cancelled"]);
1004
+ var required2 = (value, label) => {
1005
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1006
+ return value.trim();
1007
+ };
1008
+ var assessWip = ({ entries, candidate, maxInFlight = 3 }) => {
1009
+ if (!Array.isArray(entries)) fail("entries must be an array.", "INVALID_INPUT");
1010
+ if (!Number.isInteger(maxInFlight) || maxInFlight < 1) fail("maxInFlight must be a positive integer.", "INVALID_INPUT");
1011
+ const candidateId = required2(candidate.issueId, "candidate.issueId");
1012
+ if (candidate.kind !== "new" && candidate.kind !== "resume") fail("candidate.kind must be new or resume.", "INVALID_INPUT");
1013
+ const ids = /* @__PURE__ */ new Set();
1014
+ const counts = Object.fromEntries(WIP_STATES.map((state) => [state, 0]));
1015
+ for (const entry of entries) {
1016
+ const id2 = required2(entry.issueId, "entry.issueId");
1017
+ if (ids.has(id2)) fail("entry issueIds must be unique.", "INVALID_INPUT");
1018
+ ids.add(id2);
1019
+ if (!WIP_STATES.includes(entry.state)) fail(`Unknown WIP state: ${entry.state}.`, "INVALID_INPUT");
1020
+ counts[entry.state] += 1;
1021
+ }
1022
+ const inFlight = entries.filter((entry) => !terminal.has(entry.state));
1023
+ const existing = entries.find((entry) => entry.issueId === candidateId);
1024
+ if (candidate.kind === "resume") {
1025
+ if (!existing || terminal.has(existing.state)) return { decision: "hold", inFlight, counts, reason: "A resume requires an existing non-terminal issue." };
1026
+ return { decision: "admit", inFlight, counts, reason: "A resume keeps its existing WIP reservation and takes priority over new work." };
1027
+ }
1028
+ if (existing) return { decision: "hold", inFlight, counts, reason: "A new admission cannot reuse an existing issue id." };
1029
+ if (inFlight.length >= maxInFlight) return { decision: "hold", inFlight, counts, reason: `WIP limit ${maxInFlight} reached; blocked and awaiting-human work still count.` };
1030
+ return { decision: "admit", inFlight, counts, reason: `WIP slot available (${inFlight.length}/${maxInFlight}).` };
1031
+ };
1032
+
1033
+ // src/kernel/experiment.ts
1034
+ var required3 = (value, label) => {
1035
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1036
+ return value.trim();
1037
+ };
1038
+ var comparable = (candidate, baseline) => {
1039
+ for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) {
1040
+ if (candidate[key] !== baseline[key]) fail(`Candidates must share ${key}.`, "INVALID_INPUT");
1041
+ }
1042
+ };
1043
+ var selectRuntime = (candidates) => {
1044
+ if (!Array.isArray(candidates) || candidates.length < 2) fail("At least two runtime candidates are required.", "INVALID_INPUT");
1045
+ const names = /* @__PURE__ */ new Set();
1046
+ for (const candidate of candidates) {
1047
+ const runtime = required3(candidate.runtime, "candidate.runtime");
1048
+ if (names.has(runtime)) fail("candidate.runtime values must be unique.", "INVALID_INPUT");
1049
+ names.add(runtime);
1050
+ for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) required3(candidate[key], `candidate.${key}`);
1051
+ for (const key of ["humanMinutes", "durationMs", "cost"]) if (!Number.isFinite(candidate[key]) || candidate[key] < 0) fail(`candidate.${key} must be a non-negative number.`, "INVALID_INPUT");
1052
+ comparable(candidate, candidates[0]);
1053
+ }
1054
+ const eligible = candidates.filter((candidate) => candidate.hardGatesPassed);
1055
+ if (!eligible.length) return { decision: "blocked", eligible, reason: "No runtime passed every hard gate." };
1056
+ const selected = [...eligible].sort((left, right) => left.humanMinutes - right.humanMinutes || left.durationMs - right.durationMs || left.cost - right.cost || (left.runtime === "orca" ? -1 : right.runtime === "orca" ? 1 : left.runtime.localeCompare(right.runtime)))[0];
1057
+ return { decision: "selected", selected, eligible, reason: "Selected by human minutes, duration, cost, then Orca tie-break." };
1058
+ };
1059
+
1060
+ // src/delivery/index.ts
1061
+ var required4 = (value, label) => {
1062
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1063
+ return value.trim();
1064
+ };
1065
+ var criteriaFor = (criteria, gate) => {
1066
+ if (!Array.isArray(criteria)) fail("criteria must be an array.", "INVALID_INPUT");
1067
+ const ids = /* @__PURE__ */ new Set();
1068
+ for (const criterion of criteria) {
1069
+ const id2 = required4(criterion.id, "criterion.id");
1070
+ if (ids.has(id2)) fail("criterion ids must be unique.", "INVALID_INPUT");
1071
+ ids.add(id2);
1072
+ if (!["G2", "G3", "G4", "G5"].includes(criterion.gate)) fail("criterion.gate is invalid.", "INVALID_INPUT");
1073
+ if (!["passed", "failed", "pending", "not-applicable"].includes(criterion.status)) fail("criterion.status is invalid.", "INVALID_INPUT");
1074
+ if (criterion.status === "not-applicable" && !criterion.reason?.trim()) fail("not-applicable criteria require a reason.", "INVALID_INPUT");
1075
+ }
1076
+ return criteria.filter((criterion) => criterion.gate === gate);
1077
+ };
1078
+ var binding = (value) => ({ candidateRevision: required4(value.candidateRevision, "binding.candidateRevision"), contractHash: required4(value.contractHash, "binding.contractHash"), configHash: required4(value.configHash, "binding.configHash") });
1079
+ var assessed = (gate, decision, reasons, current) => {
1080
+ const base = { gate, decision, reasons, binding: binding(current) };
1081
+ return { ...base, digest: hashJson(base) };
1082
+ };
1083
+ var assessPreflight = ({ criteria, repairAttempts = 0, implementerId, reviewerId, reviewKind, reviewApproved, binding: current }) => {
1084
+ required4(implementerId, "implementerId");
1085
+ if (!Number.isInteger(repairAttempts) || repairAttempts < 0) fail("repairAttempts must be a non-negative integer.", "INVALID_INPUT");
1086
+ const g2 = criteriaFor(criteria, "G2");
1087
+ const reasons = [
1088
+ ...g2.length ? [] : ["No G2 criteria are defined."],
1089
+ ...g2.filter((criterion) => criterion.status === "failed" || criterion.status === "pending").map((criterion) => `${criterion.id} is ${criterion.status}.`),
1090
+ ...reviewApproved && reviewerId && reviewerId !== implementerId && reviewKind === "adversarial" ? [] : ["An approved adversarial review by a reviewer different from the implementer is required."],
1091
+ ...repairAttempts <= 2 ? [] : ["The two-repair limit was exceeded; preserve diagnostics and return blocked."]
1092
+ ];
1093
+ return assessed("G2", reasons.length ? "blocked" : "approved", reasons, current);
1094
+ };
1095
+ var composePullRequest = ({ draft, g2, remote }) => {
1096
+ for (const [label, value] of Object.entries({ issueId: draft.issueId, candidateRevision: draft.candidateRevision, contractHash: draft.contractHash, configHash: draft.configHash, g2Digest: draft.g2Digest, risk: draft.risk, rollback: draft.rollback })) required4(value, `draft.${label}`);
1097
+ if (g2.gate !== "G2" || g2.decision !== "approved" || g2.digest !== draft.g2Digest || g2.binding.candidateRevision !== draft.candidateRevision || g2.binding.contractHash !== draft.contractHash || g2.binding.configHash !== draft.configHash) return { decision: "blocked", reason: "A current approved G2 assessment is required before a PR can be created.", idempotencyKey: hashJson(draft) };
1098
+ const idempotencyKey = hashJson({ issueId: draft.issueId, contractHash: draft.contractHash, action: "pull-request", revision: draft.candidateRevision });
1099
+ if (remote?.state === "uncertain") return { decision: "blocked", reason: "Remote PR state is uncertain; reconcile before retrying.", idempotencyKey };
1100
+ if (remote?.state === "confirmed") {
1101
+ if (remote.candidateRevision !== draft.candidateRevision || !remote.url) return { decision: "blocked", reason: "Confirmed remote PR does not match the candidate revision.", idempotencyKey };
1102
+ return { decision: "reuse", reason: "The idempotent remote PR already exists for this candidate revision.", idempotencyKey };
1103
+ }
1104
+ const body2 = [`## Contract`, `- Issue: ${draft.issueId}`, `- Candidate: ${draft.candidateRevision}`, `- Contract: ${draft.contractHash}`, `- Configuration: ${draft.configHash}`, "", "## G2 evidence", ...draft.evidence.map((item) => `- ${item}`), "", "## Documentation", ...draft.documentation.map((item) => `- ${item}`), "", "## Risk and rollback", `- Risk: ${draft.risk}`, `- Rollback: ${draft.rollback}`, "", "## Later gates", ...draft.pendingCriteria.length ? draft.pendingCriteria.map((item) => `- Pending: ${item}`) : ["- None."]].join("\n");
1105
+ return { decision: "create", body: body2, reason: "G2 is current and the remote PR is absent.", idempotencyKey };
1106
+ };
1107
+ var assessIntegration = ({ g2, candidateRevision, evidenceRevision, contractHash, configHash, ci }) => {
1108
+ required4(candidateRevision, "candidateRevision");
1109
+ required4(evidenceRevision, "evidenceRevision");
1110
+ if (!["passed", "failed", "pending", "not-applicable"].includes(ci)) fail("ci is invalid.", "INVALID_INPUT");
1111
+ const reasons = [
1112
+ ...g2.gate === "G2" && g2.decision === "approved" ? [] : ["G2 is not approved."],
1113
+ ...g2.binding.candidateRevision === candidateRevision && g2.binding.contractHash === contractHash && g2.binding.configHash === configHash ? [] : ["G2 is not bound to the current candidate, contract, and configuration."],
1114
+ ...candidateRevision === evidenceRevision ? [] : ["Candidate revision changed; G3 evidence must be revalidated."],
1115
+ ...ci === "passed" ? [] : [`Integration CI is ${ci}.`]
1116
+ ];
1117
+ return assessed("G3", reasons.length ? "blocked" : "approved", reasons, { candidateRevision, contractHash, configHash });
1118
+ };
1119
+ var assessWorktreeCleanup = ({ branch, candidateRevision, contractHash, configHash, remoteBranchRevision, remotePr, integration }) => {
1120
+ required4(branch, "branch");
1121
+ required4(candidateRevision, "candidateRevision");
1122
+ if (remotePr === "uncertain") return { decision: "preserve", reason: "Remote PR state is uncertain; preserve the worktree for reconciliation." };
1123
+ if (remotePr !== "confirmed") return { decision: "preserve", reason: "No confirmed remote PR exists; preserve the worktree." };
1124
+ if (remoteBranchRevision !== candidateRevision) return { decision: "preserve", reason: "Remote branch SHA does not match the candidate revision." };
1125
+ if (integration.gate !== "G3" || integration.decision !== "approved") return { decision: "preserve", reason: "G3 is not approved." };
1126
+ if (integration.binding.candidateRevision !== candidateRevision || integration.binding.contractHash !== contractHash || integration.binding.configHash !== configHash) return { decision: "preserve", reason: "G3 is not bound to the current candidate, contract, and configuration." };
1127
+ return { decision: "clean", reason: "Remote branch, PR, and G3 evidence are confirmed for the candidate revision." };
1128
+ };
1129
+ var profileReasons = (profile) => Object.entries(profile).flatMap(([key, value]) => Array.isArray(value) ? value.length ? [] : [`RepositoryProfile.${key} is required.`] : typeof value === "string" && value.trim() ? [] : [`RepositoryProfile.${key} is required.`]);
1130
+ var assessProduction = ({ profile, integration, artifact, isolated, acceptanceArtifact, lowRisk = true, observationMinutes, technicalPassed, evidence, containmentPreauthorized, containmentAction, linkedDefect }) => {
1131
+ required4(artifact, "artifact");
1132
+ if (!Number.isFinite(observationMinutes) || observationMinutes < 0) fail("observationMinutes must be non-negative.", "INVALID_INPUT");
1133
+ const evidenceReasons = [required4(evidence.tenant, "evidence.tenant"), required4(evidence.realFlow, "evidence.realFlow"), ...Array.isArray(evidence.logs) && evidence.logs.length ? [] : ["Production evidence requires logs."], ...Array.isArray(evidence.metrics) && evidence.metrics.length ? [] : ["Production evidence requires metrics."]].filter((item) => item.startsWith("Production evidence"));
1134
+ const reasons = [
1135
+ ...profileReasons(profile),
1136
+ ...integration.gate === "G3" && integration.decision === "approved" ? [] : ["G3 is not approved."],
1137
+ ...evidenceReasons,
1138
+ ...isolated || acceptanceArtifact === artifact ? [] : ["Exposure requires isolation or acceptance linked to this artifact version."],
1139
+ ...technicalPassed ? [] : [containmentPreauthorized && containmentAction?.trim() && linkedDefect?.trim() ? "Technical validation failed; pre-authorized containment and linked defect are recorded." : containmentPreauthorized ? "Technical validation failed; containment action and linked defect are required." : "Technical validation failed."],
1140
+ ...lowRisk && observationMinutes < 15 ? ["Low-risk production validation requires a 15-minute observation window."] : []
1141
+ ];
1142
+ return assessed("G4", reasons.length ? "blocked" : "approved", reasons, integration.binding);
1143
+ };
1144
+ var assessAcceptance = ({ production, acceptanceRequired, accepted, notApplicableReason, materialChange }) => {
1145
+ const reasons = [
1146
+ ...production.gate === "G4" && production.decision === "approved" ? [] : ["G4 is not approved."],
1147
+ ...materialChange ? ["A material change invalidated acceptance; return to the affected gate."] : []
1148
+ ];
1149
+ if (reasons.length) return assessed("G5", "blocked", reasons, production.binding);
1150
+ if (acceptanceRequired && !accepted) return assessed("G5", "awaiting-acceptance", ["Business or UX acceptance is still required."], production.binding);
1151
+ if (!acceptanceRequired && !notApplicableReason?.trim()) return assessed("G5", "blocked", ["Acceptance marked not applicable requires a contractual reason."], production.binding);
1152
+ return assessed("G5", "approved", acceptanceRequired ? [] : [`Acceptance is not applicable: ${notApplicableReason}.`], production.binding);
1153
+ };
1154
+
1155
+ // src/kernel/pilot.ts
1156
+ var required5 = (value, label) => {
1157
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1158
+ return value.trim();
1159
+ };
1160
+ var assessPilot = (manifest) => {
1161
+ required5(manifest.policyHash, "policyHash");
1162
+ required5(manifest.baselineReference, "baselineReference");
1163
+ if (!Array.isArray(manifest.entries)) fail("entries must be an array.", "INVALID_INPUT");
1164
+ const ids = /* @__PURE__ */ new Set();
1165
+ const reasons = [];
1166
+ const included = [];
1167
+ for (const entry of manifest.entries) {
1168
+ const issueId = required5(entry.issueId, "entry.issueId");
1169
+ if (ids.has(issueId)) fail("entry issueIds must be unique; an issue cannot be substituted in the same pilot.", "INVALID_INPUT");
1170
+ ids.add(issueId);
1171
+ if (!["normal", "incident", "sensitive"].includes(entry.classification)) fail("entry.classification is invalid.", "INVALID_INPUT");
1172
+ if (!["included", "excluded", "aborted"].includes(entry.status)) fail("entry.status is invalid.", "INVALID_INPUT");
1173
+ if (entry.status !== "included" && !entry.reason?.trim()) reasons.push(`${issueId} is ${entry.status} without an auditable reason.`);
1174
+ if (entry.status === "included") {
1175
+ included.push(issueId);
1176
+ if (entry.classification !== "normal") reasons.push(`${issueId} is ${entry.classification}; only normal issues can enter the pilot.`);
1177
+ }
1178
+ }
1179
+ if (included.length !== 10) reasons.push(`Pilot requires exactly 10 included issues; found ${included.length}.`);
1180
+ const base = { decision: reasons.length ? "blocked" : "ready", included, reasons };
1181
+ return { ...base, digest: hashJson({ ...manifest, ...base }) };
1182
+ };
1183
+ var IMPROVEMENT_CYCLE_STEPS = ["adversarial-review", "g2-preflight", "baseline-record", "pilot-execution", "comparison"];
1184
+ var nonEmpty = (value, label) => {
1185
+ if (typeof value !== "string" || !value.trim()) return fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1186
+ return value.trim();
1187
+ };
1188
+ var validateMetrics = (metrics, index2) => {
1189
+ if (metrics === void 0) return void 0;
1190
+ for (const [key, value] of Object.entries(metrics)) {
1191
+ if (value !== void 0 && (typeof value !== "number" || !Number.isFinite(value) || value < 0)) return fail(`iterations[${index2}].metrics.${key} must be a non-negative number.`, "INVALID_INPUT");
1192
+ }
1193
+ return metrics;
1194
+ };
1195
+ var validateIteration = (iteration, index2) => {
1196
+ if (typeof iteration !== "object" || iteration === null || Array.isArray(iteration)) return fail(`iterations[${index2}] must be an object.`, "INVALID_INPUT");
1197
+ if (!Number.isInteger(iteration.iteration) || iteration.iteration < 1) return fail(`iterations[${index2}].iteration must be a positive integer.`, "INVALID_INPUT");
1198
+ if (!Array.isArray(iteration.steps) || iteration.steps.length !== IMPROVEMENT_CYCLE_STEPS.length) return fail(`iterations[${index2}].steps must contain the five cycle steps exactly once, in order.`, "INVALID_INPUT");
1199
+ iteration.steps.forEach((result, stepIndex) => {
1200
+ if (typeof result !== "object" || result === null || Array.isArray(result)) return fail(`iterations[${index2}].steps[${stepIndex}] must be an object.`, "INVALID_INPUT");
1201
+ if (result.step !== IMPROVEMENT_CYCLE_STEPS[stepIndex]) return fail(`iterations[${index2}].steps[${stepIndex}] must be ${IMPROVEMENT_CYCLE_STEPS[stepIndex]}.`, "INVALID_INPUT");
1202
+ if (!["passed", "failed", "blocked", "pending"].includes(result.status)) return fail(`iterations[${index2}].steps[${stepIndex}].status is invalid.`, "INVALID_INPUT");
1203
+ if (result.status !== "passed" && !nonEmpty(result.reason, `iterations[${index2}].steps[${stepIndex}].reason`)) return fail(`iterations[${index2}].steps[${stepIndex}].reason is required when the step does not pass.`, "INVALID_INPUT");
1204
+ });
1205
+ if (iteration.adjustment !== void 0) nonEmpty(iteration.adjustment, `iterations[${index2}].adjustment`);
1206
+ return { ...iteration, metrics: validateMetrics(iteration.metrics, index2) };
1207
+ };
1208
+ var assessImprovementCycle = (input) => {
1209
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return fail("cycle input must be an object.", "INVALID_INPUT");
1210
+ const cycleId = nonEmpty(input.cycleId, "cycleId");
1211
+ if (!Number.isInteger(input.maxIterations) || input.maxIterations < 1) return fail("maxIterations must be a positive integer.", "INVALID_INPUT");
1212
+ if (!Array.isArray(input.iterations) || input.iterations.length < 1) return fail("iterations must be non-empty.", "INVALID_INPUT");
1213
+ if (input.iterations.length > input.maxIterations) return fail("iterations cannot exceed maxIterations.", "INVALID_INPUT");
1214
+ const iterations = input.iterations.map(validateIteration);
1215
+ iterations.forEach((iteration, index2) => {
1216
+ if (iteration.iteration !== index2 + 1) return fail("iterations must be sequential and start at 1.", "INVALID_INPUT");
1217
+ if (index2 > 0 && iterations[index2 - 1]?.steps.every((step) => step.status === "passed")) return fail("a completed cycle cannot have later iterations.", "INVALID_INPUT");
1218
+ if (index2 < iterations.length - 1 && !iteration.adjustment) return fail(`iterations[${index2}].adjustment is required before repeating.`, "INVALID_INPUT");
1219
+ });
1220
+ const matrix = iterations.map((iteration) => {
1221
+ const statuses = Object.fromEntries(iteration.steps.map((step) => [step.step, step.status]));
1222
+ const passedSteps = iteration.steps.filter((step) => step.status === "passed").length;
1223
+ return { iteration: iteration.iteration, passedSteps, totalSteps: IMPROVEMENT_CYCLE_STEPS.length, passRate: Number((passedSteps / IMPROVEMENT_CYCLE_STEPS.length).toFixed(4)), statuses, ...iteration.adjustment ? { adjustment: iteration.adjustment } : {}, ...iteration.metrics ? { metrics: iteration.metrics } : {} };
1224
+ });
1225
+ const latest = iterations[iterations.length - 1];
1226
+ const complete = latest.steps.every((step) => step.status === "passed");
1227
+ const reasons = complete ? ["All five cycle steps passed."] : iterations.length >= input.maxIterations ? ["Maximum cycle iterations reached; human adjustment is required."] : latest.adjustment ? ["A failed or blocked step remains; repeat with the recorded adjustment."] : ["A failed or blocked step remains; an explicit adjustment is required before repeating."];
1228
+ const decision = complete ? "complete" : iterations.length >= input.maxIterations || !latest.adjustment ? "blocked" : "repeat";
1229
+ const result = { type: "agentskit-harness-improvement-cycle", cycleId, decision, ...decision === "repeat" ? { nextIteration: latest.iteration + 1 } : {}, reasons, matrix };
1230
+ const digest4 = createHash("sha256").update(JSON.stringify(result)).digest("hex");
1231
+ return { ...result, digest: digest4 };
1232
+ };
1233
+ var ARTIFACT_SCHEMA_VERSION = 1;
1234
+ var ARTIFACT_TYPES = ["plan", "finding", "decision", "repair", "blocker", "approval", "phase"];
1235
+ var text2 = (value, label) => {
1236
+ if (typeof value !== "string" || !value.trim()) return fail(`${label} is required.`, "INVALID_INPUT");
1237
+ return value.trim();
1238
+ };
1239
+ var digest3 = (value, label) => {
1240
+ const result = text2(value, label);
1241
+ if (!/^[a-f0-9]{64}$/.test(result)) fail(`${label} must be a lowercase SHA-256 digest.`, "INVALID_INPUT");
1242
+ return result;
1243
+ };
1244
+ var artifactId = (value) => {
1245
+ const result = text2(value, "Artifact artifactId");
1246
+ if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(result)) fail("Artifact artifactId is invalid.", "INVALID_INPUT");
1247
+ return result;
1248
+ };
1249
+ var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
1250
+ var artifactBody = (artifact) => ({
1251
+ type: artifact.type,
1252
+ schemaVersion: artifact.schemaVersion,
1253
+ artifactId: artifact.artifactId,
1254
+ artifactType: artifact.artifactType,
1255
+ artifactVersion: artifact.artifactVersion,
1256
+ runId: artifact.runId,
1257
+ issueRef: artifact.issueRef,
1258
+ sourceRevision: artifact.sourceRevision,
1259
+ contractHash: artifact.contractHash,
1260
+ configHash: artifact.configHash,
1261
+ contextHash: artifact.contextHash,
1262
+ phase: artifact.phase,
1263
+ payload: artifact.payload,
1264
+ payloadHash: artifact.payloadHash
1265
+ });
1266
+ var expectedArtifactHash = (artifact) => hashJson(artifactBody(artifact));
1267
+ var validateArtifactEnvelope = (value) => {
1268
+ if (!isRecord4(value)) return fail("Artifact envelope must be an object.", "INVALID_INPUT");
1269
+ if (value["type"] !== "agentskit-harness-artifact" || value["schemaVersion"] !== ARTIFACT_SCHEMA_VERSION) fail("Artifact envelope type or schemaVersion is invalid.", "INVALID_INPUT");
1270
+ if (!ARTIFACT_TYPES.includes(value["artifactType"])) fail("Artifact artifactType is invalid.", "INVALID_INPUT");
1271
+ if (!Number.isInteger(value["artifactVersion"]) || value["artifactVersion"] < 1) fail("Artifact artifactVersion must be a positive integer.", "INVALID_INPUT");
1272
+ const createdAt = text2(value["createdAt"], "Artifact createdAt");
1273
+ if (!Number.isFinite(Date.parse(createdAt))) fail("Artifact createdAt must be a valid timestamp.", "INVALID_INPUT");
1274
+ const payloadHash = digest3(value["payloadHash"], "Artifact payloadHash");
1275
+ if (hashJson(value["payload"]) !== payloadHash) fail("Artifact payloadHash does not match payload.", "INVALID_INPUT");
1276
+ const artifact = {
1277
+ type: "agentskit-harness-artifact",
1278
+ schemaVersion: ARTIFACT_SCHEMA_VERSION,
1279
+ artifactId: artifactId(value["artifactId"]),
1280
+ artifactType: value["artifactType"],
1281
+ artifactVersion: value["artifactVersion"],
1282
+ runId: text2(value["runId"], "Artifact runId"),
1283
+ issueRef: text2(value["issueRef"], "Artifact issueRef"),
1284
+ sourceRevision: text2(value["sourceRevision"], "Artifact sourceRevision"),
1285
+ contractHash: digest3(value["contractHash"], "Artifact contractHash"),
1286
+ configHash: digest3(value["configHash"], "Artifact configHash"),
1287
+ contextHash: digest3(value["contextHash"], "Artifact contextHash"),
1288
+ phase: text2(value["phase"], "Artifact phase"),
1289
+ createdAt,
1290
+ payload: value["payload"],
1291
+ payloadHash
1292
+ };
1293
+ if (digest3(value["artifactHash"], "Artifact artifactHash") !== expectedArtifactHash(artifact)) fail("Artifact artifactHash does not match envelope.", "INVALID_INPUT");
1294
+ return { ...artifact, artifactHash: value["artifactHash"] };
1295
+ };
1296
+ var renderArtifactMarkdown = (artifact) => [
1297
+ `# ${artifact.artifactType} artifact ${artifact.artifactId}`,
1298
+ "",
1299
+ `- Schema: ${artifact.schemaVersion}`,
1300
+ `- Version: ${artifact.artifactVersion}`,
1301
+ `- Run: ${artifact.runId}`,
1302
+ `- Issue: ${artifact.issueRef}`,
1303
+ `- Phase: ${artifact.phase}`,
1304
+ `- Source revision: ${artifact.sourceRevision}`,
1305
+ `- Contract hash: ${artifact.contractHash}`,
1306
+ `- Configuration hash: ${artifact.configHash}`,
1307
+ `- Context hash: ${artifact.contextHash}`,
1308
+ `- Artifact hash: ${artifact.artifactHash}`,
1309
+ "",
1310
+ "## Payload",
1311
+ "",
1312
+ "```json",
1313
+ JSON.stringify(artifact.payload, null, 2),
1314
+ "```",
1315
+ ""
1316
+ ].join("\n");
1317
+ var artifactFilePath = (stateDir, runId, id2) => join(stateDir, "runs", runId, "artifacts", `${id2}.json`);
1318
+ var artifactMarkdownPath = (stateDir, runId, id2) => join(stateDir, "runs", runId, "artifacts", `${id2}.md`);
1319
+ var FileArtifactStore = class {
1320
+ constructor(stateDir) {
1321
+ this.stateDir = stateDir;
1322
+ }
1323
+ stateDir;
1324
+ write(input) {
1325
+ const artifact = validateArtifactEnvelope(input);
1326
+ const path = artifactFilePath(this.stateDir, artifact.runId, artifact.artifactId);
1327
+ mkdirSync(join(this.stateDir, "runs", artifact.runId, "artifacts"), { recursive: true });
1328
+ if (existsSync(path)) {
1329
+ const existing = validateArtifactEnvelope(JSON.parse(readFileSync(path, "utf8")));
1330
+ if (existing.artifactHash !== artifact.artifactHash) fail(`Artifact ${artifact.artifactId} already exists with different content.`, "HARNESS_ERROR");
1331
+ return existing;
1332
+ }
1333
+ writeFileSync(path, `${JSON.stringify(artifact, null, 2)}
1334
+ `, "utf8");
1335
+ writeFileSync(artifactMarkdownPath(this.stateDir, artifact.runId, artifact.artifactId), renderArtifactMarkdown(artifact), "utf8");
1336
+ new FileEventStore(this.stateDir).append({
1337
+ runId: artifact.runId,
1338
+ sourceRevision: artifact.sourceRevision,
1339
+ configHash: artifact.configHash,
1340
+ type: "artifact.recorded",
1341
+ payload: { artifactId: artifact.artifactId, artifactType: artifact.artifactType, artifactVersion: artifact.artifactVersion, artifactHash: artifact.artifactHash, phase: artifact.phase, representation: "json+markdown" }
1342
+ });
1343
+ return artifact;
1344
+ }
1345
+ read(runId, id2) {
1346
+ return validateArtifactEnvelope(JSON.parse(readFileSync(artifactFilePath(this.stateDir, runId, artifactId(id2)), "utf8")));
1347
+ }
1348
+ list(runId) {
1349
+ const directory = join(this.stateDir, "runs", runId, "artifacts");
1350
+ if (!existsSync(directory)) return [];
1351
+ return readdirSync(directory).filter((name) => name.endsWith(".json")).sort().map((name) => validateArtifactEnvelope(JSON.parse(readFileSync(join(directory, name), "utf8"))));
1352
+ }
1353
+ };
1354
+ var readArtifactFile = (path) => validateArtifactEnvelope(JSON.parse(readFileSync(path, "utf8")));
788
1355
  var BENCHMARK_SCHEMA_VERSION = 1;
789
1356
  var percentage = (part, total) => total ? Number((part / total).toFixed(4)) : null;
790
- var DEFAULT_POLICY = { minComparableTasks: 3, maxDurationRegressionRate: 0.2, minCompletedRunsPerTask: 3, minBaselineSamplesPerTask: 3, requireZeroEscapedIncomplete: true };
791
1357
  var improvementRate = (baseline, current) => baseline === void 0 || current === void 0 || baseline === 0 ? null : Number(((baseline - current) / baseline).toFixed(4));
792
- var increaseRate = (baseline, current) => baseline === void 0 || current === void 0 || baseline === 0 ? null : Number(((current - baseline) / baseline).toFixed(4));
793
- var increaseDelta = (baseline, current) => baseline === void 0 || current === null ? null : Number((current - baseline).toFixed(4));
794
- var increaseDirection = (rate2, delta) => delta === null ? improvementDirection(rate2) : delta > 0 ? "improved" : delta < 0 ? "regressed" : "unchanged";
795
- var improvementDirection = (rate2) => rate2 === null ? "unavailable" : rate2 > 0 ? "improved" : rate2 < 0 ? "regressed" : "unchanged";
1358
+ var improvementDirection = (rate) => rate === null ? "unavailable" : rate > 0 ? "improved" : rate < 0 ? "regressed" : "unchanged";
796
1359
  var count = (items, predicate) => items.filter(predicate).length;
797
1360
  var median = (values) => {
798
1361
  if (!values.length) return null;
@@ -807,25 +1370,13 @@ var reviewMinutes = (run) => {
807
1370
  const elapsed = Date.parse(run.humanApproval.at) - Date.parse(reviewStart);
808
1371
  return Number.isFinite(elapsed) && elapsed >= 0 ? Number((elapsed / 6e4).toFixed(2)) : void 0;
809
1372
  };
810
- var confidence = (comparable, completedRuns, policy) => !comparable ? "insufficient" : completedRuns >= policy.minCompletedRunsPerTask ? "reliable" : "directional";
811
- var artifactAcceptanceRate = (run) => {
812
- const rates = run.checks.flatMap((check) => {
813
- const evidence = check.evidence;
814
- if (!evidence) return [];
815
- const direct = typeof evidence["artifactAcceptanceRate"] === "number" ? [evidence["artifactAcceptanceRate"]] : [];
816
- const reports = Array.isArray(evidence["reports"]) ? evidence["reports"].flatMap((report) => typeof report === "object" && report !== null && typeof report["artifactAcceptanceRate"] === "number" ? [report["artifactAcceptanceRate"]] : []) : [];
817
- return [...direct, ...reports].filter((rate2) => Number.isFinite(rate2) && rate2 >= 0 && rate2 <= 1);
818
- });
819
- return rates.length ? Number((rates.reduce((total, rate2) => total + rate2, 0) / rates.length).toFixed(4)) : void 0;
820
- };
821
1373
  var projectRun = (run) => {
822
1374
  const checks = { total: run.checks.length, passed: count(run.checks, (check) => check.status === "passed"), failed: count(run.checks, (check) => check.status === "failed") };
823
1375
  const outcomes = { total: run.outcomes.length, passed: count(run.outcomes, (outcome) => outcome.status === "passed"), failed: count(run.outcomes, (outcome) => outcome.status === "failed") };
824
1376
  const evidence = { total: run.checks.length, attached: count(run.checks, (check) => check.evidence !== void 0) };
825
- const acceptanceRate = artifactAcceptanceRate(run);
826
1377
  const humanReviewMinutes = reviewMinutes(run);
827
1378
  const escapedIncomplete = run.state === "COMPLETE" && checks.failed === 0 && outcomes.failed === 0 && evidence.attached === evidence.total ? 0 : void 0;
828
- return { runId: run.runId, state: run.state, sourceRevision: run.sourceRevision, configHash: run.configHash, contractHash: run.contractHash, ...run.supersedes ? { supersedes: run.supersedes } : {}, ...run.metrics ? { durationMs: run.metrics.totalDurationMs } : {}, checks, outcomes, evidence, ...acceptanceRate === void 0 ? {} : { artifactAcceptanceRate: acceptanceRate }, ...escapedIncomplete === void 0 ? {} : { escapedIncomplete }, humanApproved: run.humanApproval !== void 0, ...humanReviewMinutes === void 0 ? {} : { humanReviewMinutes }, authorized: run.authorization !== void 0, ...run.benchmark ? { benchmark: run.benchmark } : {} };
1379
+ return { runId: run.runId, state: run.state, sourceRevision: run.sourceRevision, configHash: run.configHash, contractHash: run.contractHash, ...run.supersedes ? { supersedes: run.supersedes } : {}, ...run.metrics ? { durationMs: run.metrics.totalDurationMs } : {}, checks, outcomes, evidence, ...escapedIncomplete === void 0 ? {} : { escapedIncomplete }, humanApproved: run.humanApproval !== void 0, ...humanReviewMinutes === void 0 ? {} : { humanReviewMinutes }, authorized: run.authorization !== void 0, ...run.metrics?.machine ? { machine: run.metrics.machine } : {}, ...run.benchmark ? { benchmark: run.benchmark } : {} };
829
1380
  };
830
1381
  var summarize = (runs) => {
831
1382
  const stateCounts = Object.fromEntries(RUN_STATES.map((state) => [state, count(runs, (run) => run.state === state)]));
@@ -836,14 +1387,6 @@ var summarize = (runs) => {
836
1387
  const evidenceTotal = runs.reduce((total, run) => total + run.evidence.total, 0);
837
1388
  const evidenceAttached = runs.reduce((total, run) => total + run.evidence.attached, 0);
838
1389
  const firstAttempts = runs.filter((run) => !run.supersedes);
839
- const superseded = new Set(runs.flatMap((run) => run.supersedes ? [run.supersedes] : []));
840
- const effectiveRuns = runs.filter((run) => !superseded.has(run.runId));
841
- const effectiveChecksTotal = effectiveRuns.reduce((total, run) => total + run.checks.total, 0);
842
- const effectiveChecksPassed = effectiveRuns.reduce((total, run) => total + run.checks.passed, 0);
843
- const effectiveOutcomesTotal = effectiveRuns.reduce((total, run) => total + run.outcomes.total, 0);
844
- const effectiveOutcomesPassed = effectiveRuns.reduce((total, run) => total + run.outcomes.passed, 0);
845
- const effectiveEvidenceTotal = effectiveRuns.reduce((total, run) => total + run.evidence.total, 0);
846
- const effectiveEvidenceAttached = effectiveRuns.reduce((total, run) => total + run.evidence.attached, 0);
847
1390
  const durations = runs.flatMap((run) => run.durationMs === void 0 ? [] : [run.durationMs]);
848
1391
  return {
849
1392
  totalRuns: runs.length,
@@ -854,12 +1397,6 @@ var summarize = (runs) => {
854
1397
  firstAttemptRuns: firstAttempts.length,
855
1398
  humanApprovedRuns: count(runs, (run) => run.humanApproved),
856
1399
  authorizedRuns: count(runs, (run) => run.authorized),
857
- effectiveRunCount: effectiveRuns.length,
858
- effectiveCompleteRuns: count(effectiveRuns, (run) => run.state === "COMPLETE"),
859
- effectiveCompletionRate: percentage(count(effectiveRuns, (run) => run.state === "COMPLETE"), effectiveRuns.length),
860
- effectiveCheckPassRate: percentage(effectiveChecksPassed, effectiveChecksTotal),
861
- effectiveOutcomePassRate: percentage(effectiveOutcomesPassed, effectiveOutcomesTotal),
862
- effectiveEvidenceCoverageRate: percentage(effectiveEvidenceAttached, effectiveEvidenceTotal),
863
1400
  checkPassRate: percentage(checksPassed, checksTotal),
864
1401
  outcomePassRate: percentage(outcomesPassed, outcomesTotal),
865
1402
  evidenceCoverageRate: percentage(evidenceAttached, evidenceTotal),
@@ -912,68 +1449,11 @@ var nonNegativeInteger = (value, label) => {
912
1449
  if (result !== void 0 && !Number.isInteger(result)) return fail(`${label} must be an integer.`, "INVALID_CONFIG");
913
1450
  return result;
914
1451
  };
915
- var rate = (value, label) => {
916
- const result = nonNegativeNumber(value, label);
917
- if (result !== void 0 && result > 1) return fail(`${label} must be between 0 and 1.`, "INVALID_CONFIG");
918
- return result;
919
- };
920
1452
  var timestamp = (value, label) => {
921
1453
  const result = nonEmptyString(value, label);
922
1454
  if (!Number.isFinite(Date.parse(result))) return fail(`${label} must be a valid timestamp.`, "INVALID_CONFIG");
923
1455
  return result;
924
1456
  };
925
- var relativePath = (value, label) => {
926
- const result = nonEmptyString(value, label);
927
- if (result.startsWith("/") || result.split("/").includes("..")) return fail(`${label} must be a repository-relative path.`, "INVALID_CONFIG");
928
- return result;
929
- };
930
- var taskFile = (value, label) => {
931
- if (value === void 0) return void 0;
932
- if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
933
- const raw = value;
934
- return { path: relativePath(raw["path"], `${label}.path`), sha256: sha2562(raw["sha256"], `${label}.sha256`) ?? fail(`${label}.sha256 is required.`, "INVALID_CONFIG") };
935
- };
936
- var taskSource = (value, label) => {
937
- if (value === void 0) return void 0;
938
- if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
939
- const raw = value;
940
- return { repository: nonEmptyString(raw["repository"], `${label}.repository`), path: relativePath(raw["path"], `${label}.path`), revision: nonEmptyString(raw["revision"], `${label}.revision`) };
941
- };
942
- var suiteSource = (value, label) => {
943
- if (value === void 0) return void 0;
944
- if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
945
- const raw = value;
946
- return { repository: nonEmptyString(raw["repository"], `${label}.repository`), revision: nonEmptyString(raw["revision"], `${label}.revision`), taskDefinition: relativePath(raw["taskDefinition"], `${label}.taskDefinition`) };
947
- };
948
- var taskScope = (value, label) => {
949
- if (value === void 0) return void 0;
950
- if (typeof value !== "object" || value === null || Array.isArray(value)) return fail(`${label} must be an object.`, "INVALID_CONFIG");
951
- const raw = value;
952
- return { read: stringList(raw["read"], `${label}.read`), write: stringList(raw["write"], `${label}.write`) };
953
- };
954
- var taskSurfaces = (value, label) => {
955
- if (value === void 0) return void 0;
956
- if (!Array.isArray(value) || !value.length) return fail(`${label} must be a non-empty array.`, "INVALID_CONFIG");
957
- const surfaces = value.map((item, index2) => nonEmptyString(item, `${label}[${index2}]`));
958
- if (surfaces.some((surface2) => !SURFACE_NAMES.includes(surface2))) fail(`${label} contains an unknown surface.`, "INVALID_CONFIG");
959
- if (new Set(surfaces).size !== surfaces.length) fail(`${label} must contain unique surfaces.`, "INVALID_CONFIG");
960
- return surfaces;
961
- };
962
- var benchmarkPolicy = (value) => {
963
- if (value === void 0) return void 0;
964
- if (typeof value !== "object" || value === null || Array.isArray(value)) return fail("benchmark.policy must be an object.", "INVALID_CONFIG");
965
- const raw = value;
966
- const minComparableTasks = nonNegativeInteger(raw["minComparableTasks"], "benchmark.policy.minComparableTasks");
967
- const maxDurationRegressionRate = nonNegativeNumber(raw["maxDurationRegressionRate"], "benchmark.policy.maxDurationRegressionRate");
968
- const minCompletedRunsPerTask = nonNegativeInteger(raw["minCompletedRunsPerTask"], "benchmark.policy.minCompletedRunsPerTask");
969
- const minBaselineSamplesPerTask = nonNegativeInteger(raw["minBaselineSamplesPerTask"] ?? 1, "benchmark.policy.minBaselineSamplesPerTask");
970
- if (minComparableTasks === void 0 || minComparableTasks < 1) return fail("benchmark.policy.minComparableTasks must be at least 1.", "INVALID_CONFIG");
971
- if (maxDurationRegressionRate === void 0 || maxDurationRegressionRate > 1) return fail("benchmark.policy.maxDurationRegressionRate must be between 0 and 1.", "INVALID_CONFIG");
972
- if (minCompletedRunsPerTask === void 0 || minCompletedRunsPerTask < 1) return fail("benchmark.policy.minCompletedRunsPerTask must be at least 1.", "INVALID_CONFIG");
973
- if (minBaselineSamplesPerTask === void 0 || minBaselineSamplesPerTask < 1) return fail("benchmark.policy.minBaselineSamplesPerTask must be at least 1.", "INVALID_CONFIG");
974
- if (typeof raw["requireZeroEscapedIncomplete"] !== "boolean") return fail("benchmark.policy.requireZeroEscapedIncomplete must be boolean.", "INVALID_CONFIG");
975
- return { minComparableTasks, maxDurationRegressionRate, minCompletedRunsPerTask, minBaselineSamplesPerTask, requireZeroEscapedIncomplete: raw["requireZeroEscapedIncomplete"] };
976
- };
977
1457
  var validateBenchmarkManifest = (value) => {
978
1458
  if (typeof value !== "object" || value === null || Array.isArray(value)) fail("benchmark manifest must be an object.", "INVALID_CONFIG");
979
1459
  const raw = value;
@@ -982,12 +1462,7 @@ var validateBenchmarkManifest = (value) => {
982
1462
  const tasks = rawTasks.map((item, index2) => {
983
1463
  if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`benchmark.tasks[${index2}] must be an object.`, "INVALID_CONFIG");
984
1464
  const task = item;
985
- const kind = task["kind"] === void 0 ? void 0 : nonEmptyString(task["kind"], `benchmark.tasks[${index2}].kind`);
986
- const prompt = taskFile(task["prompt"], `benchmark.tasks[${index2}].prompt`);
987
- const source = taskSource(task["source"], `benchmark.tasks[${index2}].source`);
988
- const scope = taskScope(task["scope"], `benchmark.tasks[${index2}].scope`);
989
- const surfaces = taskSurfaces(task["surfaces"], `benchmark.tasks[${index2}].surfaces`);
990
- return { id: nonEmptyString(task["id"], `benchmark.tasks[${index2}].id`), title: nonEmptyString(task["title"], `benchmark.tasks[${index2}].title`), acceptanceCriteria: stringList(task["acceptanceCriteria"], `benchmark.tasks[${index2}].acceptanceCriteria`), ...surfaces === void 0 ? {} : { surfaces }, ...kind === void 0 ? {} : { kind }, ...prompt === void 0 ? {} : { prompt }, ...source === void 0 ? {} : { source }, ...scope === void 0 ? {} : { scope } };
1465
+ return { id: nonEmptyString(task["id"], `benchmark.tasks[${index2}].id`), title: nonEmptyString(task["title"], `benchmark.tasks[${index2}].title`), acceptanceCriteria: stringList(task["acceptanceCriteria"], `benchmark.tasks[${index2}].acceptanceCriteria`) };
991
1466
  });
992
1467
  if (new Set(tasks.map((task) => task.id)).size !== tasks.length) fail("benchmark task ids must be unique.", "INVALID_CONFIG");
993
1468
  const taskIds = new Set(tasks.map((task) => task.id));
@@ -995,17 +1470,13 @@ var validateBenchmarkManifest = (value) => {
995
1470
  const observations = rawObservations.map((item, index2) => {
996
1471
  if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`benchmark.observations[${index2}] must be an object.`, "INVALID_CONFIG");
997
1472
  const observation = item;
998
- const status = observation["status"];
999
- if (!["passed", "failed", "blocked", "not-run"].includes(String(status))) fail(`benchmark.observations[${index2}].status is invalid.`, "INVALID_CONFIG");
1473
+ const status2 = observation["status"];
1474
+ if (!["passed", "failed", "blocked", "not-run"].includes(String(status2))) fail(`benchmark.observations[${index2}].status is invalid.`, "INVALID_CONFIG");
1000
1475
  const taskId = nonEmptyString(observation["taskId"], `benchmark.observations[${index2}].taskId`);
1001
1476
  if (!taskIds.has(taskId)) fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
1002
1477
  const task = tasks.find((candidate) => candidate.id === taskId) ?? fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
1003
1478
  const attempts = nonNegativeInteger(observation["attempts"], `benchmark.observations[${index2}].attempts`);
1004
1479
  const durationMs = nonNegativeNumber(observation["durationMs"], `benchmark.observations[${index2}].durationMs`);
1005
- const durationSamplesMs = observation["durationSamplesMs"] === void 0 ? void 0 : Array.isArray(observation["durationSamplesMs"]) ? observation["durationSamplesMs"].map((sample, sampleIndex) => nonNegativeNumber(sample, `benchmark.observations[${index2}].durationSamplesMs[${sampleIndex}]`) ?? fail(`benchmark.observations[${index2}].durationSamplesMs must contain numbers.`, "INVALID_CONFIG")) : fail(`benchmark.observations[${index2}].durationSamplesMs must be an array.`, "INVALID_CONFIG");
1006
- if (durationSamplesMs && !durationSamplesMs.length) fail(`benchmark.observations[${index2}].durationSamplesMs must not be empty.`, "INVALID_CONFIG");
1007
- const artifactAcceptanceRate2 = rate(observation["artifactAcceptanceRate"], `benchmark.observations[${index2}].artifactAcceptanceRate`);
1008
- const protocolCompletionRate = rate(observation["protocolCompletionRate"], `benchmark.observations[${index2}].protocolCompletionRate`);
1009
1480
  const reviewMinutes2 = nonNegativeNumber(observation["reviewMinutes"], `benchmark.observations[${index2}].reviewMinutes`);
1010
1481
  const escapedIncomplete = nonNegativeInteger(observation["escapedIncomplete"], `benchmark.observations[${index2}].escapedIncomplete`);
1011
1482
  const evidenceDigest = sha2562(observation["evidenceDigest"], `benchmark.observations[${index2}].evidenceDigest`);
@@ -1020,12 +1491,10 @@ var validateBenchmarkManifest = (value) => {
1020
1491
  return { criterion, status: evidenceStatus, source: nonEmptyString(entry["source"], `benchmark.observations[${index2}].evidence[${evidenceIndex}].source`) };
1021
1492
  });
1022
1493
  if (evidence && new Set(evidence.map((entry) => entry.criterion)).size !== evidence.length) fail(`benchmark.observations[${index2}].evidence criteria must be unique.`, "INVALID_CONFIG");
1023
- return { taskId, mode: "baseline", status, source: nonEmptyString(observation["source"], `benchmark.observations[${index2}].source`), recordedAt: timestamp(observation["recordedAt"], `benchmark.observations[${index2}].recordedAt`), ...attempts === void 0 ? {} : { attempts }, ...durationMs === void 0 ? {} : { durationMs }, ...durationSamplesMs === void 0 ? {} : { durationSamplesMs }, ...artifactAcceptanceRate2 === void 0 ? {} : { artifactAcceptanceRate: artifactAcceptanceRate2 }, ...protocolCompletionRate === void 0 ? {} : { protocolCompletionRate }, ...reviewMinutes2 === void 0 ? {} : { reviewMinutes: reviewMinutes2 }, ...escapedIncomplete === void 0 ? {} : { escapedIncomplete }, ...evidence === void 0 ? {} : { evidence }, ...evidenceDigest === void 0 ? {} : { evidenceDigest } };
1494
+ return { taskId, mode: "baseline", status: status2, source: nonEmptyString(observation["source"], `benchmark.observations[${index2}].source`), recordedAt: timestamp(observation["recordedAt"], `benchmark.observations[${index2}].recordedAt`), ...attempts === void 0 ? {} : { attempts }, ...durationMs === void 0 ? {} : { durationMs }, ...reviewMinutes2 === void 0 ? {} : { reviewMinutes: reviewMinutes2 }, ...escapedIncomplete === void 0 ? {} : { escapedIncomplete }, ...evidence === void 0 ? {} : { evidence }, ...evidenceDigest === void 0 ? {} : { evidenceDigest } };
1024
1495
  });
1025
1496
  if (new Set(observations.map((observation) => observation.taskId)).size !== observations.length) fail("benchmark allows at most one baseline observation per task.", "INVALID_CONFIG");
1026
- const provenance = suiteSource(raw["provenance"], "benchmark.provenance");
1027
- const policy = benchmarkPolicy(raw["policy"]);
1028
- return { type: "agentskit-harness-benchmark-manifest", schemaVersion: BENCHMARK_SCHEMA_VERSION, suiteId: nonEmptyString(raw["suiteId"], "benchmark.suiteId"), name: nonEmptyString(raw["name"], "benchmark.name"), ...provenance === void 0 ? {} : { provenance }, tasks, observations, ...policy === void 0 ? {} : { policy } };
1497
+ return { type: "agentskit-harness-benchmark-manifest", schemaVersion: BENCHMARK_SCHEMA_VERSION, suiteId: nonEmptyString(raw["suiteId"], "benchmark.suiteId"), name: nonEmptyString(raw["name"], "benchmark.name"), tasks, observations };
1029
1498
  };
1030
1499
  var loadBenchmarkManifest = (path) => {
1031
1500
  try {
@@ -1051,9 +1520,6 @@ var recordBenchmarkObservation = (path, input) => {
1051
1520
  recordedAt: input.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1052
1521
  ...input.attempts === void 0 ? {} : { attempts: input.attempts },
1053
1522
  ...input.durationMs === void 0 ? {} : { durationMs: input.durationMs },
1054
- ...input.durationSamplesMs === void 0 ? {} : { durationSamplesMs: input.durationSamplesMs },
1055
- ...input.artifactAcceptanceRate === void 0 ? {} : { artifactAcceptanceRate: input.artifactAcceptanceRate },
1056
- ...input.protocolCompletionRate === void 0 ? {} : { protocolCompletionRate: input.protocolCompletionRate },
1057
1523
  ...input.reviewMinutes === void 0 ? {} : { reviewMinutes: input.reviewMinutes },
1058
1524
  ...input.escapedIncomplete === void 0 ? {} : { escapedIncomplete: input.escapedIncomplete },
1059
1525
  ...input.evidence === void 0 ? {} : { evidence: input.evidence },
@@ -1072,57 +1538,261 @@ var recordBenchmarkObservation = (path, input) => {
1072
1538
  }
1073
1539
  return observation;
1074
1540
  };
1075
- var comparisons = (runs, manifest, policy) => manifest.tasks.map((task) => {
1541
+ var comparisons = (runs, manifest) => manifest.tasks.map((task) => {
1076
1542
  const taskRuns = runs.filter((run) => run.benchmark?.suiteId === manifest.suiteId && run.benchmark.taskId === task.id);
1077
1543
  const latest = taskRuns.at(-1);
1078
1544
  const baseline = manifest.observations.find((observation) => observation.taskId === task.id);
1079
1545
  const coveredCriteria = new Set((baseline?.evidence ?? []).map((entry) => entry.criterion));
1080
1546
  const baselineEvidenceCoverageRate = baseline ? percentage(coveredCriteria.size, task.acceptanceCriteria.length) : null;
1081
1547
  const baselineEvidenceComplete = baselineEvidenceCoverageRate === 1;
1082
- const baselineEvidencePassed = baselineEvidenceComplete && (baseline?.evidence?.every((entry) => entry.status === "passed") ?? false);
1083
- const baselineDeliveryComplete = baseline?.status === "passed" && baselineEvidencePassed;
1084
- const baselineDurationSamples = baseline?.durationSamplesMs ?? (baseline?.durationMs === void 0 ? [] : [baseline.durationMs]);
1085
- const baselineMedianDurationMs = median(baselineDurationSamples);
1086
- const baselineSamplesSufficient = baselineDurationSamples.length >= policy.minBaselineSamplesPerTask;
1087
- const comparable = baseline !== void 0 && baseline.status !== "not-run" && baselineEvidenceComplete && baselineDeliveryComplete && baselineSamplesSufficient && latest?.state === "COMPLETE";
1088
- const comparability = comparable ? "comparable" : baseline === void 0 ? "missing-baseline" : baseline.status === "not-run" ? "baseline-not-run" : !baselineEvidenceComplete ? "baseline-evidence-missing" : latest === void 0 ? "harness-not-run" : latest.state !== "COMPLETE" ? "harness-not-complete" : !baselineDeliveryComplete ? "baseline-incomplete" : "baseline-samples-insufficient";
1089
- const completedTaskRuns = taskRuns.filter((run) => run.state === "COMPLETE");
1090
- const durationSamplesMs = completedTaskRuns.flatMap((run) => run.durationMs === void 0 ? [] : [run.durationMs]);
1091
- const medianDurationMs = median(durationSamplesMs);
1092
- const retryCount = count(taskRuns, (run) => run.supersedes !== void 0);
1093
- const attempts = retryCount + (taskRuns.length ? 1 : 0);
1094
- const durationRate = comparable ? improvementRate(baselineMedianDurationMs ?? void 0, medianDurationMs ?? void 0) : null;
1095
- const attemptsRate = comparable ? improvementRate(baseline?.attempts, attempts) : null;
1096
- const reviewRate = comparable ? improvementRate(baseline?.reviewMinutes, latest?.humanReviewMinutes) : null;
1097
- const acceptanceSamples = taskRuns.flatMap((run) => run.artifactAcceptanceRate === void 0 ? [] : [run.artifactAcceptanceRate]);
1098
- const harnessArtifactAcceptanceRate = acceptanceSamples.length ? Number((acceptanceSamples.reduce((total, rate2) => total + rate2, 0) / acceptanceSamples.length).toFixed(4)) : null;
1099
- const artifactAcceptanceImprovementRate = increaseRate(baseline?.artifactAcceptanceRate, harnessArtifactAcceptanceRate ?? void 0);
1100
- const artifactAcceptanceDelta = increaseDelta(baseline?.artifactAcceptanceRate, harnessArtifactAcceptanceRate);
1101
- const completedRuns = completedTaskRuns.length;
1102
- const harnessProtocolCompletionRate = taskRuns.length ? percentage(completedRuns, taskRuns.length) : null;
1103
- const protocolCompletionImprovementRate = increaseRate(baseline?.protocolCompletionRate, harnessProtocolCompletionRate ?? void 0);
1104
- const protocolCompletionDelta = increaseDelta(baseline?.protocolCompletionRate, harnessProtocolCompletionRate);
1105
- const escapedIncompleteRate = improvementRate(baseline?.escapedIncomplete, latest?.escapedIncomplete);
1106
- return { taskId: task.id, title: task.title, comparability, comparable, baselineDeliveryComplete, baselineEvidenceCoverageRate, baselineSampleCount: baselineDurationSamples.length, baselineArtifactAcceptanceRate: baseline?.artifactAcceptanceRate ?? null, baselineProtocolCompletionRate: baseline?.protocolCompletionRate ?? null, ...baselineMedianDurationMs === null ? {} : { baselineMedianDurationMs }, improvement: { durationRate, duration: improvementDirection(durationRate), attemptsRate, attempts: improvementDirection(attemptsRate), reviewRate, review: improvementDirection(reviewRate), artifactAcceptanceRate: artifactAcceptanceImprovementRate, artifactAcceptance: increaseDirection(artifactAcceptanceImprovementRate, artifactAcceptanceDelta), artifactAcceptanceDelta, protocolCompletionRate: protocolCompletionImprovementRate, protocolCompletion: increaseDirection(protocolCompletionImprovementRate, protocolCompletionDelta), protocolCompletionDelta, escapedIncompleteRate, escapedIncomplete: improvementDirection(escapedIncompleteRate) }, ...baseline ? { baseline } : {}, harness: { attempts, retryCount, completedRuns, durationSamplesMs, ...medianDurationMs === null ? {} : { medianDurationMs }, ...harnessArtifactAcceptanceRate === null ? {} : { artifactAcceptanceRate: harnessArtifactAcceptanceRate }, artifactAcceptanceSampleCount: acceptanceSamples.length, protocolCompletionRate: harnessProtocolCompletionRate, protocolCompletionSampleCount: taskRuns.length, latestState: latest?.state ?? "NOT_RUN", ...latest ? { latestRunId: latest.runId } : {}, ...latest?.durationMs === void 0 ? {} : { latestDurationMs: latest.durationMs }, checkPassRate: latest ? percentage(latest.checks.passed, latest.checks.total) : null, outcomePassRate: latest ? percentage(latest.outcomes.passed, latest.outcomes.total) : null, evidenceCoverageRate: latest ? percentage(latest.evidence.attached, latest.evidence.total) : null, ...latest?.escapedIncomplete === void 0 ? {} : { escapedIncomplete: latest.escapedIncomplete }, ...latest?.humanReviewMinutes === void 0 ? {} : { humanReviewMinutes: latest.humanReviewMinutes }, humanApproved: latest?.humanApproved ?? false }, confidence: confidence(comparable, completedRuns, policy), ...comparable && baselineMedianDurationMs !== null && medianDurationMs !== null ? { durationDeltaMs: medianDurationMs - baselineMedianDurationMs } : {}, ...comparable && baseline?.attempts !== void 0 ? { attemptDelta: attempts - baseline.attempts } : {}, ...comparable && baseline?.reviewMinutes !== void 0 && latest?.humanReviewMinutes !== void 0 ? { reviewDeltaMinutes: latest.humanReviewMinutes - baseline.reviewMinutes } : {}, ...baseline?.escapedIncomplete !== void 0 && latest?.escapedIncomplete !== void 0 ? { escapedIncompleteDelta: latest.escapedIncomplete - baseline.escapedIncomplete } : {} };
1548
+ const comparable2 = baseline !== void 0 && baseline.status !== "not-run" && baselineEvidenceComplete && latest?.state === "COMPLETE";
1549
+ const comparability = comparable2 ? "comparable" : baseline === void 0 ? "missing-baseline" : baseline.status === "not-run" ? "baseline-not-run" : !baselineEvidenceComplete ? "baseline-evidence-missing" : latest === void 0 ? "harness-not-run" : "harness-not-complete";
1550
+ const durationRate = comparable2 ? improvementRate(baseline?.durationMs, latest?.durationMs) : null;
1551
+ const attemptsRate = comparable2 ? improvementRate(baseline?.attempts, taskRuns.length) : null;
1552
+ const reviewRate = comparable2 ? improvementRate(baseline?.reviewMinutes, latest?.humanReviewMinutes) : null;
1553
+ const escapedIncompleteRate = comparable2 ? improvementRate(baseline?.escapedIncomplete, latest?.escapedIncomplete) : null;
1554
+ return { taskId: task.id, title: task.title, comparability, comparable: comparable2, baselineEvidenceCoverageRate, improvement: { durationRate, duration: improvementDirection(durationRate), attemptsRate, attempts: improvementDirection(attemptsRate), reviewRate, review: improvementDirection(reviewRate), escapedIncompleteRate, escapedIncomplete: improvementDirection(escapedIncompleteRate) }, ...baseline ? { baseline } : {}, harness: { attempts: taskRuns.length, latestState: latest?.state ?? "NOT_RUN", ...latest ? { latestRunId: latest.runId } : {}, ...latest?.durationMs === void 0 ? {} : { latestDurationMs: latest.durationMs }, checkPassRate: latest ? percentage(latest.checks.passed, latest.checks.total) : null, outcomePassRate: latest ? percentage(latest.outcomes.passed, latest.outcomes.total) : null, evidenceCoverageRate: latest ? percentage(latest.evidence.attached, latest.evidence.total) : null, ...latest?.escapedIncomplete === void 0 ? {} : { escapedIncomplete: latest.escapedIncomplete }, ...latest?.humanReviewMinutes === void 0 ? {} : { humanReviewMinutes: latest.humanReviewMinutes }, humanApproved: latest?.humanApproved ?? false }, ...comparable2 && baseline?.durationMs !== void 0 && latest?.durationMs !== void 0 ? { durationDeltaMs: latest.durationMs - baseline.durationMs } : {}, ...comparable2 && baseline?.attempts !== void 0 ? { attemptDelta: taskRuns.length - baseline.attempts } : {}, ...comparable2 && baseline?.reviewMinutes !== void 0 && latest?.humanReviewMinutes !== void 0 ? { reviewDeltaMinutes: latest.humanReviewMinutes - baseline.reviewMinutes } : {}, ...comparable2 && baseline?.escapedIncomplete !== void 0 && latest?.escapedIncomplete !== void 0 ? { escapedIncompleteDelta: latest.escapedIncomplete - baseline.escapedIncomplete } : {} };
1107
1555
  });
1108
1556
  var benchmarkRuns = (stateDir, manifest) => {
1109
1557
  const runs = readRuns(stateDir).map(projectRun).sort((left, right) => left.runId.localeCompare(right.runId));
1110
- const policy = manifest?.policy ?? DEFAULT_POLICY;
1111
- const reportComparisons = manifest ? comparisons(runs, manifest, policy) : [];
1112
- const comparable = reportComparisons.filter((comparison) => comparison.comparable);
1113
- const durationRegressionTaskIds = comparable.filter((comparison) => (comparison.improvement.durationRate ?? 0) < -policy.maxDurationRegressionRate).map((comparison) => comparison.taskId);
1114
- const escapedIncompleteTaskIds = comparable.filter((comparison) => comparison.harness.escapedIncomplete !== 0).map((comparison) => comparison.taskId);
1115
- const reasons = [];
1116
- if (comparable.length < policy.minComparableTasks) reasons.push(`requires at least ${policy.minComparableTasks} comparable tasks`);
1117
- const incompleteBaselineTaskIds = reportComparisons.filter((comparison) => comparison.comparability === "baseline-incomplete").map((comparison) => comparison.taskId);
1118
- if (incompleteBaselineTaskIds.length) reasons.push(`baseline delivery incomplete: ${incompleteBaselineTaskIds.join(", ")}`);
1119
- const baselineSampleGaps = reportComparisons.filter((comparison) => comparison.baselineSampleCount < policy.minBaselineSamplesPerTask).map((comparison) => `${comparison.taskId} (${comparison.baselineSampleCount}/${policy.minBaselineSamplesPerTask})`);
1120
- if (baselineSampleGaps.length) reasons.push(`requires ${policy.minBaselineSamplesPerTask} baseline samples per task: ${baselineSampleGaps.join(", ")}`);
1121
- if (durationRegressionTaskIds.length) reasons.push(`duration regression exceeds ${policy.maxDurationRegressionRate * 100}%: ${durationRegressionTaskIds.join(", ")}`);
1122
- if (policy.requireZeroEscapedIncomplete && escapedIncompleteTaskIds.length) reasons.push(`escaped incomplete delivery: ${escapedIncompleteTaskIds.join(", ")}`);
1123
- const confidenceLevel = comparable.length < policy.minComparableTasks ? "insufficient" : comparable.every((comparison) => comparison.confidence === "reliable") ? "reliable" : "directional";
1124
- const qualityGate = { status: comparable.length < policy.minComparableTasks ? "insufficient-data" : reasons.length ? "failed" : "passed", confidence: confidenceLevel, comparableTaskCount: comparable.length, policy, durationRegressionTaskIds, escapedIncompleteTaskIds, reasons };
1125
- return { type: "agentskit-harness-benchmark", schemaVersion: BENCHMARK_SCHEMA_VERSION, stateDir, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), runs, summary: summarize(runs), comparisons: reportComparisons, qualityGate, ...manifest ? { manifest: { suiteId: manifest.suiteId, taskCount: manifest.tasks.length, baselineCount: manifest.observations.length, comparableTaskCount: comparable.length } } : {} };
1558
+ const reportComparisons = manifest ? comparisons(runs, manifest) : [];
1559
+ return { type: "agentskit-harness-benchmark", schemaVersion: BENCHMARK_SCHEMA_VERSION, stateDir, generatedAt: (/* @__PURE__ */ new Date()).toISOString(), runs, summary: summarize(runs), comparisons: reportComparisons, ...manifest ? { manifest: { suiteId: manifest.suiteId, taskCount: manifest.tasks.length, baselineCount: manifest.observations.length, comparableTaskCount: reportComparisons.filter((comparison) => comparison.comparable).length } } : {} };
1560
+ };
1561
+ var required6 = (value, label) => {
1562
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1563
+ return value.trim();
1564
+ };
1565
+ var safeKey = (identity) => hashJson(identity);
1566
+ var now3 = () => (/* @__PURE__ */ new Date()).toISOString();
1567
+ var parse = (value, label) => {
1568
+ try {
1569
+ const raw = JSON.parse(value);
1570
+ const identity = {
1571
+ tracker: required6(raw["tracker"], `${label}.tracker`),
1572
+ repository: required6(raw["repository"], `${label}.repository`),
1573
+ issue: required6(raw["issue"], `${label}.issue`),
1574
+ worktree: required6(raw["worktree"], `${label}.worktree`),
1575
+ branch: required6(raw["branch"], `${label}.branch`)
1576
+ };
1577
+ return { ...identity, key: required6(raw["key"], `${label}.key`), leaseId: required6(raw["leaseId"], `${label}.leaseId`), owner: required6(raw["owner"], `${label}.owner`), claimedAt: required6(raw["claimedAt"], `${label}.claimedAt`) };
1578
+ } catch (error) {
1579
+ if (error instanceof SyntaxError) fail(`${label} contains invalid JSON.`, "HARNESS_ERROR");
1580
+ throw error;
1581
+ }
1582
+ };
1583
+ var createDispatchLedger = (stateDir) => {
1584
+ const root = required6(stateDir, "stateDir");
1585
+ const claimsDir = join(root, "coordination", "claims");
1586
+ const ledgerPath = join(root, "coordination", "dispatch-ledger.ndjson");
1587
+ mkdirSync(claimsDir, { recursive: true });
1588
+ const claimPath = (key) => join(claimsDir, `${key}.json`);
1589
+ const append = (record3) => appendFileSync(ledgerPath, `${JSON.stringify(record3)}
1590
+ `, "utf8");
1591
+ const records = () => {
1592
+ if (!existsSync(ledgerPath)) return [];
1593
+ return readFileSync(ledgerPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line, index2) => {
1594
+ try {
1595
+ return JSON.parse(line);
1596
+ } catch {
1597
+ return fail(`Dispatch ledger record ${index2 + 1} is invalid JSON.`, "HARNESS_ERROR");
1598
+ }
1599
+ });
1600
+ };
1601
+ const active = () => {
1602
+ const byKey = /* @__PURE__ */ new Map();
1603
+ for (const record3 of records()) {
1604
+ if (record3.action === "release" || record3.action === "recover") byKey.delete(record3.key);
1605
+ else if (record3.action === "dispatch") byKey.set(record3.key, record3);
1606
+ }
1607
+ return [...byKey.values()];
1608
+ };
1609
+ return {
1610
+ claim: (input) => {
1611
+ const identity = {
1612
+ tracker: required6(input.tracker, "tracker"),
1613
+ repository: required6(input.repository, "repository"),
1614
+ issue: required6(input.issue, "issue"),
1615
+ worktree: required6(input.worktree, "worktree"),
1616
+ branch: required6(input.branch, "branch")
1617
+ };
1618
+ const owner = required6(input.owner, "owner");
1619
+ const key = safeKey(identity);
1620
+ const path = claimPath(key);
1621
+ if (existsSync(path)) return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
1622
+ const lease = { ...identity, key, leaseId: randomUUID(), owner, claimedAt: now3() };
1623
+ let fd;
1624
+ try {
1625
+ fd = openSync(path, "wx");
1626
+ } catch (error) {
1627
+ if (error.code === "EEXIST") return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
1628
+ throw error;
1629
+ }
1630
+ try {
1631
+ writeFileSync(fd, JSON.stringify(lease), "utf8");
1632
+ } finally {
1633
+ closeSync(fd);
1634
+ }
1635
+ append({ ...lease, action: "dispatch", at: lease.claimedAt });
1636
+ return { decision: "claimed", lease };
1637
+ },
1638
+ recordDispatch: ({ lease, idempotencyKey, commandDigest }) => {
1639
+ const id2 = required6(idempotencyKey, "idempotencyKey");
1640
+ const digest4 = required6(commandDigest, "commandDigest");
1641
+ const existing = records().find((record4) => record4.action === "dispatch" && record4.idempotencyKey === id2);
1642
+ if (existing) return { decision: "duplicate", record: existing };
1643
+ const record3 = { ...lease, action: "dispatch", at: now3(), idempotencyKey: id2, commandDigest: digest4 };
1644
+ append(record3);
1645
+ return { decision: "recorded", record: record3 };
1646
+ },
1647
+ release: (lease, reason = "lease released") => {
1648
+ const path = claimPath(required6(lease.key, "lease.key"));
1649
+ if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
1650
+ const current = parse(readFileSync(path, "utf8"), "claim");
1651
+ if (current.leaseId !== lease.leaseId) fail("Dispatch lease owner does not match.", "INVALID_STATE");
1652
+ unlinkSync(path);
1653
+ const record3 = { ...current, action: "release", at: now3(), reason: required6(reason, "reason") };
1654
+ append(record3);
1655
+ return record3;
1656
+ },
1657
+ recover: (key, input) => {
1658
+ if (input.actor !== "human") fail("Dispatch lease recovery requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
1659
+ const normalizedKey = required6(key, "key");
1660
+ const maxAgeMs = input.maxAgeMs ?? 3e5;
1661
+ if (!Number.isInteger(maxAgeMs) || maxAgeMs < 0) fail("maxAgeMs must be a non-negative integer.", "INVALID_INPUT");
1662
+ const path = claimPath(normalizedKey);
1663
+ if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
1664
+ const current = parse(readFileSync(path, "utf8"), "claim");
1665
+ if (Date.now() - Date.parse(current.claimedAt) < maxAgeMs) fail("Dispatch lease is not old enough to recover.", "HARNESS_ERROR");
1666
+ unlinkSync(path);
1667
+ const record3 = { ...current, action: "recover", at: now3(), reason: required6(input.reason, "reason") };
1668
+ append(record3);
1669
+ return record3;
1670
+ },
1671
+ active,
1672
+ records
1673
+ };
1674
+ };
1675
+ var DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx", ".txt", ".adoc", ".rst"]);
1676
+ var TEST_SUFFIXES = [".test.", ".spec.", "__tests__"];
1677
+ var normalizedPath = (value, label) => {
1678
+ if (typeof value !== "string" || !value.trim()) fail(`${label} must be a non-empty path.`, "INVALID_INPUT");
1679
+ const path = value.trim().replaceAll("\\", "/");
1680
+ if (path.startsWith("/") || path.split("/").includes("..")) fail(`${label} must be repository-relative.`, "INVALID_INPUT");
1681
+ return path;
1682
+ };
1683
+ var isTest = (path) => TEST_SUFFIXES.some((suffix) => path.includes(suffix)) || /(^|\/)(test|tests|__tests__)\//.test(path);
1684
+ var isDoc = (path) => DOC_EXTENSIONS.has(extname(path).toLowerCase());
1685
+ var planFilePreflight = (files, options2 = {}) => {
1686
+ if (!Array.isArray(files)) fail("files must be an array.", "INVALID_INPUT");
1687
+ const unique2 = [...new Set(files.map((file, index2) => normalizedPath(file.path, `files[${index2}].path`)))].sort();
1688
+ const codeFiles = unique2.filter((path) => !isDoc(path) && !isTest(path));
1689
+ const existingTests = unique2.filter(isTest);
1690
+ const roots = (options2.testRoots ?? ["test", "tests", "__tests__"]).map((root, index2) => normalizedPath(root, `testRoots[${index2}]`));
1691
+ const colocated = options2.includeTests === false ? [] : codeFiles.flatMap((path) => {
1692
+ const file = basename(path);
1693
+ const directory = dirname(path);
1694
+ const stem = file.includes(".") ? file.slice(0, file.lastIndexOf(".")) : file;
1695
+ return [join(directory, `${stem}.test.ts`), join(directory, `${stem}.spec.ts`)].filter((candidate) => unique2.includes(candidate));
1696
+ });
1697
+ const testFiles = [...new Set([...existingTests, ...colocated, ...unique2.filter((path) => roots.some((root) => path === root || path.startsWith(`${root}/`)))].sort())];
1698
+ const docsOnly = unique2.length > 0 && codeFiles.length === 0 && existingTests.length === 0;
1699
+ return { files: unique2, codeFiles, testFiles, docsOnly, checks: docsOnly ? [] : ["lint", "typecheck", ...testFiles.length ? ["test"] : []] };
1700
+ };
1701
+
1702
+ // src/kernel/block.ts
1703
+ var BLOCK_STATUSES = ["todo", "picked", "development", "validation", "pr-open", "merged", "post-merge", "done", "blocked", "scope-cut"];
1704
+ var text3 = (value, label) => {
1705
+ return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1706
+ };
1707
+ var list = (value, label) => {
1708
+ if (!Array.isArray(value)) return fail(`${label} must be an array of non-empty strings.`, "INVALID_INPUT");
1709
+ if (!value.every((item) => typeof item === "string" && Boolean(item.trim()))) return fail(`${label} must be an array of non-empty strings.`, "INVALID_INPUT");
1710
+ return [...new Set(value.map((item) => item.trim()))];
1711
+ };
1712
+ var validateBlockManifest = (value) => {
1713
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("block manifest must be an object.", "INVALID_INPUT");
1714
+ const raw = value;
1715
+ if (raw["schemaVersion"] !== 1) fail("block manifest schemaVersion must be 1.", "INVALID_INPUT");
1716
+ const criteria = list(raw["acceptanceCriteria"], "acceptanceCriteria");
1717
+ if (!criteria.length) fail("acceptanceCriteria must not be empty.", "INVALID_INPUT");
1718
+ const dependencies = list(raw["dependencies"] ?? [], "dependencies");
1719
+ const wave = raw["wave"];
1720
+ if (!Number.isInteger(wave) || wave < 1) fail("wave must be a positive integer.", "INVALID_INPUT");
1721
+ const status2 = raw["status"];
1722
+ if (!BLOCK_STATUSES.includes(status2)) fail("status is invalid.", "INVALID_INPUT");
1723
+ const budgetRaw = raw["budget"];
1724
+ let budget;
1725
+ if (budgetRaw !== void 0) {
1726
+ if (typeof budgetRaw !== "object" || budgetRaw === null || Array.isArray(budgetRaw)) fail("budget must be an object.", "INVALID_INPUT");
1727
+ const candidate = budgetRaw;
1728
+ for (const key of ["maxMinutes", "maxAttempts"]) if (candidate[key] !== void 0 && (!Number.isInteger(candidate[key]) || candidate[key] < 1)) fail(`budget.${key} must be a positive integer.`, "INVALID_INPUT");
1729
+ budget = { ...candidate["maxMinutes"] === void 0 ? {} : { maxMinutes: candidate["maxMinutes"] }, ...candidate["maxAttempts"] === void 0 ? {} : { maxAttempts: candidate["maxAttempts"] } };
1730
+ }
1731
+ return { schemaVersion: 1, id: text3(raw["id"], "id"), title: text3(raw["title"], "title"), tracker: text3(raw["tracker"], "tracker"), repository: text3(raw["repository"], "repository"), acceptanceCriteria: criteria, dependencies, wave, status: status2, ...budget ? { budget } : {}, ...raw["humanGates"] === void 0 ? {} : { humanGates: list(raw["humanGates"], "humanGates") }, ...raw["sourceHash"] === void 0 ? {} : { sourceHash: text3(raw["sourceHash"], "sourceHash") } };
1732
+ };
1733
+ var assessBlock = (manifest, completedDependencies = []) => {
1734
+ const value = validateBlockManifest(manifest);
1735
+ const completed = new Set(completedDependencies.map((item) => text3(item, "completedDependencies[]")));
1736
+ const blockers = value.dependencies.filter((dependency) => !completed.has(dependency));
1737
+ const next = blockers.length ? [`Complete dependencies: ${blockers.join(", ")}`] : value.status === "blocked" ? ["Resolve the recorded blocker before dispatch."] : ["Dispatch the block with the frozen acceptance criteria."];
1738
+ return { status: blockers.length || value.status === "blocked" ? "blocked" : "ready", manifestHash: hashJson(value), blockers, next };
1739
+ };
1740
+ var text4 = (value, label) => {
1741
+ return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1742
+ };
1743
+ var category = (heading) => {
1744
+ const value = heading.toLowerCase();
1745
+ if (/went well|success|worked/.test(value)) return "worked";
1746
+ if (/problem|failed|blocker|pain/.test(value)) return "problem";
1747
+ if (/adjust|action|next|improv/.test(value)) return "adjustment";
1748
+ return "other";
1749
+ };
1750
+ var parseRetro = (markdown, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) => {
1751
+ const input = text4(markdown, "markdown");
1752
+ const origin = text4(source, "source");
1753
+ if (!Number.isFinite(Date.parse(recordedAt))) fail("recordedAt must be a valid timestamp.", "INVALID_INPUT");
1754
+ const records = [];
1755
+ let current = "other";
1756
+ for (const line of input.split(/\r?\n/)) {
1757
+ const heading = line.match(/^#{1,6}\s+(.+)$/);
1758
+ if (heading) {
1759
+ current = category(heading[1] ?? "");
1760
+ continue;
1761
+ }
1762
+ const item = line.match(/^\s*[-*]\s+(?:\[[ xX]\]\s+)?(.+?)\s*$/);
1763
+ if (!item?.[1]?.trim()) continue;
1764
+ const value = item[1].trim();
1765
+ const id2 = `L-${createHash("sha256").update(`${origin}|${current}|${value}`).digest("hex").slice(0, 12)}`;
1766
+ if (!records.some((record3) => record3.id === id2)) records.push({ id: id2, source: origin, category: current, text: value, status: "proposed", recordedAt });
1767
+ }
1768
+ return records;
1769
+ };
1770
+
1771
+ // src/kernel/status.ts
1772
+ var required7 = (value, label) => {
1773
+ return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1774
+ };
1775
+ var createStatusSnapshot = (input) => {
1776
+ const sourceRevision = required7(input.sourceRevision, "sourceRevision");
1777
+ if (!Number.isFinite(Date.parse(input.generatedAt))) fail("generatedAt must be a valid timestamp.", "INVALID_INPUT");
1778
+ if (!Array.isArray(input.blocks)) fail("blocks must be an array.", "INVALID_INPUT");
1779
+ const blocks = input.blocks.map((block2, index2) => {
1780
+ if (typeof block2 !== "object" || block2 === null || Array.isArray(block2)) fail(`blocks[${index2}] must be an object.`, "INVALID_INPUT");
1781
+ const value = block2;
1782
+ if (!(typeof value.id === "string" && value.id.trim())) fail(`blocks[${index2}].id is required.`, "INVALID_INPUT");
1783
+ if (!["todo", "picked", "development", "validation", "pr-open", "merged", "post-merge", "done", "blocked", "scope-cut"].includes(value.status)) fail(`blocks[${index2}].status is invalid.`, "INVALID_INPUT");
1784
+ return { ...value, id: value.id.trim() };
1785
+ }).sort((left, right) => left.id.localeCompare(right.id));
1786
+ if (input.metrics !== void 0 && Object.entries(input.metrics).some(([key, value]) => !key.trim() || typeof value !== "number" || !Number.isFinite(value) || value < 0)) fail("metrics must contain finite non-negative numbers.", "INVALID_INPUT");
1787
+ const body2 = { schemaVersion: 1, generatedAt: input.generatedAt, sourceRevision, blocks, ...input.machine ? { machine: input.machine } : {}, ...input.metrics ? { metrics: input.metrics } : {}, ...input.next ? { next: required7(input.next, "next") } : {} };
1788
+ return { ...body2, digest: hashJson(body2) };
1789
+ };
1790
+ var validateStatusSnapshot = (value) => {
1791
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("status snapshot must be an object.", "INVALID_INPUT");
1792
+ const raw = value;
1793
+ const snapshot = createStatusSnapshot({ generatedAt: required7(raw.generatedAt, "generatedAt"), sourceRevision: required7(raw.sourceRevision, "sourceRevision"), blocks: raw.blocks, ...raw.machine ? { machine: raw.machine } : {}, ...raw.metrics ? { metrics: raw.metrics } : {}, ...raw.next ? { next: raw.next } : {} });
1794
+ if (raw.schemaVersion !== 1 || raw.digest !== snapshot.digest) fail("status snapshot digest or schemaVersion is invalid.", "HARNESS_ERROR");
1795
+ return snapshot;
1126
1796
  };
1127
1797
  var EVIDENCE_BUNDLE_SCHEMA_VERSION = 1;
1128
1798
  var body = (bundle) => {
@@ -1150,7 +1820,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
1150
1820
  const loaded = loadConfig(configPath);
1151
1821
  const run = requireRun2(runId ? readJson(join(loaded.stateDir, "runs", runId, "run.json")) : loadLatestRun(loaded.stateDir));
1152
1822
  const reconciliation = await reconcileRun({ configPath, runId: run.runId });
1153
- const digest2 = run.verificationDigest ?? fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
1823
+ const digest4 = run.verificationDigest ?? fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
1154
1824
  if (reconciliation.state !== "COMPLETE") fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
1155
1825
  const eventLog = new FileEventStore(loaded.stateDir);
1156
1826
  eventLog.read(run.runId);
@@ -1165,7 +1835,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
1165
1835
  return bundleFile(loaded.stateDir, path);
1166
1836
  });
1167
1837
  const privateKey = createPrivateKey(readFileSync(privateKeyPath));
1168
- const unsigned = { type: "agentskit-harness-evidence-bundle", schemaVersion: EVIDENCE_BUNDLE_SCHEMA_VERSION, runId: run.runId, signerKeyId: keyId, sourceRevision: run.sourceRevision, configHash: run.configHash, contractHash: run.contractHash, verificationDigest: digest2, eventLog: eventVerification, files };
1838
+ const unsigned = { type: "agentskit-harness-evidence-bundle", schemaVersion: EVIDENCE_BUNDLE_SCHEMA_VERSION, runId: run.runId, signerKeyId: keyId, sourceRevision: run.sourceRevision, configHash: run.configHash, contractHash: run.contractHash, verificationDigest: digest4, eventLog: eventVerification, files };
1169
1839
  const payloadHash = sha256(JSON.stringify(unsigned));
1170
1840
  const publicKeyPem = createPublicKey(privateKey).export({ type: "spki", format: "pem" }).toString();
1171
1841
  const bundle = { ...unsigned, payloadHash, signature: { algorithm: "ed25519", keyId, publicKeyPem, signatureBase64: sign(null, Buffer.from(payloadHash), privateKey).toString("base64") } };
@@ -1233,14 +1903,63 @@ var decisionArgs = (first, second) => {
1233
1903
  const decisions = /* @__PURE__ */ new Set(["approved", "approve", "yes", "ok", "rejected", "reject", "no"]);
1234
1904
  return decisions.has(first) ? { decision: first, ...second ? { runId: second } : {} } : { decision: second ?? "", runId: first };
1235
1905
  };
1906
+ var readJsonInput = (path, label) => {
1907
+ try {
1908
+ return JSON.parse(readFileSync(path, "utf8"));
1909
+ } catch (error) {
1910
+ return fail(`Invalid ${label} JSON: ${error instanceof Error ? error.message : String(error)}`, "INVALID_INPUT");
1911
+ }
1912
+ };
1236
1913
  program.command("doctor").description("Validate the contract without starting a run.").action(() => print({ status: "passed", criteria: ["package"], config: loadConfig(options().config).config }));
1237
- program.command("plan <decision>").description("Prepare or approve the frozen task contract and create a planned run.").option("--by <actor>", "planner: human approval or ci preparation", "human").option("--allow-dirty", "allow a human-authorized dirty worktree").option("--context-file <path>", "attach a context snapshot JSON file").action(async (decision, command) => print(await planRun({ configPath: options().config, decision, actor: command.by, allowDirty: command.allowDirty ?? false, contextSnapshots: command.contextFile ? readContextSnapshots(command.contextFile) : [] })));
1914
+ program.command("plan <decision>").description("Approve the frozen task contract and create a planned run.").option("--by <actor>", "approval actor", "human").option("--allow-dirty", "allow a human-authorized dirty worktree").option("--context-file <path>", "attach a context snapshot JSON file").action(async (decision, command) => print(await planRun({ configPath: options().config, decision, actor: command.by, allowDirty: command.allowDirty ?? false, contextSnapshots: command.contextFile ? readContextSnapshots(command.contextFile) : [] })));
1238
1915
  var context = program.command("context").description("Resolve portable, provenance-bearing context snapshots.");
1239
1916
  context.command("resolve <query>").description("Resolve a Doc Bridge snapshot from the local index.").option("--provider <provider>", "context provider", "doc-bridge").option("--scope <scope...>", "optional search scopes").option("--index <path>", "Doc Bridge index path", ".doc-bridge/index.json").action(async (query, command) => {
1240
1917
  if (command.provider !== "doc-bridge") fail(`Unsupported context provider: ${command.provider}`, "INVALID_INPUT");
1241
1918
  const loaded = loadConfig(options().config);
1242
1919
  print(await createDocBridgeContextProvider({ root: loaded.root, indexPath: command.index }).resolve({ query, ...command.scope?.length ? { scope: command.scope } : {} }));
1243
1920
  });
1921
+ var discovery = program.command("discovery").description("Assess a structured discovery result before implementation.");
1922
+ discovery.command("assess <input>").description("Emit Ready or a human decision packet from a discovery JSON file.").action((input) => print(assessDiscovery(readJsonInput(input, "discovery input"))));
1923
+ var wip = program.command("wip").description("Assess deterministic WIP admission before starting work.");
1924
+ wip.command("assess <input>").description("Emit an admission decision from WIP ledger JSON.").action((input) => print(assessWip(readJsonInput(input, "WIP input"))));
1925
+ var experiment = program.command("experiment").description("Select a runtime only from a controlled, comparable experiment.");
1926
+ experiment.command("select <input>").description("Select the eligible runtime from experiment JSON.").action((input) => print(selectRuntime(readJsonInput(input, "experiment input"))));
1927
+ var delivery = program.command("delivery").description("Assess deterministic G2\u2013G5 gates and prepare idempotent PR handoff.");
1928
+ delivery.command("preflight <input>").action((input) => print(assessPreflight(readJsonInput(input, "preflight input"))));
1929
+ delivery.command("pr <input>").action((input) => print(composePullRequest(readJsonInput(input, "PR input"))));
1930
+ delivery.command("integration <input>").action((input) => print(assessIntegration(readJsonInput(input, "integration input"))));
1931
+ delivery.command("production <input>").action((input) => print(assessProduction(readJsonInput(input, "production input"))));
1932
+ delivery.command("acceptance <input>").action((input) => print(assessAcceptance(readJsonInput(input, "acceptance input"))));
1933
+ delivery.command("cleanup <input>").action((input) => print(assessWorktreeCleanup(readJsonInput(input, "cleanup input"))));
1934
+ program.command("pilot <input>").description("Freeze and assess a ten-issue pilot cohort.").action((input) => print(assessPilot(readJsonInput(input, "pilot input"))));
1935
+ var cycle = program.command("cycle").description("Run the five-step improvement cycle with explicit adjustment and bounded repetition.");
1936
+ cycle.command("assess <input>").description("Assess run \u2192 verify \u2192 adjust \u2192 repeat from a cycle JSON file.").action((input) => print(assessImprovementCycle(readJsonInput(input, "cycle input"))));
1937
+ var block = program.command("block").description("Validate and assess a portable execution block manifest.");
1938
+ block.command("validate <input>").action((input) => print(validateBlockManifest(readJsonInput(input, "block manifest"))));
1939
+ block.command("assess <input>").option("--completed <ids...>", "completed dependency IDs").action((input, command) => print(assessBlock(readJsonInput(input, "block manifest"), command.completed ?? [])));
1940
+ var preflight = program.command("preflight").description("Plan safe, file-scoped validation before commit.");
1941
+ preflight.command("files <input>").action((input) => print(planFilePreflight(readJsonInput(input, "changed files"))));
1942
+ var status = program.command("snapshot <input>").description("Create or validate a deterministic status snapshot.");
1943
+ status.action((input) => print(createStatusSnapshot(readJsonInput(input, "status input"))));
1944
+ status.command("validate <input>").action((input) => print(validateStatusSnapshot(readJsonInput(input, "status snapshot"))));
1945
+ var learning = program.command("learning").description("Parse retrospectives into proposed learnings.");
1946
+ learning.command("parse <input>").requiredOption("--source <source>").action((input, command) => print(parseRetro(readFileSync(input, "utf8"), command.source)));
1947
+ var coordination = program.command("coordination").description("Manage idempotent issue/worktree claims and dispatch records.");
1948
+ coordination.command("claim <input>").action((input) => {
1949
+ const loaded = loadConfig(options().config);
1950
+ print(createDispatchLedger(loaded.stateDir).claim(readJsonInput(input, "coordination identity")));
1951
+ });
1952
+ var artifacts = program.command("artifacts").description("Inspect versioned, provenance-bound run artifacts.");
1953
+ artifacts.command("inspect <path>").description("Validate and print one artifact as JSON or Markdown.").action((path) => {
1954
+ const artifact = readArtifactFile(path);
1955
+ print(options().json ? artifact : renderArtifactMarkdown(artifact));
1956
+ });
1957
+ artifacts.command("list [run-id]").description("List artifacts for the latest or selected run.").action((runId) => {
1958
+ const loaded = loadConfig(options().config);
1959
+ const run = runId ? { runId } : loadLatestRun(loaded.stateDir);
1960
+ print(new FileArtifactStore(loaded.stateDir).list(run?.runId ?? fail("No verification run exists.", "NO_RUN")));
1961
+ });
1962
+ artifacts.command("schema").description("Print the artifact schema version.").action(() => print({ schemaVersion: ARTIFACT_SCHEMA_VERSION, types: ["plan", "finding", "decision", "repair", "blocker", "approval", "phase"] }));
1244
1963
  program.command("start").description("Move a planned run into implementation.").action(() => print(startRun(loadConfig(options().config))));
1245
1964
  program.command("verify").description("Execute every configured check and record evidence.").action(async () => print(await verifyRun({ configPath: options().config })));
1246
1965
  program.command("run").description("Alias for verify, compatible with the common protocol.").action(async () => print(await verifyRun({ configPath: options().config })));
@@ -1287,9 +2006,9 @@ var benchmark = program.command("benchmark").description("Aggregate reproducible
1287
2006
  benchmark.command("baseline <taskId>").description("Record one controlled baseline observation in a benchmark manifest.").option("--manifest <path>", "benchmark manifest path").requiredOption("--status <status>", "passed, failed, blocked, or not-run").requiredOption("--source <source>", "baseline source or run reference").option("--evidence-file <path>", "JSON file with criterion-level baseline evidence").option("--recorded-at <timestamp>", "ISO-8601 timestamp").option("--attempts <count>", "attempt count", (value) => Number(value)).option("--duration-ms <milliseconds>", "duration in milliseconds", (value) => Number(value)).option("--review-minutes <minutes>", "human review time in minutes", (value) => Number(value)).option("--escaped-incomplete <count>", "incomplete deliveries discovered after handoff", (value) => Number(value)).action((taskId, command, cliCommand) => {
1288
2007
  const manifest = command.manifest ?? cliCommand.parent?.opts().manifest;
1289
2008
  const manifestPath = manifest ?? fail("baseline requires --manifest <path>.", "INVALID_INPUT");
1290
- const status = ["passed", "failed", "blocked", "not-run"].includes(command.status) ? command.status : fail("status must be passed, failed, blocked, or not-run.", "INVALID_INPUT");
2009
+ const status2 = ["passed", "failed", "blocked", "not-run"].includes(command.status) ? command.status : fail("status must be passed, failed, blocked, or not-run.", "INVALID_INPUT");
1291
2010
  const evidence = command.evidenceFile ? readBenchmarkEvidence(command.evidenceFile) : void 0;
1292
- print(recordBenchmarkObservation(manifestPath, { taskId, status, source: command.source, ...evidence ? { evidence: evidence.evidence, evidenceDigest: evidence.digest } : {}, ...command.recordedAt ? { recordedAt: command.recordedAt } : {}, ...command.attempts === void 0 ? {} : { attempts: command.attempts }, ...command.durationMs === void 0 ? {} : { durationMs: command.durationMs }, ...command.reviewMinutes === void 0 ? {} : { reviewMinutes: command.reviewMinutes }, ...command.escapedIncomplete === void 0 ? {} : { escapedIncomplete: command.escapedIncomplete } }));
2011
+ print(recordBenchmarkObservation(manifestPath, { taskId, status: status2, source: command.source, ...evidence ? { evidence: evidence.evidence, evidenceDigest: evidence.digest } : {}, ...command.recordedAt ? { recordedAt: command.recordedAt } : {}, ...command.attempts === void 0 ? {} : { attempts: command.attempts }, ...command.durationMs === void 0 ? {} : { durationMs: command.durationMs }, ...command.reviewMinutes === void 0 ? {} : { reviewMinutes: command.reviewMinutes }, ...command.escapedIncomplete === void 0 ? {} : { escapedIncomplete: command.escapedIncomplete } }));
1293
2012
  });
1294
2013
  program.command("clean").description("Remove only configured task-owned temporary artifacts.").action(() => print(cleanTaskArtifacts(options().config)));
1295
2014
  process.on("SIGINT", () => {