@agentskit/harness 0.1.0 → 0.3.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.
package/dist/cli.js CHANGED
@@ -1,11 +1,11 @@
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, writeSync, appendFileSync, mkdtempSync, renameSync, rmSync, readdirSync } 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
10
  // src/constants.ts
11
11
  var STATES = [
@@ -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"],
@@ -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"]] : [];
@@ -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,7 +266,7 @@ 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
  };
@@ -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");
@@ -459,8 +486,7 @@ var saveRun2 = (stateDir, run) => {
459
486
  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
487
  }
461
488
  };
462
- var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized, contextSnapshots = [], planner = "human" }) => {
463
- const contractHash = hashJson(loaded.config.contract);
489
+ var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized, contextSnapshots = [] }) => {
464
490
  const run = {
465
491
  type: "agentskit-harness-run",
466
492
  schemaVersion: 1,
@@ -468,17 +494,18 @@ var createRun = async ({ loaded, baseline, supersedes, dirtyBaselineAuthorized,
468
494
  project: loaded.config.project,
469
495
  state: "PLANNED",
470
496
  configHash: loaded.configHash,
471
- contractHash,
497
+ contractHash: hashJson(loaded.config.contract),
472
498
  sourceRevision: baseline.revision,
473
499
  sourceStatusHash: baseline.statusHash,
474
500
  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" })),
501
+ autonomy: loaded.config.autonomy,
502
+ contractApproval: { actor: "human", at: now(), contractHash: hashJson(loaded.config.contract) },
503
+ checks: loaded.config.checks.map(({ id: id2, category: category2 }) => ({ id: id2, category: category2, status: "pending" })),
477
504
  contextSnapshots,
478
505
  ...contextSnapshots.length ? { contextHash: hashContextSnapshots(contextSnapshots) } : {},
479
506
  ...loaded.config.benchmark ? { benchmark: loaded.config.benchmark } : {},
480
507
  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 }],
508
+ transitions: [{ from: null, to: "PLANNED", at: now(), actor: "human" }],
482
509
  evidenceReferences: [],
483
510
  ...supersedes ? { supersedes } : {},
484
511
  ...dirtyBaselineAuthorized ? { dirtyBaselineAuthorized: true } : {}
@@ -531,22 +558,97 @@ var git = async (root, args) => {
531
558
  };
532
559
  var sourceSnapshot = async (root, stateDir) => {
533
560
  const revision = await git(root, ["rev-parse", "HEAD"]);
561
+ if (!revision) fail("Current-source evidence requires a Git repository with a committed HEAD.", "GIT_REQUIRED");
534
562
  const stateRelative = relative(root, stateDir).replaceAll("\\", "/");
535
563
  const pathspec = ["--", "."];
536
564
  if (stateRelative && stateRelative !== ".." && !stateRelative.startsWith("../")) pathspec.push(`:(exclude)${stateRelative}`);
537
- const status = await git(root, ["status", "--porcelain=v1", "--untracked-files=all", ...pathspec]);
565
+ const status2 = await git(root, ["status", "--porcelain=v1", "--untracked-files=all", ...pathspec]);
538
566
  const diff = await git(root, ["diff", "--no-ext-diff", "--binary", "HEAD", ...pathspec]);
539
567
  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 [];
568
+ const untracked = untrackedPaths.map((path) => ({ path, hash: sha256(readFileSync(resolve(root, path))) }));
569
+ const fingerprint = { revision, status: status2, diff, untracked };
570
+ return { revision, status: status2, statusHash: hashJson(fingerprint) };
571
+ };
572
+ var thresholds = (value = {}) => {
573
+ const result = { warningPercent: value.warningPercent ?? 75, criticalPercent: value.criticalPercent ?? 90 };
574
+ 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");
575
+ return result;
576
+ };
577
+ var linuxSwap = () => {
578
+ if (process.platform !== "linux" || !existsSync("/proc/meminfo")) return void 0;
579
+ const values = Object.fromEntries(readFileSync("/proc/meminfo", "utf8").split(/\r?\n/).flatMap((line) => {
580
+ const match = line.match(/^(SwapTotal|SwapFree):\s+(\d+)\s+kB$/);
581
+ return match ? [[match[1], Number(match[2])]] : [];
582
+ }));
583
+ if (!values["SwapTotal"]) return void 0;
584
+ return Number(((1 - (values["SwapFree"] ?? 0) / values["SwapTotal"]) * 100).toFixed(2));
585
+ };
586
+ var sampleMachine = () => {
587
+ const cpus$1 = Math.max(1, cpus().length);
588
+ const load1 = Math.max(0, loadavg()[0] ?? 0);
589
+ const memory = Math.max(0, Math.min(100, (1 - freemem() / Math.max(1, totalmem())) * 100));
590
+ const swapUsedPercent = linuxSwap();
591
+ return {
592
+ at: (/* @__PURE__ */ new Date()).toISOString(),
593
+ cpus: cpus$1,
594
+ load1: Number(load1.toFixed(4)),
595
+ load1PerCpuPercent: Number(Math.min(100, load1 / cpus$1 * 100).toFixed(2)),
596
+ memoryUsedPercent: Number(memory.toFixed(2)),
597
+ rssBytes: process.memoryUsage().rss,
598
+ ...swapUsedPercent === void 0 ? {} : { swapUsedPercent }
599
+ };
600
+ };
601
+ var summarizeMachine = (samples, sampleIntervalMs = 5e3, limits = {}) => {
602
+ const limit = thresholds(limits);
603
+ const load = samples.map((sample) => sample.load1PerCpuPercent);
604
+ const memory = samples.map((sample) => sample.memoryUsedPercent);
605
+ const rss = samples.map((sample) => sample.rssBytes);
606
+ return {
607
+ sampleIntervalMs,
608
+ samples,
609
+ peakLoad1PerCpuPercent: Number(Math.max(...load, 0).toFixed(2)),
610
+ peakMemoryUsedPercent: Number(Math.max(...memory, 0).toFixed(2)),
611
+ peakRssBytes: Math.max(...rss, 0),
612
+ pressureEvents: samples.filter((sample) => sample.load1PerCpuPercent >= limit.criticalPercent || sample.memoryUsedPercent >= limit.criticalPercent || (sample.swapUsedPercent ?? 0) >= limit.criticalPercent || sample.memoryPressure === "critical").length,
613
+ throttleEvents: 0,
614
+ minimumEffectiveConcurrency: 0
615
+ };
616
+ };
617
+ var adaptiveConcurrency = (configured, sample, limits = {}) => {
618
+ if (!Number.isInteger(configured) || configured < 1) throw new Error("configured concurrency must be a positive integer.");
619
+ const limit = thresholds(limits);
620
+ const critical = sample.load1PerCpuPercent >= limit.criticalPercent || sample.memoryUsedPercent >= limit.criticalPercent || (sample.swapUsedPercent ?? 0) >= limit.criticalPercent || sample.memoryPressure === "critical";
621
+ const warning = sample.load1PerCpuPercent >= limit.warningPercent || sample.memoryUsedPercent >= limit.warningPercent || (sample.swapUsedPercent ?? 0) >= limit.warningPercent || sample.memoryPressure === "warning";
622
+ if (critical) return 1;
623
+ if (warning) return Math.min(configured, 2);
624
+ return configured;
625
+ };
626
+ var createMachineMonitor = (sampleIntervalMs = 5e3, options2 = {}) => {
627
+ const sampler = options2.sample ?? sampleMachine;
628
+ const limits = thresholds(options2.thresholds);
629
+ const samples = [sampler()];
630
+ let throttleEvents = 0;
631
+ const effectiveConcurrency = [];
632
+ const record3 = () => {
633
+ const sample = sampler();
634
+ samples.push(sample);
635
+ return sample;
636
+ };
637
+ const timer = setInterval(record3, sampleIntervalMs);
638
+ timer.unref();
639
+ return {
640
+ sample: record3,
641
+ observeConcurrency: (value) => effectiveConcurrency.push(value),
642
+ markThrottle: () => {
643
+ throttleEvents += 1;
644
+ },
645
+ stop: () => {
646
+ clearInterval(timer);
647
+ record3();
648
+ const summary = summarizeMachine(samples, sampleIntervalMs, limits);
649
+ return { ...summary, throttleEvents, minimumEffectiveConcurrency: effectiveConcurrency.length ? Math.min(...effectiveConcurrency) : 0 };
546
650
  }
547
- });
548
- const fingerprint = { revision, status, diff, untracked };
549
- return { revision: revision || `content:${hashJson(fingerprint)}`, status, statusHash: hashJson(fingerprint) };
651
+ };
550
652
  };
551
653
 
552
654
  // src/verification.ts
@@ -575,6 +677,12 @@ var runCommand = (check, cwd) => new Promise((resolveResult) => {
575
677
  resolveResult({ exitCode: exitCode ?? 1, timedOut, stdout, stderr, durationMs: Date.now() - started });
576
678
  });
577
679
  });
680
+ var executeCheck = async (check, cwd, checkDir, outcomes) => {
681
+ const result = await runCommand(check, cwd);
682
+ const evidence = parseStructuredEvidence(result.stdout);
683
+ 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"];
684
+ 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 };
685
+ };
578
686
  var currentBinding = async (loaded) => ({ source: await sourceSnapshot(loaded.root, loaded.stateDir), configHash: loaded.configHash });
579
687
  var staleRun = (loaded, run, reason) => {
580
688
  const stale = transition(run, "STALE", reason);
@@ -587,11 +695,8 @@ var isFresh = async (loaded, run) => {
587
695
  return current.configHash === run.configHash && current.source.revision === run.sourceRevision && current.source.statusHash === run.sourceStatusHash;
588
696
  };
589
697
  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
- }
698
+ assertHuman(actor);
699
+ if (!approvedDecision(decision)) fail("Contract was not approved.", "CLARIFYING");
595
700
  const loaded = loadConfig(configPath);
596
701
  if (loaded.config.contract.ambiguities.length) fail(`Unresolved ambiguities remain: ${loaded.config.contract.ambiguities.join(" | ")}`, "CLARIFYING");
597
702
  const validatedContextSnapshots = validateContextSnapshots(contextSnapshots);
@@ -603,7 +708,7 @@ ${meaningful.join("\n")}
603
708
  Use --allow-dirty only with explicit human authorization.`, "WORKTREE_DIRTY");
604
709
  const previous = loadLatestRun(loaded.stateDir);
605
710
  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" });
711
+ return createRun({ loaded, baseline, supersedes: previous?.runId, dirtyBaselineAuthorized: allowDirty, contextSnapshots: validatedContextSnapshots });
607
712
  };
608
713
  var startRun = (loaded) => {
609
714
  const run = requireRun(loadLatestRun(loaded.stateDir));
@@ -623,42 +728,81 @@ var cancelRun = async ({ configPath, runId, reason = "Run cancelled by a human."
623
728
  };
624
729
  var verifyRun = async ({ configPath }) => {
625
730
  const loaded = loadConfig(configPath);
731
+ const machineMonitor = createMachineMonitor();
626
732
  const run = requireRun(loadLatestRun(loaded.stateDir));
627
733
  if (!["IMPLEMENTING", "VERIFYING"].includes(run.state)) {
628
734
  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
735
  fail(`Cannot verify from ${run.state}.`, "INVALID_STATE");
630
736
  }
631
737
  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 };
738
+ const binding2 = await currentBinding(loaded);
739
+ let current = { ...transition(run, "VERIFYING", "Verification started.", "agent"), sourceRevision: binding2.source.revision, sourceStatusHash: binding2.source.statusHash };
634
740
  saveRun2(loaded.stateDir, current);
635
741
  const checkDir = join(loaded.stateDir, "runs", current.runId, "checks");
636
742
  mkdirSync(checkDir, { recursive: true });
637
743
  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
744
  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);
745
+ let activeChecks = 0;
746
+ let observedPeakConcurrency = 0;
747
+ const verificationStarted = Date.now();
748
+ const maxConcurrency = loaded.config.verification?.maxConcurrency ?? 1;
749
+ const requiredChecks = loaded.config.checks.filter((check) => check.required);
750
+ for (const check of loaded.config.checks.filter((item) => !item.required)) {
751
+ const nextCheck = { id: check.id, category: check.category, status: "not-applicable", failures: [check.reason ?? "Not required by the selected profile."] };
752
+ current = { ...current, checks: current.checks.map((item) => item.id === check.id ? nextCheck : item) };
753
+ }
754
+ const remaining = new Set(requiredChecks.map((check) => check.id));
755
+ const completed = new Set(loaded.config.checks.filter((check) => !check.required).map((check) => check.id));
756
+ while (remaining.size) {
757
+ const ready = requiredChecks.filter((check) => remaining.has(check.id) && (check.dependsOn ?? []).every((dependency) => completed.has(dependency)));
758
+ if (!ready.length) fail("Check dependency graph contains an unknown dependency or cycle.", "INVALID_CONFIG");
759
+ const buildChecks = ready.filter((check) => check.category === "build");
760
+ const queue = buildChecks.length ? buildChecks : ready;
761
+ let offset = 0;
762
+ while (offset < queue.length) {
763
+ const effectiveConcurrency = buildChecks.length ? 1 : adaptiveConcurrency(maxConcurrency, machineMonitor.sample());
764
+ machineMonitor.observeConcurrency(effectiveConcurrency);
765
+ if (effectiveConcurrency < maxConcurrency) machineMonitor.markThrottle();
766
+ const batch = queue.slice(offset, offset + effectiveConcurrency);
767
+ const executed = await Promise.all(batch.map(async (check) => {
768
+ activeChecks += 1;
769
+ observedPeakConcurrency = Math.max(observedPeakConcurrency, activeChecks);
770
+ try {
771
+ return await executeCheck(check, loaded.root, checkDir, outcomesByCheck.get(check.id) ?? []);
772
+ } finally {
773
+ activeChecks -= 1;
774
+ }
775
+ }));
776
+ for (const item of executed) {
777
+ totalDurationMs += item.durationMs;
778
+ const stdoutPath = join(checkDir, `${item.check.id}.stdout`);
779
+ const stderrPath = join(checkDir, `${item.check.id}.stderr`);
780
+ writeFileSync(stdoutPath, item.stdout, "utf8");
781
+ writeFileSync(stderrPath, item.stderr, "utf8");
782
+ 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) };
783
+ }
784
+ saveRun2(loaded.stateDir, current);
785
+ for (const check of batch) {
786
+ remaining.delete(check.id);
787
+ completed.add(check.id);
788
+ }
789
+ offset += batch.length;
790
+ }
651
791
  }
652
792
  const statuses = new Map(current.checks.map((check) => [check.id, check.status]));
653
793
  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 };
794
+ const allPassed = loaded.config.checks.every((check) => !check.required || statuses.get(check.id) === "passed") && !budgetExceeded;
795
+ current = { ...current, outcomes: current.outcomes.map((outcome) => {
796
+ const required8 = outcome.checks.filter((id2) => loaded.config.checks.find((check) => check.id === id2)?.required);
797
+ return { ...outcome, status: required8.length === 0 ? "not-applicable" : required8.every((id2) => statuses.get(id2) === "passed") ? "passed" : "failed" };
798
+ }), metrics: { totalDurationMs, wallDurationMs: Date.now() - verificationStarted, peakConcurrency: observedPeakConcurrency, budgetExceeded, machine: machineMonitor.stop() } };
799
+ const digest3 = verificationDigest(current);
800
+ current = { ...current, verificationDigest: digest3 };
658
801
  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") };
802
+ new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest: digest3, checkCount: current.checks.length, outcomeCount: current.outcomes.length, totalDurationMs, budgetExceeded } });
803
+ const automatic = allPassed && current.autonomy === "yolo" && !loaded.config.tracking.required && loaded.config.contract.ambiguities.length === 0;
804
+ const nextState = allPassed ? automatic ? "COMPLETE" : "AWAITING_HUMAN_APPROVAL" : "BLOCKED";
805
+ 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
806
  saveRun2(loaded.stateDir, current);
663
807
  setLatest(loaded.stateDir, current);
664
808
  return current;
@@ -672,11 +816,6 @@ var assertVerificationAttestation = (loaded, run) => {
672
816
  const event = new FileEventStore(loaded.stateDir).read(run.runId).filter((item) => item.type === "verification.completed").at(-1);
673
817
  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
818
  };
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
819
  var recordDecision = (loaded, run, type, payload) => {
681
820
  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
821
  };
@@ -696,7 +835,7 @@ var reconcileRun = async ({ configPath, runId }) => {
696
835
  if (eventLog.status !== "verified") fail("Terminal run requires a verified event log.", "HARNESS_ERROR");
697
836
  assertVerificationAttestation(loaded, run);
698
837
  }
699
- if (run.state === "AWAITING_AUTHORIZATION" || run.state === "COMPLETE") {
838
+ if ((run.state === "AWAITING_AUTHORIZATION" || run.state === "COMPLETE") && run.autonomy !== "yolo") {
700
839
  const approval = events2.filter((event) => event.type === "approval.recorded").at(-1) ?? fail("Terminal run is missing its human approval event.", "HARNESS_ERROR");
701
840
  assertDecisionProjection(run, approval.payload, run.state === "COMPLETE" && !loaded.config.tracking.required ? "COMPLETE" : "AWAITING_AUTHORIZATION");
702
841
  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 +897,7 @@ var retryRun = async ({ configPath }) => {
758
897
  const baseline = await sourceSnapshot(loaded.root, loaded.stateDir);
759
898
  const superseded = transition(previousRun, "SUPERSEDED", "Retry superseded the previous run.", "harness");
760
899
  saveRun2(loaded.stateDir, superseded);
761
- const run = await createRun({ loaded, baseline, supersedes: previousRun.runId, dirtyBaselineAuthorized: previousRun.dirtyBaselineAuthorized, planner: plannerForRetry(loaded, previousRun) });
900
+ const run = await createRun({ loaded, baseline, supersedes: previousRun.runId, dirtyBaselineAuthorized: previousRun.dirtyBaselineAuthorized });
762
901
  const next = transition(run, "IMPLEMENTING", "Retry started after a previous attempt.", "agent");
763
902
  saveRun2(loaded.stateDir, next);
764
903
  setLatest(loaded.stateDir, next);
@@ -785,14 +924,305 @@ var createDocBridgeContextProvider = ({ root, indexPath = ".doc-bridge/index.jso
785
924
  return { providerId: "doc-bridge", query, references, sourceHash: contentHash, snapshotHash: hashContextSnapshot({ providerId: "doc-bridge", query, references, sourceHash: contentHash }), resolvedAt: (/* @__PURE__ */ new Date()).toISOString() };
786
925
  }
787
926
  });
927
+
928
+ // src/discovery.ts
929
+ var required = (value, label) => {
930
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
931
+ return value.trim();
932
+ };
933
+ var unique = (values, label) => {
934
+ if (new Set(values).size !== values.length) fail(`${label} must be unique.`, "INVALID_INPUT");
935
+ };
936
+ var validate = (input) => {
937
+ required(input.issueId, "issueId");
938
+ required(input.sourceRevision, "sourceRevision");
939
+ required(input.contractHash, "contractHash");
940
+ if (!Array.isArray(input.ambiguities)) fail("ambiguities must be an array.", "INVALID_INPUT");
941
+ unique(input.ambiguities.map((item) => required(item.id, "ambiguity.id")), "ambiguity ids");
942
+ const assumptions = /* @__PURE__ */ new Map();
943
+ for (const assumption of input.approvedAssumptions ?? []) {
944
+ const id2 = required(assumption.id, "assumption.id");
945
+ if (assumptions.has(id2)) fail("assumption ids must be unique.", "INVALID_INPUT");
946
+ assumptions.set(id2, { id: id2, policyId: required(assumption.policyId, "assumption.policyId"), resolution: required(assumption.resolution, "assumption.resolution") });
947
+ }
948
+ for (const ambiguity of input.ambiguities) {
949
+ required(ambiguity.question, "ambiguity.question");
950
+ if (typeof ambiguity.material !== "boolean") fail("ambiguity.material must be boolean.", "INVALID_INPUT");
951
+ if (!Array.isArray(ambiguity.options) || ambiguity.options.length < 2 || ambiguity.options.length > 4) fail("ambiguity.options must contain 2 to 4 options.", "INVALID_INPUT");
952
+ unique(ambiguity.options.map((option) => required(option.id, "option.id")), "option ids");
953
+ for (const option of ambiguity.options) {
954
+ required(option.summary, "option.summary");
955
+ required(option.impact, "option.impact");
956
+ }
957
+ if (!ambiguity.options.some((option) => option.id === ambiguity.recommendedOptionId)) fail("recommendedOptionId must identify an option.", "INVALID_INPUT");
958
+ if (!ambiguity.material && (!ambiguity.assumptionId || !assumptions.has(ambiguity.assumptionId))) fail("non-material ambiguity requires an approved assumption.", "INVALID_INPUT");
959
+ }
960
+ return { assumptions };
961
+ };
962
+ var digest2 = (result) => hashJson(result);
963
+ var assessDiscovery = (input) => {
964
+ const { assumptions } = validate(input);
965
+ const human = input.ambiguities.filter((ambiguity) => ambiguity.material);
966
+ const decisionLog = input.ambiguities.map((ambiguity) => {
967
+ if (ambiguity.material) return { ambiguityId: ambiguity.id, kind: "human-decision-required", detail: `Recommendation: ${ambiguity.recommendedOptionId}.` };
968
+ const assumption = assumptions.get(ambiguity.assumptionId);
969
+ return { ambiguityId: ambiguity.id, kind: "approved-assumption", detail: assumption.resolution, policyId: assumption.policyId };
970
+ });
971
+ const base = {
972
+ version: 1,
973
+ issueId: input.issueId,
974
+ sourceRevision: input.sourceRevision,
975
+ contractHash: input.contractHash,
976
+ ...input.contextHash ? { contextHash: input.contextHash } : {},
977
+ status: human.length ? "awaiting-decision" : "ready",
978
+ ...human.length ? { packet: {
979
+ issueId: input.issueId,
980
+ contractHash: input.contractHash,
981
+ sourceRevision: input.sourceRevision,
982
+ ...input.contextHash ? { contextHash: input.contextHash } : {},
983
+ decisions: human.map((ambiguity) => ({ id: ambiguity.id, question: ambiguity.question, options: ambiguity.options, recommendedOptionId: ambiguity.recommendedOptionId }))
984
+ } } : {},
985
+ decisionLog
986
+ };
987
+ return { ...base, digest: digest2(base) };
988
+ };
989
+
990
+ // src/wip.ts
991
+ var WIP_STATES = ["ready", "implementing", "blocked", "awaiting-decision", "awaiting-acceptance", "done", "cancelled"];
992
+ var terminal = /* @__PURE__ */ new Set(["done", "cancelled"]);
993
+ var required2 = (value, label) => {
994
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
995
+ return value.trim();
996
+ };
997
+ var assessWip = ({ entries, candidate, maxInFlight = 3 }) => {
998
+ if (!Array.isArray(entries)) fail("entries must be an array.", "INVALID_INPUT");
999
+ if (!Number.isInteger(maxInFlight) || maxInFlight < 1) fail("maxInFlight must be a positive integer.", "INVALID_INPUT");
1000
+ const candidateId = required2(candidate.issueId, "candidate.issueId");
1001
+ if (candidate.kind !== "new" && candidate.kind !== "resume") fail("candidate.kind must be new or resume.", "INVALID_INPUT");
1002
+ const ids = /* @__PURE__ */ new Set();
1003
+ const counts = Object.fromEntries(WIP_STATES.map((state) => [state, 0]));
1004
+ for (const entry of entries) {
1005
+ const id2 = required2(entry.issueId, "entry.issueId");
1006
+ if (ids.has(id2)) fail("entry issueIds must be unique.", "INVALID_INPUT");
1007
+ ids.add(id2);
1008
+ if (!WIP_STATES.includes(entry.state)) fail(`Unknown WIP state: ${entry.state}.`, "INVALID_INPUT");
1009
+ counts[entry.state] += 1;
1010
+ }
1011
+ const inFlight = entries.filter((entry) => !terminal.has(entry.state));
1012
+ const existing = entries.find((entry) => entry.issueId === candidateId);
1013
+ if (candidate.kind === "resume") {
1014
+ if (!existing || terminal.has(existing.state)) return { decision: "hold", inFlight, counts, reason: "A resume requires an existing non-terminal issue." };
1015
+ return { decision: "admit", inFlight, counts, reason: "A resume keeps its existing WIP reservation and takes priority over new work." };
1016
+ }
1017
+ if (existing) return { decision: "hold", inFlight, counts, reason: "A new admission cannot reuse an existing issue id." };
1018
+ if (inFlight.length >= maxInFlight) return { decision: "hold", inFlight, counts, reason: `WIP limit ${maxInFlight} reached; blocked and awaiting-human work still count.` };
1019
+ return { decision: "admit", inFlight, counts, reason: `WIP slot available (${inFlight.length}/${maxInFlight}).` };
1020
+ };
1021
+
1022
+ // src/experiment.ts
1023
+ var required3 = (value, label) => {
1024
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1025
+ return value.trim();
1026
+ };
1027
+ var comparable = (candidate, baseline) => {
1028
+ for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) {
1029
+ if (candidate[key] !== baseline[key]) fail(`Candidates must share ${key}.`, "INVALID_INPUT");
1030
+ }
1031
+ };
1032
+ var selectRuntime = (candidates) => {
1033
+ if (!Array.isArray(candidates) || candidates.length < 2) fail("At least two runtime candidates are required.", "INVALID_INPUT");
1034
+ const names = /* @__PURE__ */ new Set();
1035
+ for (const candidate of candidates) {
1036
+ const runtime = required3(candidate.runtime, "candidate.runtime");
1037
+ if (names.has(runtime)) fail("candidate.runtime values must be unique.", "INVALID_INPUT");
1038
+ names.add(runtime);
1039
+ for (const key of ["sourceRevision", "contractHash", "provider", "model", "configurationHash"]) required3(candidate[key], `candidate.${key}`);
1040
+ 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");
1041
+ comparable(candidate, candidates[0]);
1042
+ }
1043
+ const eligible = candidates.filter((candidate) => candidate.hardGatesPassed);
1044
+ if (!eligible.length) return { decision: "blocked", eligible, reason: "No runtime passed every hard gate." };
1045
+ 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];
1046
+ return { decision: "selected", selected, eligible, reason: "Selected by human minutes, duration, cost, then Orca tie-break." };
1047
+ };
1048
+
1049
+ // src/delivery.ts
1050
+ var required4 = (value, label) => {
1051
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1052
+ return value.trim();
1053
+ };
1054
+ var criteriaFor = (criteria, gate) => {
1055
+ if (!Array.isArray(criteria)) fail("criteria must be an array.", "INVALID_INPUT");
1056
+ const ids = /* @__PURE__ */ new Set();
1057
+ for (const criterion of criteria) {
1058
+ const id2 = required4(criterion.id, "criterion.id");
1059
+ if (ids.has(id2)) fail("criterion ids must be unique.", "INVALID_INPUT");
1060
+ ids.add(id2);
1061
+ if (!["G2", "G3", "G4", "G5"].includes(criterion.gate)) fail("criterion.gate is invalid.", "INVALID_INPUT");
1062
+ if (!["passed", "failed", "pending", "not-applicable"].includes(criterion.status)) fail("criterion.status is invalid.", "INVALID_INPUT");
1063
+ if (criterion.status === "not-applicable" && !criterion.reason?.trim()) fail("not-applicable criteria require a reason.", "INVALID_INPUT");
1064
+ }
1065
+ return criteria.filter((criterion) => criterion.gate === gate);
1066
+ };
1067
+ var binding = (value) => ({ candidateRevision: required4(value.candidateRevision, "binding.candidateRevision"), contractHash: required4(value.contractHash, "binding.contractHash"), configHash: required4(value.configHash, "binding.configHash") });
1068
+ var assessed = (gate, decision, reasons, current) => {
1069
+ const base = { gate, decision, reasons, binding: binding(current) };
1070
+ return { ...base, digest: hashJson(base) };
1071
+ };
1072
+ var assessPreflight = ({ criteria, repairAttempts = 0, implementerId, reviewerId, reviewKind, reviewApproved, binding: current }) => {
1073
+ required4(implementerId, "implementerId");
1074
+ if (!Number.isInteger(repairAttempts) || repairAttempts < 0) fail("repairAttempts must be a non-negative integer.", "INVALID_INPUT");
1075
+ const g2 = criteriaFor(criteria, "G2");
1076
+ const reasons = [
1077
+ ...g2.length ? [] : ["No G2 criteria are defined."],
1078
+ ...g2.filter((criterion) => criterion.status === "failed" || criterion.status === "pending").map((criterion) => `${criterion.id} is ${criterion.status}.`),
1079
+ ...reviewApproved && reviewerId && reviewerId !== implementerId && reviewKind === "adversarial" ? [] : ["An approved adversarial review by a reviewer different from the implementer is required."],
1080
+ ...repairAttempts <= 2 ? [] : ["The two-repair limit was exceeded; preserve diagnostics and return blocked."]
1081
+ ];
1082
+ return assessed("G2", reasons.length ? "blocked" : "approved", reasons, current);
1083
+ };
1084
+ var composePullRequest = ({ draft, g2, remote }) => {
1085
+ 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}`);
1086
+ 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) };
1087
+ const idempotencyKey = hashJson({ issueId: draft.issueId, contractHash: draft.contractHash, action: "pull-request", revision: draft.candidateRevision });
1088
+ if (remote?.state === "uncertain") return { decision: "blocked", reason: "Remote PR state is uncertain; reconcile before retrying.", idempotencyKey };
1089
+ if (remote?.state === "confirmed") {
1090
+ if (remote.candidateRevision !== draft.candidateRevision || !remote.url) return { decision: "blocked", reason: "Confirmed remote PR does not match the candidate revision.", idempotencyKey };
1091
+ return { decision: "reuse", reason: "The idempotent remote PR already exists for this candidate revision.", idempotencyKey };
1092
+ }
1093
+ 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");
1094
+ return { decision: "create", body: body2, reason: "G2 is current and the remote PR is absent.", idempotencyKey };
1095
+ };
1096
+ var assessIntegration = ({ g2, candidateRevision, evidenceRevision, contractHash, configHash, ci }) => {
1097
+ required4(candidateRevision, "candidateRevision");
1098
+ required4(evidenceRevision, "evidenceRevision");
1099
+ if (!["passed", "failed", "pending", "not-applicable"].includes(ci)) fail("ci is invalid.", "INVALID_INPUT");
1100
+ const reasons = [
1101
+ ...g2.gate === "G2" && g2.decision === "approved" ? [] : ["G2 is not approved."],
1102
+ ...g2.binding.candidateRevision === candidateRevision && g2.binding.contractHash === contractHash && g2.binding.configHash === configHash ? [] : ["G2 is not bound to the current candidate, contract, and configuration."],
1103
+ ...candidateRevision === evidenceRevision ? [] : ["Candidate revision changed; G3 evidence must be revalidated."],
1104
+ ...ci === "passed" ? [] : [`Integration CI is ${ci}.`]
1105
+ ];
1106
+ return assessed("G3", reasons.length ? "blocked" : "approved", reasons, { candidateRevision, contractHash, configHash });
1107
+ };
1108
+ var assessWorktreeCleanup = ({ branch, candidateRevision, contractHash, configHash, remoteBranchRevision, remotePr, integration }) => {
1109
+ required4(branch, "branch");
1110
+ required4(candidateRevision, "candidateRevision");
1111
+ if (remotePr === "uncertain") return { decision: "preserve", reason: "Remote PR state is uncertain; preserve the worktree for reconciliation." };
1112
+ if (remotePr !== "confirmed") return { decision: "preserve", reason: "No confirmed remote PR exists; preserve the worktree." };
1113
+ if (remoteBranchRevision !== candidateRevision) return { decision: "preserve", reason: "Remote branch SHA does not match the candidate revision." };
1114
+ if (integration.gate !== "G3" || integration.decision !== "approved") return { decision: "preserve", reason: "G3 is not approved." };
1115
+ 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." };
1116
+ return { decision: "clean", reason: "Remote branch, PR, and G3 evidence are confirmed for the candidate revision." };
1117
+ };
1118
+ 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.`]);
1119
+ var assessProduction = ({ profile, integration, artifact, isolated, acceptanceArtifact, lowRisk = true, observationMinutes, technicalPassed, evidence, containmentPreauthorized, containmentAction, linkedDefect }) => {
1120
+ required4(artifact, "artifact");
1121
+ if (!Number.isFinite(observationMinutes) || observationMinutes < 0) fail("observationMinutes must be non-negative.", "INVALID_INPUT");
1122
+ 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"));
1123
+ const reasons = [
1124
+ ...profileReasons(profile),
1125
+ ...integration.gate === "G3" && integration.decision === "approved" ? [] : ["G3 is not approved."],
1126
+ ...evidenceReasons,
1127
+ ...isolated || acceptanceArtifact === artifact ? [] : ["Exposure requires isolation or acceptance linked to this artifact version."],
1128
+ ...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."],
1129
+ ...lowRisk && observationMinutes < 15 ? ["Low-risk production validation requires a 15-minute observation window."] : []
1130
+ ];
1131
+ return assessed("G4", reasons.length ? "blocked" : "approved", reasons, integration.binding);
1132
+ };
1133
+ var assessAcceptance = ({ production, acceptanceRequired, accepted, notApplicableReason, materialChange }) => {
1134
+ const reasons = [
1135
+ ...production.gate === "G4" && production.decision === "approved" ? [] : ["G4 is not approved."],
1136
+ ...materialChange ? ["A material change invalidated acceptance; return to the affected gate."] : []
1137
+ ];
1138
+ if (reasons.length) return assessed("G5", "blocked", reasons, production.binding);
1139
+ if (acceptanceRequired && !accepted) return assessed("G5", "awaiting-acceptance", ["Business or UX acceptance is still required."], production.binding);
1140
+ if (!acceptanceRequired && !notApplicableReason?.trim()) return assessed("G5", "blocked", ["Acceptance marked not applicable requires a contractual reason."], production.binding);
1141
+ return assessed("G5", "approved", acceptanceRequired ? [] : [`Acceptance is not applicable: ${notApplicableReason}.`], production.binding);
1142
+ };
1143
+
1144
+ // src/pilot.ts
1145
+ var required5 = (value, label) => {
1146
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1147
+ return value.trim();
1148
+ };
1149
+ var assessPilot = (manifest) => {
1150
+ required5(manifest.policyHash, "policyHash");
1151
+ required5(manifest.baselineReference, "baselineReference");
1152
+ if (!Array.isArray(manifest.entries)) fail("entries must be an array.", "INVALID_INPUT");
1153
+ const ids = /* @__PURE__ */ new Set();
1154
+ const reasons = [];
1155
+ const included = [];
1156
+ for (const entry of manifest.entries) {
1157
+ const issueId = required5(entry.issueId, "entry.issueId");
1158
+ if (ids.has(issueId)) fail("entry issueIds must be unique; an issue cannot be substituted in the same pilot.", "INVALID_INPUT");
1159
+ ids.add(issueId);
1160
+ if (!["normal", "incident", "sensitive"].includes(entry.classification)) fail("entry.classification is invalid.", "INVALID_INPUT");
1161
+ if (!["included", "excluded", "aborted"].includes(entry.status)) fail("entry.status is invalid.", "INVALID_INPUT");
1162
+ if (entry.status !== "included" && !entry.reason?.trim()) reasons.push(`${issueId} is ${entry.status} without an auditable reason.`);
1163
+ if (entry.status === "included") {
1164
+ included.push(issueId);
1165
+ if (entry.classification !== "normal") reasons.push(`${issueId} is ${entry.classification}; only normal issues can enter the pilot.`);
1166
+ }
1167
+ }
1168
+ if (included.length !== 10) reasons.push(`Pilot requires exactly 10 included issues; found ${included.length}.`);
1169
+ const base = { decision: reasons.length ? "blocked" : "ready", included, reasons };
1170
+ return { ...base, digest: hashJson({ ...manifest, ...base }) };
1171
+ };
1172
+ var IMPROVEMENT_CYCLE_STEPS = ["adversarial-review", "g2-preflight", "baseline-record", "pilot-execution", "comparison"];
1173
+ var nonEmpty = (value, label) => {
1174
+ if (typeof value !== "string" || !value.trim()) return fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1175
+ return value.trim();
1176
+ };
1177
+ var validateMetrics = (metrics, index2) => {
1178
+ if (metrics === void 0) return void 0;
1179
+ for (const [key, value] of Object.entries(metrics)) {
1180
+ 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");
1181
+ }
1182
+ return metrics;
1183
+ };
1184
+ var validateIteration = (iteration, index2) => {
1185
+ if (typeof iteration !== "object" || iteration === null || Array.isArray(iteration)) return fail(`iterations[${index2}] must be an object.`, "INVALID_INPUT");
1186
+ if (!Number.isInteger(iteration.iteration) || iteration.iteration < 1) return fail(`iterations[${index2}].iteration must be a positive integer.`, "INVALID_INPUT");
1187
+ 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");
1188
+ iteration.steps.forEach((result, stepIndex) => {
1189
+ if (typeof result !== "object" || result === null || Array.isArray(result)) return fail(`iterations[${index2}].steps[${stepIndex}] must be an object.`, "INVALID_INPUT");
1190
+ if (result.step !== IMPROVEMENT_CYCLE_STEPS[stepIndex]) return fail(`iterations[${index2}].steps[${stepIndex}] must be ${IMPROVEMENT_CYCLE_STEPS[stepIndex]}.`, "INVALID_INPUT");
1191
+ if (!["passed", "failed", "blocked", "pending"].includes(result.status)) return fail(`iterations[${index2}].steps[${stepIndex}].status is invalid.`, "INVALID_INPUT");
1192
+ 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");
1193
+ });
1194
+ if (iteration.adjustment !== void 0) nonEmpty(iteration.adjustment, `iterations[${index2}].adjustment`);
1195
+ return { ...iteration, metrics: validateMetrics(iteration.metrics, index2) };
1196
+ };
1197
+ var assessImprovementCycle = (input) => {
1198
+ if (typeof input !== "object" || input === null || Array.isArray(input)) return fail("cycle input must be an object.", "INVALID_INPUT");
1199
+ const cycleId = nonEmpty(input.cycleId, "cycleId");
1200
+ if (!Number.isInteger(input.maxIterations) || input.maxIterations < 1) return fail("maxIterations must be a positive integer.", "INVALID_INPUT");
1201
+ if (!Array.isArray(input.iterations) || input.iterations.length < 1) return fail("iterations must be non-empty.", "INVALID_INPUT");
1202
+ if (input.iterations.length > input.maxIterations) return fail("iterations cannot exceed maxIterations.", "INVALID_INPUT");
1203
+ const iterations = input.iterations.map(validateIteration);
1204
+ iterations.forEach((iteration, index2) => {
1205
+ if (iteration.iteration !== index2 + 1) return fail("iterations must be sequential and start at 1.", "INVALID_INPUT");
1206
+ if (index2 > 0 && iterations[index2 - 1]?.steps.every((step) => step.status === "passed")) return fail("a completed cycle cannot have later iterations.", "INVALID_INPUT");
1207
+ if (index2 < iterations.length - 1 && !iteration.adjustment) return fail(`iterations[${index2}].adjustment is required before repeating.`, "INVALID_INPUT");
1208
+ });
1209
+ const matrix = iterations.map((iteration) => {
1210
+ const statuses = Object.fromEntries(iteration.steps.map((step) => [step.step, step.status]));
1211
+ const passedSteps = iteration.steps.filter((step) => step.status === "passed").length;
1212
+ 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 } : {} };
1213
+ });
1214
+ const latest = iterations[iterations.length - 1];
1215
+ const complete = latest.steps.every((step) => step.status === "passed");
1216
+ 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."];
1217
+ const decision = complete ? "complete" : iterations.length >= input.maxIterations || !latest.adjustment ? "blocked" : "repeat";
1218
+ const result = { type: "agentskit-harness-improvement-cycle", cycleId, decision, ...decision === "repeat" ? { nextIteration: latest.iteration + 1 } : {}, reasons, matrix };
1219
+ const digest3 = createHash("sha256").update(JSON.stringify(result)).digest("hex");
1220
+ return { ...result, digest: digest3 };
1221
+ };
788
1222
  var BENCHMARK_SCHEMA_VERSION = 1;
789
1223
  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
1224
  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";
1225
+ var improvementDirection = (rate) => rate === null ? "unavailable" : rate > 0 ? "improved" : rate < 0 ? "regressed" : "unchanged";
796
1226
  var count = (items, predicate) => items.filter(predicate).length;
797
1227
  var median = (values) => {
798
1228
  if (!values.length) return null;
@@ -807,25 +1237,13 @@ var reviewMinutes = (run) => {
807
1237
  const elapsed = Date.parse(run.humanApproval.at) - Date.parse(reviewStart);
808
1238
  return Number.isFinite(elapsed) && elapsed >= 0 ? Number((elapsed / 6e4).toFixed(2)) : void 0;
809
1239
  };
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
1240
  var projectRun = (run) => {
822
1241
  const checks = { total: run.checks.length, passed: count(run.checks, (check) => check.status === "passed"), failed: count(run.checks, (check) => check.status === "failed") };
823
1242
  const outcomes = { total: run.outcomes.length, passed: count(run.outcomes, (outcome) => outcome.status === "passed"), failed: count(run.outcomes, (outcome) => outcome.status === "failed") };
824
1243
  const evidence = { total: run.checks.length, attached: count(run.checks, (check) => check.evidence !== void 0) };
825
- const acceptanceRate = artifactAcceptanceRate(run);
826
1244
  const humanReviewMinutes = reviewMinutes(run);
827
1245
  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 } : {} };
1246
+ 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
1247
  };
830
1248
  var summarize = (runs) => {
831
1249
  const stateCounts = Object.fromEntries(RUN_STATES.map((state) => [state, count(runs, (run) => run.state === state)]));
@@ -836,14 +1254,6 @@ var summarize = (runs) => {
836
1254
  const evidenceTotal = runs.reduce((total, run) => total + run.evidence.total, 0);
837
1255
  const evidenceAttached = runs.reduce((total, run) => total + run.evidence.attached, 0);
838
1256
  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
1257
  const durations = runs.flatMap((run) => run.durationMs === void 0 ? [] : [run.durationMs]);
848
1258
  return {
849
1259
  totalRuns: runs.length,
@@ -854,12 +1264,6 @@ var summarize = (runs) => {
854
1264
  firstAttemptRuns: firstAttempts.length,
855
1265
  humanApprovedRuns: count(runs, (run) => run.humanApproved),
856
1266
  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
1267
  checkPassRate: percentage(checksPassed, checksTotal),
864
1268
  outcomePassRate: percentage(outcomesPassed, outcomesTotal),
865
1269
  evidenceCoverageRate: percentage(evidenceAttached, evidenceTotal),
@@ -912,68 +1316,11 @@ var nonNegativeInteger = (value, label) => {
912
1316
  if (result !== void 0 && !Number.isInteger(result)) return fail(`${label} must be an integer.`, "INVALID_CONFIG");
913
1317
  return result;
914
1318
  };
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
1319
  var timestamp = (value, label) => {
921
1320
  const result = nonEmptyString(value, label);
922
1321
  if (!Number.isFinite(Date.parse(result))) return fail(`${label} must be a valid timestamp.`, "INVALID_CONFIG");
923
1322
  return result;
924
1323
  };
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
1324
  var validateBenchmarkManifest = (value) => {
978
1325
  if (typeof value !== "object" || value === null || Array.isArray(value)) fail("benchmark manifest must be an object.", "INVALID_CONFIG");
979
1326
  const raw = value;
@@ -982,12 +1329,7 @@ var validateBenchmarkManifest = (value) => {
982
1329
  const tasks = rawTasks.map((item, index2) => {
983
1330
  if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`benchmark.tasks[${index2}] must be an object.`, "INVALID_CONFIG");
984
1331
  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 } };
1332
+ 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
1333
  });
992
1334
  if (new Set(tasks.map((task) => task.id)).size !== tasks.length) fail("benchmark task ids must be unique.", "INVALID_CONFIG");
993
1335
  const taskIds = new Set(tasks.map((task) => task.id));
@@ -995,17 +1337,13 @@ var validateBenchmarkManifest = (value) => {
995
1337
  const observations = rawObservations.map((item, index2) => {
996
1338
  if (typeof item !== "object" || item === null || Array.isArray(item)) fail(`benchmark.observations[${index2}] must be an object.`, "INVALID_CONFIG");
997
1339
  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");
1340
+ const status2 = observation["status"];
1341
+ if (!["passed", "failed", "blocked", "not-run"].includes(String(status2))) fail(`benchmark.observations[${index2}].status is invalid.`, "INVALID_CONFIG");
1000
1342
  const taskId = nonEmptyString(observation["taskId"], `benchmark.observations[${index2}].taskId`);
1001
1343
  if (!taskIds.has(taskId)) fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
1002
1344
  const task = tasks.find((candidate) => candidate.id === taskId) ?? fail(`benchmark observation references unknown task: ${taskId}.`, "INVALID_CONFIG");
1003
1345
  const attempts = nonNegativeInteger(observation["attempts"], `benchmark.observations[${index2}].attempts`);
1004
1346
  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
1347
  const reviewMinutes2 = nonNegativeNumber(observation["reviewMinutes"], `benchmark.observations[${index2}].reviewMinutes`);
1010
1348
  const escapedIncomplete = nonNegativeInteger(observation["escapedIncomplete"], `benchmark.observations[${index2}].escapedIncomplete`);
1011
1349
  const evidenceDigest = sha2562(observation["evidenceDigest"], `benchmark.observations[${index2}].evidenceDigest`);
@@ -1020,12 +1358,10 @@ var validateBenchmarkManifest = (value) => {
1020
1358
  return { criterion, status: evidenceStatus, source: nonEmptyString(entry["source"], `benchmark.observations[${index2}].evidence[${evidenceIndex}].source`) };
1021
1359
  });
1022
1360
  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 } };
1361
+ 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
1362
  });
1025
1363
  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 } };
1364
+ 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
1365
  };
1030
1366
  var loadBenchmarkManifest = (path) => {
1031
1367
  try {
@@ -1051,9 +1387,6 @@ var recordBenchmarkObservation = (path, input) => {
1051
1387
  recordedAt: input.recordedAt ?? (/* @__PURE__ */ new Date()).toISOString(),
1052
1388
  ...input.attempts === void 0 ? {} : { attempts: input.attempts },
1053
1389
  ...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
1390
  ...input.reviewMinutes === void 0 ? {} : { reviewMinutes: input.reviewMinutes },
1058
1391
  ...input.escapedIncomplete === void 0 ? {} : { escapedIncomplete: input.escapedIncomplete },
1059
1392
  ...input.evidence === void 0 ? {} : { evidence: input.evidence },
@@ -1072,57 +1405,261 @@ var recordBenchmarkObservation = (path, input) => {
1072
1405
  }
1073
1406
  return observation;
1074
1407
  };
1075
- var comparisons = (runs, manifest, policy) => manifest.tasks.map((task) => {
1408
+ var comparisons = (runs, manifest) => manifest.tasks.map((task) => {
1076
1409
  const taskRuns = runs.filter((run) => run.benchmark?.suiteId === manifest.suiteId && run.benchmark.taskId === task.id);
1077
1410
  const latest = taskRuns.at(-1);
1078
1411
  const baseline = manifest.observations.find((observation) => observation.taskId === task.id);
1079
1412
  const coveredCriteria = new Set((baseline?.evidence ?? []).map((entry) => entry.criterion));
1080
1413
  const baselineEvidenceCoverageRate = baseline ? percentage(coveredCriteria.size, task.acceptanceCriteria.length) : null;
1081
1414
  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 } : {} };
1415
+ const comparable2 = baseline !== void 0 && baseline.status !== "not-run" && baselineEvidenceComplete && latest?.state === "COMPLETE";
1416
+ 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";
1417
+ const durationRate = comparable2 ? improvementRate(baseline?.durationMs, latest?.durationMs) : null;
1418
+ const attemptsRate = comparable2 ? improvementRate(baseline?.attempts, taskRuns.length) : null;
1419
+ const reviewRate = comparable2 ? improvementRate(baseline?.reviewMinutes, latest?.humanReviewMinutes) : null;
1420
+ const escapedIncompleteRate = comparable2 ? improvementRate(baseline?.escapedIncomplete, latest?.escapedIncomplete) : null;
1421
+ 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
1422
  });
1108
1423
  var benchmarkRuns = (stateDir, manifest) => {
1109
1424
  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 } } : {} };
1425
+ const reportComparisons = manifest ? comparisons(runs, manifest) : [];
1426
+ 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 } } : {} };
1427
+ };
1428
+ var required6 = (value, label) => {
1429
+ if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
1430
+ return value.trim();
1431
+ };
1432
+ var safeKey = (identity) => hashJson(identity);
1433
+ var now3 = () => (/* @__PURE__ */ new Date()).toISOString();
1434
+ var parse = (value, label) => {
1435
+ try {
1436
+ const raw = JSON.parse(value);
1437
+ const identity = {
1438
+ tracker: required6(raw["tracker"], `${label}.tracker`),
1439
+ repository: required6(raw["repository"], `${label}.repository`),
1440
+ issue: required6(raw["issue"], `${label}.issue`),
1441
+ worktree: required6(raw["worktree"], `${label}.worktree`),
1442
+ branch: required6(raw["branch"], `${label}.branch`)
1443
+ };
1444
+ 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`) };
1445
+ } catch (error) {
1446
+ if (error instanceof SyntaxError) fail(`${label} contains invalid JSON.`, "HARNESS_ERROR");
1447
+ throw error;
1448
+ }
1449
+ };
1450
+ var createDispatchLedger = (stateDir) => {
1451
+ const root = required6(stateDir, "stateDir");
1452
+ const claimsDir = join(root, "coordination", "claims");
1453
+ const ledgerPath = join(root, "coordination", "dispatch-ledger.ndjson");
1454
+ mkdirSync(claimsDir, { recursive: true });
1455
+ const claimPath = (key) => join(claimsDir, `${key}.json`);
1456
+ const append = (record3) => appendFileSync(ledgerPath, `${JSON.stringify(record3)}
1457
+ `, "utf8");
1458
+ const records = () => {
1459
+ if (!existsSync(ledgerPath)) return [];
1460
+ return readFileSync(ledgerPath, "utf8").split(/\r?\n/).map((line) => line.trim()).filter(Boolean).map((line, index2) => {
1461
+ try {
1462
+ return JSON.parse(line);
1463
+ } catch {
1464
+ return fail(`Dispatch ledger record ${index2 + 1} is invalid JSON.`, "HARNESS_ERROR");
1465
+ }
1466
+ });
1467
+ };
1468
+ const active = () => {
1469
+ const byKey = /* @__PURE__ */ new Map();
1470
+ for (const record3 of records()) {
1471
+ if (record3.action === "release" || record3.action === "recover") byKey.delete(record3.key);
1472
+ else if (record3.action === "dispatch") byKey.set(record3.key, record3);
1473
+ }
1474
+ return [...byKey.values()];
1475
+ };
1476
+ return {
1477
+ claim: (input) => {
1478
+ const identity = {
1479
+ tracker: required6(input.tracker, "tracker"),
1480
+ repository: required6(input.repository, "repository"),
1481
+ issue: required6(input.issue, "issue"),
1482
+ worktree: required6(input.worktree, "worktree"),
1483
+ branch: required6(input.branch, "branch")
1484
+ };
1485
+ const owner = required6(input.owner, "owner");
1486
+ const key = safeKey(identity);
1487
+ const path = claimPath(key);
1488
+ if (existsSync(path)) return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
1489
+ const lease = { ...identity, key, leaseId: randomUUID(), owner, claimedAt: now3() };
1490
+ let fd;
1491
+ try {
1492
+ fd = openSync(path, "wx");
1493
+ } catch (error) {
1494
+ if (error.code === "EEXIST") return { decision: "already-claimed", lease: parse(readFileSync(path, "utf8"), "claim") };
1495
+ throw error;
1496
+ }
1497
+ try {
1498
+ writeFileSync(fd, JSON.stringify(lease), "utf8");
1499
+ } finally {
1500
+ closeSync(fd);
1501
+ }
1502
+ append({ ...lease, action: "dispatch", at: lease.claimedAt });
1503
+ return { decision: "claimed", lease };
1504
+ },
1505
+ recordDispatch: ({ lease, idempotencyKey, commandDigest }) => {
1506
+ const id2 = required6(idempotencyKey, "idempotencyKey");
1507
+ const digest3 = required6(commandDigest, "commandDigest");
1508
+ const existing = records().find((record4) => record4.action === "dispatch" && record4.idempotencyKey === id2);
1509
+ if (existing) return { decision: "duplicate", record: existing };
1510
+ const record3 = { ...lease, action: "dispatch", at: now3(), idempotencyKey: id2, commandDigest: digest3 };
1511
+ append(record3);
1512
+ return { decision: "recorded", record: record3 };
1513
+ },
1514
+ release: (lease, reason = "lease released") => {
1515
+ const path = claimPath(required6(lease.key, "lease.key"));
1516
+ if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
1517
+ const current = parse(readFileSync(path, "utf8"), "claim");
1518
+ if (current.leaseId !== lease.leaseId) fail("Dispatch lease owner does not match.", "INVALID_STATE");
1519
+ unlinkSync(path);
1520
+ const record3 = { ...current, action: "release", at: now3(), reason: required6(reason, "reason") };
1521
+ append(record3);
1522
+ return record3;
1523
+ },
1524
+ recover: (key, input) => {
1525
+ if (input.actor !== "human") fail("Dispatch lease recovery requires a human actor.", "HUMAN_APPROVAL_REQUIRED");
1526
+ const normalizedKey = required6(key, "key");
1527
+ const maxAgeMs = input.maxAgeMs ?? 3e5;
1528
+ if (!Number.isInteger(maxAgeMs) || maxAgeMs < 0) fail("maxAgeMs must be a non-negative integer.", "INVALID_INPUT");
1529
+ const path = claimPath(normalizedKey);
1530
+ if (!existsSync(path)) fail("Dispatch lease is not active.", "INVALID_STATE");
1531
+ const current = parse(readFileSync(path, "utf8"), "claim");
1532
+ if (Date.now() - Date.parse(current.claimedAt) < maxAgeMs) fail("Dispatch lease is not old enough to recover.", "HARNESS_ERROR");
1533
+ unlinkSync(path);
1534
+ const record3 = { ...current, action: "recover", at: now3(), reason: required6(input.reason, "reason") };
1535
+ append(record3);
1536
+ return record3;
1537
+ },
1538
+ active,
1539
+ records
1540
+ };
1541
+ };
1542
+ var DOC_EXTENSIONS = /* @__PURE__ */ new Set([".md", ".mdx", ".txt", ".adoc", ".rst"]);
1543
+ var TEST_SUFFIXES = [".test.", ".spec.", "__tests__"];
1544
+ var normalizedPath = (value, label) => {
1545
+ if (typeof value !== "string" || !value.trim()) fail(`${label} must be a non-empty path.`, "INVALID_INPUT");
1546
+ const path = value.trim().replaceAll("\\", "/");
1547
+ if (path.startsWith("/") || path.split("/").includes("..")) fail(`${label} must be repository-relative.`, "INVALID_INPUT");
1548
+ return path;
1549
+ };
1550
+ var isTest = (path) => TEST_SUFFIXES.some((suffix) => path.includes(suffix)) || /(^|\/)(test|tests|__tests__)\//.test(path);
1551
+ var isDoc = (path) => DOC_EXTENSIONS.has(extname(path).toLowerCase());
1552
+ var planFilePreflight = (files, options2 = {}) => {
1553
+ if (!Array.isArray(files)) fail("files must be an array.", "INVALID_INPUT");
1554
+ const unique2 = [...new Set(files.map((file, index2) => normalizedPath(file.path, `files[${index2}].path`)))].sort();
1555
+ const codeFiles = unique2.filter((path) => !isDoc(path) && !isTest(path));
1556
+ const existingTests = unique2.filter(isTest);
1557
+ const roots = (options2.testRoots ?? ["test", "tests", "__tests__"]).map((root, index2) => normalizedPath(root, `testRoots[${index2}]`));
1558
+ const colocated = options2.includeTests === false ? [] : codeFiles.flatMap((path) => {
1559
+ const file = basename(path);
1560
+ const directory = dirname(path);
1561
+ const stem = file.includes(".") ? file.slice(0, file.lastIndexOf(".")) : file;
1562
+ return [join(directory, `${stem}.test.ts`), join(directory, `${stem}.spec.ts`)].filter((candidate) => unique2.includes(candidate));
1563
+ });
1564
+ const testFiles = [...new Set([...existingTests, ...colocated, ...unique2.filter((path) => roots.some((root) => path === root || path.startsWith(`${root}/`)))].sort())];
1565
+ const docsOnly = unique2.length > 0 && codeFiles.length === 0 && existingTests.length === 0;
1566
+ return { files: unique2, codeFiles, testFiles, docsOnly, checks: docsOnly ? [] : ["lint", "typecheck", ...testFiles.length ? ["test"] : []] };
1567
+ };
1568
+
1569
+ // src/block.ts
1570
+ var BLOCK_STATUSES = ["todo", "picked", "development", "validation", "pr-open", "merged", "post-merge", "done", "blocked", "scope-cut"];
1571
+ var text2 = (value, label) => {
1572
+ return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1573
+ };
1574
+ var list = (value, label) => {
1575
+ if (!Array.isArray(value)) return fail(`${label} must be an array of non-empty strings.`, "INVALID_INPUT");
1576
+ if (!value.every((item) => typeof item === "string" && Boolean(item.trim()))) return fail(`${label} must be an array of non-empty strings.`, "INVALID_INPUT");
1577
+ return [...new Set(value.map((item) => item.trim()))];
1578
+ };
1579
+ var validateBlockManifest = (value) => {
1580
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("block manifest must be an object.", "INVALID_INPUT");
1581
+ const raw = value;
1582
+ if (raw["schemaVersion"] !== 1) fail("block manifest schemaVersion must be 1.", "INVALID_INPUT");
1583
+ const criteria = list(raw["acceptanceCriteria"], "acceptanceCriteria");
1584
+ if (!criteria.length) fail("acceptanceCriteria must not be empty.", "INVALID_INPUT");
1585
+ const dependencies = list(raw["dependencies"] ?? [], "dependencies");
1586
+ const wave = raw["wave"];
1587
+ if (!Number.isInteger(wave) || wave < 1) fail("wave must be a positive integer.", "INVALID_INPUT");
1588
+ const status2 = raw["status"];
1589
+ if (!BLOCK_STATUSES.includes(status2)) fail("status is invalid.", "INVALID_INPUT");
1590
+ const budgetRaw = raw["budget"];
1591
+ let budget;
1592
+ if (budgetRaw !== void 0) {
1593
+ if (typeof budgetRaw !== "object" || budgetRaw === null || Array.isArray(budgetRaw)) fail("budget must be an object.", "INVALID_INPUT");
1594
+ const candidate = budgetRaw;
1595
+ 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");
1596
+ budget = { ...candidate["maxMinutes"] === void 0 ? {} : { maxMinutes: candidate["maxMinutes"] }, ...candidate["maxAttempts"] === void 0 ? {} : { maxAttempts: candidate["maxAttempts"] } };
1597
+ }
1598
+ return { schemaVersion: 1, id: text2(raw["id"], "id"), title: text2(raw["title"], "title"), tracker: text2(raw["tracker"], "tracker"), repository: text2(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: text2(raw["sourceHash"], "sourceHash") } };
1599
+ };
1600
+ var assessBlock = (manifest, completedDependencies = []) => {
1601
+ const value = validateBlockManifest(manifest);
1602
+ const completed = new Set(completedDependencies.map((item) => text2(item, "completedDependencies[]")));
1603
+ const blockers = value.dependencies.filter((dependency) => !completed.has(dependency));
1604
+ 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."];
1605
+ return { status: blockers.length || value.status === "blocked" ? "blocked" : "ready", manifestHash: hashJson(value), blockers, next };
1606
+ };
1607
+ var text3 = (value, label) => {
1608
+ return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1609
+ };
1610
+ var category = (heading) => {
1611
+ const value = heading.toLowerCase();
1612
+ if (/went well|success|worked/.test(value)) return "worked";
1613
+ if (/problem|failed|blocker|pain/.test(value)) return "problem";
1614
+ if (/adjust|action|next|improv/.test(value)) return "adjustment";
1615
+ return "other";
1616
+ };
1617
+ var parseRetro = (markdown, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) => {
1618
+ const input = text3(markdown, "markdown");
1619
+ const origin = text3(source, "source");
1620
+ if (!Number.isFinite(Date.parse(recordedAt))) fail("recordedAt must be a valid timestamp.", "INVALID_INPUT");
1621
+ const records = [];
1622
+ let current = "other";
1623
+ for (const line of input.split(/\r?\n/)) {
1624
+ const heading = line.match(/^#{1,6}\s+(.+)$/);
1625
+ if (heading) {
1626
+ current = category(heading[1] ?? "");
1627
+ continue;
1628
+ }
1629
+ const item = line.match(/^\s*[-*]\s+(?:\[[ xX]\]\s+)?(.+?)\s*$/);
1630
+ if (!item?.[1]?.trim()) continue;
1631
+ const value = item[1].trim();
1632
+ const id2 = `L-${createHash("sha256").update(`${origin}|${current}|${value}`).digest("hex").slice(0, 12)}`;
1633
+ if (!records.some((record3) => record3.id === id2)) records.push({ id: id2, source: origin, category: current, text: value, status: "proposed", recordedAt });
1634
+ }
1635
+ return records;
1636
+ };
1637
+
1638
+ // src/status.ts
1639
+ var required7 = (value, label) => {
1640
+ return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
1641
+ };
1642
+ var createStatusSnapshot = (input) => {
1643
+ const sourceRevision = required7(input.sourceRevision, "sourceRevision");
1644
+ if (!Number.isFinite(Date.parse(input.generatedAt))) fail("generatedAt must be a valid timestamp.", "INVALID_INPUT");
1645
+ if (!Array.isArray(input.blocks)) fail("blocks must be an array.", "INVALID_INPUT");
1646
+ const blocks = input.blocks.map((block2, index2) => {
1647
+ if (typeof block2 !== "object" || block2 === null || Array.isArray(block2)) fail(`blocks[${index2}] must be an object.`, "INVALID_INPUT");
1648
+ const value = block2;
1649
+ if (!(typeof value.id === "string" && value.id.trim())) fail(`blocks[${index2}].id is required.`, "INVALID_INPUT");
1650
+ 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");
1651
+ return { ...value, id: value.id.trim() };
1652
+ }).sort((left, right) => left.id.localeCompare(right.id));
1653
+ 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");
1654
+ 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") } : {} };
1655
+ return { ...body2, digest: hashJson(body2) };
1656
+ };
1657
+ var validateStatusSnapshot = (value) => {
1658
+ if (typeof value !== "object" || value === null || Array.isArray(value)) fail("status snapshot must be an object.", "INVALID_INPUT");
1659
+ const raw = value;
1660
+ 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 } : {} });
1661
+ if (raw.schemaVersion !== 1 || raw.digest !== snapshot.digest) fail("status snapshot digest or schemaVersion is invalid.", "HARNESS_ERROR");
1662
+ return snapshot;
1126
1663
  };
1127
1664
  var EVIDENCE_BUNDLE_SCHEMA_VERSION = 1;
1128
1665
  var body = (bundle) => {
@@ -1150,7 +1687,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
1150
1687
  const loaded = loadConfig(configPath);
1151
1688
  const run = requireRun2(runId ? readJson(join(loaded.stateDir, "runs", runId, "run.json")) : loadLatestRun(loaded.stateDir));
1152
1689
  const reconciliation = await reconcileRun({ configPath, runId: run.runId });
1153
- const digest2 = run.verificationDigest ?? fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
1690
+ const digest3 = run.verificationDigest ?? fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
1154
1691
  if (reconciliation.state !== "COMPLETE") fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
1155
1692
  const eventLog = new FileEventStore(loaded.stateDir);
1156
1693
  eventLog.read(run.runId);
@@ -1165,7 +1702,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
1165
1702
  return bundleFile(loaded.stateDir, path);
1166
1703
  });
1167
1704
  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 };
1705
+ 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: digest3, eventLog: eventVerification, files };
1169
1706
  const payloadHash = sha256(JSON.stringify(unsigned));
1170
1707
  const publicKeyPem = createPublicKey(privateKey).export({ type: "spki", format: "pem" }).toString();
1171
1708
  const bundle = { ...unsigned, payloadHash, signature: { algorithm: "ed25519", keyId, publicKeyPem, signatureBase64: sign(null, Buffer.from(payloadHash), privateKey).toString("base64") } };
@@ -1233,14 +1770,52 @@ var decisionArgs = (first, second) => {
1233
1770
  const decisions = /* @__PURE__ */ new Set(["approved", "approve", "yes", "ok", "rejected", "reject", "no"]);
1234
1771
  return decisions.has(first) ? { decision: first, ...second ? { runId: second } : {} } : { decision: second ?? "", runId: first };
1235
1772
  };
1773
+ var readJsonInput = (path, label) => {
1774
+ try {
1775
+ return JSON.parse(readFileSync(path, "utf8"));
1776
+ } catch (error) {
1777
+ return fail(`Invalid ${label} JSON: ${error instanceof Error ? error.message : String(error)}`, "INVALID_INPUT");
1778
+ }
1779
+ };
1236
1780
  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) : [] })));
1781
+ 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
1782
  var context = program.command("context").description("Resolve portable, provenance-bearing context snapshots.");
1239
1783
  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
1784
  if (command.provider !== "doc-bridge") fail(`Unsupported context provider: ${command.provider}`, "INVALID_INPUT");
1241
1785
  const loaded = loadConfig(options().config);
1242
1786
  print(await createDocBridgeContextProvider({ root: loaded.root, indexPath: command.index }).resolve({ query, ...command.scope?.length ? { scope: command.scope } : {} }));
1243
1787
  });
1788
+ var discovery = program.command("discovery").description("Assess a structured discovery result before implementation.");
1789
+ 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"))));
1790
+ var wip = program.command("wip").description("Assess deterministic WIP admission before starting work.");
1791
+ wip.command("assess <input>").description("Emit an admission decision from WIP ledger JSON.").action((input) => print(assessWip(readJsonInput(input, "WIP input"))));
1792
+ var experiment = program.command("experiment").description("Select a runtime only from a controlled, comparable experiment.");
1793
+ experiment.command("select <input>").description("Select the eligible runtime from experiment JSON.").action((input) => print(selectRuntime(readJsonInput(input, "experiment input"))));
1794
+ var delivery = program.command("delivery").description("Assess deterministic G2\u2013G5 gates and prepare idempotent PR handoff.");
1795
+ delivery.command("preflight <input>").action((input) => print(assessPreflight(readJsonInput(input, "preflight input"))));
1796
+ delivery.command("pr <input>").action((input) => print(composePullRequest(readJsonInput(input, "PR input"))));
1797
+ delivery.command("integration <input>").action((input) => print(assessIntegration(readJsonInput(input, "integration input"))));
1798
+ delivery.command("production <input>").action((input) => print(assessProduction(readJsonInput(input, "production input"))));
1799
+ delivery.command("acceptance <input>").action((input) => print(assessAcceptance(readJsonInput(input, "acceptance input"))));
1800
+ delivery.command("cleanup <input>").action((input) => print(assessWorktreeCleanup(readJsonInput(input, "cleanup input"))));
1801
+ program.command("pilot <input>").description("Freeze and assess a ten-issue pilot cohort.").action((input) => print(assessPilot(readJsonInput(input, "pilot input"))));
1802
+ var cycle = program.command("cycle").description("Run the five-step improvement cycle with explicit adjustment and bounded repetition.");
1803
+ 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"))));
1804
+ var block = program.command("block").description("Validate and assess a portable execution block manifest.");
1805
+ block.command("validate <input>").action((input) => print(validateBlockManifest(readJsonInput(input, "block manifest"))));
1806
+ block.command("assess <input>").option("--completed <ids...>", "completed dependency IDs").action((input, command) => print(assessBlock(readJsonInput(input, "block manifest"), command.completed ?? [])));
1807
+ var preflight = program.command("preflight").description("Plan safe, file-scoped validation before commit.");
1808
+ preflight.command("files <input>").action((input) => print(planFilePreflight(readJsonInput(input, "changed files"))));
1809
+ var status = program.command("snapshot <input>").description("Create or validate a deterministic status snapshot.");
1810
+ status.action((input) => print(createStatusSnapshot(readJsonInput(input, "status input"))));
1811
+ status.command("validate <input>").action((input) => print(validateStatusSnapshot(readJsonInput(input, "status snapshot"))));
1812
+ var learning = program.command("learning").description("Parse retrospectives into proposed learnings.");
1813
+ learning.command("parse <input>").requiredOption("--source <source>").action((input, command) => print(parseRetro(readFileSync(input, "utf8"), command.source)));
1814
+ var coordination = program.command("coordination").description("Manage idempotent issue/worktree claims and dispatch records.");
1815
+ coordination.command("claim <input>").action((input) => {
1816
+ const loaded = loadConfig(options().config);
1817
+ print(createDispatchLedger(loaded.stateDir).claim(readJsonInput(input, "coordination identity")));
1818
+ });
1244
1819
  program.command("start").description("Move a planned run into implementation.").action(() => print(startRun(loadConfig(options().config))));
1245
1820
  program.command("verify").description("Execute every configured check and record evidence.").action(async () => print(await verifyRun({ configPath: options().config })));
1246
1821
  program.command("run").description("Alias for verify, compatible with the common protocol.").action(async () => print(await verifyRun({ configPath: options().config })));
@@ -1287,9 +1862,9 @@ var benchmark = program.command("benchmark").description("Aggregate reproducible
1287
1862
  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
1863
  const manifest = command.manifest ?? cliCommand.parent?.opts().manifest;
1289
1864
  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");
1865
+ 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
1866
  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 } }));
1867
+ 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
1868
  });
1294
1869
  program.command("clean").description("Remove only configured task-owned temporary artifacts.").action(() => print(cleanTaskArtifacts(options().config)));
1295
1870
  process.on("SIGINT", () => {