@agentskit/harness 0.3.0 → 0.4.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +7 -0
- package/README.md +77 -2
- package/capabilities/public-surface.json +668 -0
- package/compatibility/manifest.json +17 -0
- package/compatibility/migration.md +10 -0
- package/compatibility/report.json +23 -0
- package/compatibility/report.md +22 -0
- package/compatibility/rollback.md +8 -0
- package/dist/cli.js +185 -41
- package/dist/cli.js.map +1 -1
- package/dist/index.d.ts +633 -35
- package/dist/index.js +1344 -300
- package/dist/index.js.map +1 -1
- package/docs/ADR-0026-kernel-adapters-boundary.md +82 -0
- package/docs/GETTING-STARTED.md +18 -0
- package/docs/MODULE-BOUNDARIES.md +143 -0
- package/docs/ORGANIZATION.md +13 -4
- package/docs/TROUBLESHOOTING.md +24 -0
- package/examples/minimum-profile.mjs +27 -0
- package/package.json +13 -2
- package/release/manifest.json +14 -0
- package/release/notes.md +10 -0
- package/release/qualification.json +14 -0
|
@@ -0,0 +1,23 @@
|
|
|
1
|
+
{
|
|
2
|
+
"type": "agentskit-harness-compatibility-report",
|
|
3
|
+
"schemaVersion": 1,
|
|
4
|
+
"harnessVersion": "0.4.0",
|
|
5
|
+
"harnessSourceRevision": "a18b57d43f037b7a34b134e01baf913ae2341fd7",
|
|
6
|
+
"capturedAt": "2026-09-10T18:30:00Z",
|
|
7
|
+
"status": "blocked",
|
|
8
|
+
"components": [
|
|
9
|
+
{ "id": "core", "package": "@agentskit/core", "version": "1.12.9", "revision": "d89a2d74108c29f2c6227948265449e985af3a0a", "status": "passed", "command": "pnpm --filter @agentskit/core test", "evidence": "34 files, 498 tests passed" },
|
|
10
|
+
{ "id": "memory", "package": "@agentskit/memory", "version": "0.11.9", "revision": "d89a2d74108c29f2c6227948265449e985af3a0a", "status": "passed", "command": "pnpm --filter @agentskit/memory test", "evidence": "31 files, 228 tests passed" },
|
|
11
|
+
{ "id": "eval", "package": "@agentskit/eval", "version": "0.6.5", "revision": "d89a2d74108c29f2c6227948265449e985af3a0a", "status": "passed", "command": "pnpm --filter @agentskit/eval test", "evidence": "9 files, 105 tests passed; build and bundler preparation passed" },
|
|
12
|
+
{ "id": "doc-bridge", "package": "@agentskit/doc-bridge", "version": "1.7.45", "revision": "9f9e9ddd4f736981c2bea243fef33b68be2b5002", "status": "passed", "command": "pnpm build && pnpm test && pnpm typecheck", "evidence": "50 files, 351 tests passed; Cursor/Claude/Copilot/portable plugin contracts passed; typecheck passed" },
|
|
13
|
+
{ "id": "code-review", "package": "@agentskit/code-review", "version": "0.30.8", "revision": "c993f9a9359157a767d738d60c30add2b02fd09a", "status": "failed", "command": "npm test; npm run quality:matrix -- --input <real-runner-artifact>", "evidence": "241 tests passed. Real run ceaf007a-ea6e-4dc9-a601-f816702b301c reached BLOCKED: detection 1/4, precision 1/4, batch-efficiency 1/4, integration 1/4; no comparable baseline." },
|
|
14
|
+
{ "id": "adapter-boundary", "package": "@agentskit/harness", "version": "0.4.0", "revision": "a18b57d43f037b7a34b134e01baf913ae2341fd7", "status": "passed", "command": "pnpm test:adapters", "evidence": "covered by ak-verify run 1789063362018-21016-775q93" },
|
|
15
|
+
{ "id": "runtime", "package": "@agentskit/harness", "version": "0.4.0", "revision": "a18b57d43f037b7a34b134e01baf913ae2341fd7", "status": "passed", "command": "pnpm test:runtime && pnpm test:runtime-attestation", "evidence": "covered by ak-verify run 1789063362018-21016-775q93" }
|
|
16
|
+
],
|
|
17
|
+
"blockers": [
|
|
18
|
+
"code-review real-run quality matrix has no passing detection/precision/baseline evidence",
|
|
19
|
+
"harness-vs-no-Harness cohort is not yet comparable (10-task owner-approved cohort required)",
|
|
20
|
+
"published-registry smoke is pending the main-branch release"
|
|
21
|
+
],
|
|
22
|
+
"baseline": { "path": "benchmarks/harness-0.4.0-baseline.json", "status": "not-run", "doNotInfer": true }
|
|
23
|
+
}
|
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
# Compatibility report
|
|
2
|
+
|
|
3
|
+
**Release:** `@agentskit/harness@0.4.0`
|
|
4
|
+
**Harness revision:** `a18b57d43f037b7a34b134e01baf913ae2341fd7`
|
|
5
|
+
**Captured:** 2026-09-10
|
|
6
|
+
**Decision:** BLOCKED (fail closed)
|
|
7
|
+
|
|
8
|
+
## Passed boundaries
|
|
9
|
+
|
|
10
|
+
- `@agentskit/core@1.12.9` at `d89a2d7`: 498/498 tests.
|
|
11
|
+
- `@agentskit/memory@0.11.9` at `d89a2d7`: 228/228 tests.
|
|
12
|
+
- `@agentskit/eval@0.6.5` at `d89a2d7`: 105/105 tests, including build/bundler preparation.
|
|
13
|
+
- `@agentskit/doc-bridge@1.7.45` at `9f9e9d`: 351/351 tests plus plugin contracts and typecheck.
|
|
14
|
+
- Harness adapter and runtime boundaries: covered by `ak-verify` run `1789063362018-21016-775q93` (21/21 checks).
|
|
15
|
+
|
|
16
|
+
## Blocked evidence
|
|
17
|
+
|
|
18
|
+
`@agentskit/code-review@0.30.8` passed its 241-test suite. A real fixed-provider run (`codex-cli`, `gpt-5.6-luna`, high, run `ceaf007a-ea6e-4dc9-a601-f816702b301c`) completed coverage, safety, reliability, memory, configuration, and actionability checks, but the quality matrix blocked on detection, precision, batch efficiency, integration, and missing comparable baselines. The result is retained as evidence, not promoted to a pass.
|
|
19
|
+
|
|
20
|
+
The 0.4.0 no-Harness comparison remains unavailable because the owner-approved ten-task cohort and matching criterion-level logs have not been captured. Registry smoke can only run after the main Trusted Publishing workflow publishes the package.
|
|
21
|
+
|
|
22
|
+
See [`migration.md`](migration.md), [`rollback.md`](rollback.md), and the machine-readable [`report.json`](report.json).
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
# Compatibility rollback rehearsal
|
|
2
|
+
|
|
3
|
+
Rollback is a clean checkout at the pinned `previousVersion`, followed by the
|
|
4
|
+
same adapter-boundary smoke and eval commands. Record the resulting revision,
|
|
5
|
+
commands, exit codes, and evidence digest in `compatibility/report.json`.
|
|
6
|
+
|
|
7
|
+
No external mutation is performed by the Harness. A missing rehearsal record
|
|
8
|
+
is `unknown` and blocks release rather than being inferred as a pass.
|
package/dist/cli.js
CHANGED
|
@@ -1,13 +1,13 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
import { createHash, randomUUID, createPrivateKey, createPublicKey, sign, verify } from 'crypto';
|
|
3
|
-
import { readFileSync, mkdirSync, existsSync, unlinkSync, openSync, writeFileSync, closeSync, writeSync, appendFileSync, mkdtempSync, renameSync, rmSync
|
|
3
|
+
import { readFileSync, mkdirSync, existsSync, unlinkSync, openSync, writeFileSync, closeSync, readdirSync, writeSync, appendFileSync, mkdtempSync, renameSync, rmSync } from 'fs';
|
|
4
4
|
import { Command } from 'commander';
|
|
5
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
8
|
import { tmpdir, cpus, loadavg, freemem, totalmem } from 'os';
|
|
9
9
|
|
|
10
|
-
// src/constants.ts
|
|
10
|
+
// src/kernel/constants.ts
|
|
11
11
|
var STATES = [
|
|
12
12
|
"CLARIFYING",
|
|
13
13
|
"PLANNED",
|
|
@@ -37,7 +37,7 @@ var LEGAL_TRANSITIONS = {
|
|
|
37
37
|
var REAL_CATEGORIES = /* @__PURE__ */ new Set(["endpoint", "database", "cli", "mcp", "ui"]);
|
|
38
38
|
var DECISIONS = /* @__PURE__ */ new Set(["approved", "approve", "yes", "ok", "rejected", "reject", "no"]);
|
|
39
39
|
|
|
40
|
-
// src/errors.ts
|
|
40
|
+
// src/kernel/errors.ts
|
|
41
41
|
var HarnessError = class extends Error {
|
|
42
42
|
code;
|
|
43
43
|
constructor(message, code = "HARNESS_ERROR") {
|
|
@@ -50,7 +50,7 @@ var fail = (message, code = "HARNESS_ERROR") => {
|
|
|
50
50
|
throw new HarnessError(message, code);
|
|
51
51
|
};
|
|
52
52
|
|
|
53
|
-
// src/profiles.ts
|
|
53
|
+
// src/profiles/index.ts
|
|
54
54
|
var isRecord = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
55
55
|
var record = (value, label) => {
|
|
56
56
|
if (!isRecord(value)) fail(`${label} must be an object.`, "INVALID_CONFIG");
|
|
@@ -140,7 +140,7 @@ var cleanConfiguredArtifacts = (loaded) => {
|
|
|
140
140
|
};
|
|
141
141
|
var fileContents = (path) => readFileSync(path, "utf8");
|
|
142
142
|
|
|
143
|
-
// src/types.ts
|
|
143
|
+
// src/kernel/types.ts
|
|
144
144
|
var SURFACE_NAMES = ["logic", "endpoint", "database", "cli", "mcp", "ui", "docs"];
|
|
145
145
|
var CHECK_CATEGORIES = ["build", "test", "lint", ...SURFACE_NAMES, "custom"];
|
|
146
146
|
var RUN_STATES = [
|
|
@@ -157,7 +157,7 @@ var RUN_STATES = [
|
|
|
157
157
|
"SUPERSEDED"
|
|
158
158
|
];
|
|
159
159
|
|
|
160
|
-
// src/config.ts
|
|
160
|
+
// src/execution/config.ts
|
|
161
161
|
var isRecord2 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
162
162
|
var stringValue = (value, label) => {
|
|
163
163
|
if (typeof value !== "string") return fail(`${label} is required.`, "INVALID_CONFIG");
|
|
@@ -271,7 +271,7 @@ var loadConfig = (configPath = ".codex/verification.json") => {
|
|
|
271
271
|
return { absolute, root, stateDir, config, configHash: hashJson(config) };
|
|
272
272
|
};
|
|
273
273
|
|
|
274
|
-
// src/state-machine.ts
|
|
274
|
+
// src/kernel/state-machine.ts
|
|
275
275
|
var transition = (run, to, reason, actor = "harness") => {
|
|
276
276
|
if (!STATES.includes(to)) fail(`Unknown state ${to}.`, "INVALID_STATE");
|
|
277
277
|
if (run.state !== to && !LEGAL_TRANSITIONS[run.state].some((state) => state === to)) fail(`Illegal transition ${run.state} -> ${to}.`, "INVALID_STATE");
|
|
@@ -287,7 +287,7 @@ var approvedDecision = (decision) => {
|
|
|
287
287
|
};
|
|
288
288
|
var HARNESS_EVENT_SCHEMA_VERSION = 1;
|
|
289
289
|
var EVENT_LOG_GENESIS = "GENESIS";
|
|
290
|
-
var HARNESS_EVENT_TYPES = ["run.created", "state.transitioned", "context.attached", "verification.completed", "approval.recorded", "authorization.recorded", "session.started", "session.resumed", "agent.turn.started", "policy.evaluated", "tool.approval.requested", "tool.approval.recorded", "tool.requested", "tool.execution.started", "tool.recovery.recorded", "tool.blocked", "tool.completed", "tool.failed", "session.ended"];
|
|
290
|
+
var HARNESS_EVENT_TYPES = ["run.created", "state.transitioned", "context.attached", "verification.completed", "artifact.recorded", "approval.recorded", "authorization.recorded", "session.started", "session.resumed", "agent.turn.started", "policy.evaluated", "tool.approval.requested", "tool.approval.recorded", "tool.requested", "tool.execution.started", "tool.recovery.recorded", "tool.blocked", "tool.completed", "tool.failed", "session.ended"];
|
|
291
291
|
var SESSION_EVENT_TYPES = /* @__PURE__ */ new Set(["session.started", "session.resumed", "agent.turn.started", "policy.evaluated", "tool.approval.requested", "tool.approval.recorded", "tool.requested", "tool.execution.started", "tool.recovery.recorded", "tool.blocked", "tool.completed", "tool.failed", "session.ended"]);
|
|
292
292
|
var eventPath = (stateDir, runId) => join(stateDir, "runs", runId, "events.ndjson");
|
|
293
293
|
var lockPath = (stateDir, runId) => `${eventPath(stateDir, runId)}.lock`;
|
|
@@ -416,13 +416,16 @@ var recoverEventLogLock = ({ stateDir, runId, actor, maxAgeMs = 3e5 }) => {
|
|
|
416
416
|
return fail("Event log lock owner is still alive.", "HARNESS_ERROR");
|
|
417
417
|
};
|
|
418
418
|
|
|
419
|
-
// src/plugins.ts
|
|
419
|
+
// src/kernel/plugins.ts
|
|
420
420
|
var createPluginSlot = (id2) => {
|
|
421
421
|
if (!id2.trim()) fail("Plugin slot id is required.", "INVALID_INPUT");
|
|
422
|
-
return { id: id2 };
|
|
422
|
+
return { id: id2.trim() };
|
|
423
423
|
};
|
|
424
424
|
|
|
425
|
-
// src/
|
|
425
|
+
// src/kernel/adapter-contract.ts
|
|
426
|
+
var ASSURANCE_LEVELS = ["unverified", "contract-tested", "runtime-attested"];
|
|
427
|
+
|
|
428
|
+
// src/context/index.ts
|
|
426
429
|
var hashContextSnapshot = ({ providerId, query, references, sourceHash: sourceHash2 }) => hashJson({ providerId, query, references, sourceHash: sourceHash2 });
|
|
427
430
|
var hashContextSnapshots = (snapshots) => hashJson(snapshots.map(({ providerId, query, references, sourceHash: sourceHash2, snapshotHash }) => ({ providerId, query, references, sourceHash: sourceHash2, snapshotHash })));
|
|
428
431
|
var record2 = (value, label) => {
|
|
@@ -445,17 +448,23 @@ var validateContextSnapshot = (value, index2 = 0) => {
|
|
|
445
448
|
uri: requiredString(rawReference["uri"], `context snapshot ${index2}.references[${referenceIndex}].uri`),
|
|
446
449
|
...typeof rawReference["title"] === "string" ? { title: rawReference["title"] } : {},
|
|
447
450
|
...typeof rawReference["version"] === "string" ? { version: rawReference["version"] } : {},
|
|
448
|
-
...typeof rawReference["contentHash"] === "string" ? { contentHash: rawReference["contentHash"] } : {}
|
|
451
|
+
...typeof rawReference["contentHash"] === "string" ? { contentHash: rawReference["contentHash"] } : {},
|
|
452
|
+
...rawReference["relevance"] === void 0 ? {} : typeof rawReference["relevance"] === "number" && rawReference["relevance"] >= 0 && rawReference["relevance"] <= 1 ? { relevance: rawReference["relevance"] } : fail(`context snapshot ${index2}.references[${referenceIndex}].relevance must be between 0 and 1.`, "INVALID_INPUT")
|
|
449
453
|
};
|
|
450
454
|
});
|
|
451
455
|
const scope = rawQuery["scope"] === void 0 ? void 0 : Array.isArray(rawQuery["scope"]) && rawQuery["scope"].every((item) => typeof item === "string") ? rawQuery["scope"] : fail(`context snapshot ${index2}.query.scope must be an array of strings.`, "INVALID_INPUT");
|
|
456
|
+
const assurance = raw["assurance"] === void 0 ? void 0 : ASSURANCE_LEVELS.includes(raw["assurance"]) ? raw["assurance"] : fail(`context snapshot ${index2}.assurance is invalid.`, "INVALID_INPUT");
|
|
457
|
+
const telemetry = raw["telemetry"] === void 0 ? void 0 : record2(raw["telemetry"], `context snapshot ${index2}.telemetry`);
|
|
458
|
+
if (telemetry && telemetry["status"] !== "measured" && telemetry["status"] !== "unknown") fail(`context snapshot ${index2}.telemetry.status is invalid.`, "INVALID_INPUT");
|
|
452
459
|
const snapshot = {
|
|
453
460
|
providerId: requiredString(raw["providerId"], `context snapshot ${index2}.providerId`),
|
|
454
461
|
query: { query: requiredString(rawQuery["query"], `context snapshot ${index2}.query.query`), ...scope ? { scope } : {}, ...typeof rawQuery["sourceRevision"] === "string" ? { sourceRevision: rawQuery["sourceRevision"] } : {} },
|
|
455
462
|
references,
|
|
456
463
|
sourceHash: requiredString(raw["sourceHash"], `context snapshot ${index2}.sourceHash`),
|
|
457
464
|
snapshotHash: requiredString(raw["snapshotHash"], `context snapshot ${index2}.snapshotHash`),
|
|
458
|
-
resolvedAt: requiredString(raw["resolvedAt"], `context snapshot ${index2}.resolvedAt`)
|
|
465
|
+
resolvedAt: requiredString(raw["resolvedAt"], `context snapshot ${index2}.resolvedAt`),
|
|
466
|
+
...assurance === void 0 ? {} : { assurance },
|
|
467
|
+
...telemetry === void 0 ? {} : { telemetry }
|
|
459
468
|
};
|
|
460
469
|
if (snapshot.snapshotHash !== hashContextSnapshot(snapshot)) fail(`context snapshot ${index2}.snapshotHash does not match its contents.`, "INVALID_INPUT");
|
|
461
470
|
return snapshot;
|
|
@@ -467,7 +476,7 @@ var readContextSnapshots = (path) => {
|
|
|
467
476
|
var validateContextSnapshots = (snapshots) => snapshots.map((snapshot, index2) => validateContextSnapshot(snapshot, index2));
|
|
468
477
|
createPluginSlot("context.provider");
|
|
469
478
|
|
|
470
|
-
// src/runs.ts
|
|
479
|
+
// src/execution/runs.ts
|
|
471
480
|
var now = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
472
481
|
var newRunId = () => `${Date.now()}-${process.pid}-${Math.random().toString(36).slice(2, 8)}`;
|
|
473
482
|
var saveRun2 = (stateDir, run) => {
|
|
@@ -534,8 +543,8 @@ var validateEvidence = (root, check, evidence, outcomeIds) => {
|
|
|
534
543
|
if (evidence.capability !== "real-browser") failures.push("UI evidence must declare capability real-browser");
|
|
535
544
|
if (!Array.isArray(evidence.artifacts) || evidence.artifacts.length === 0) failures.push("UI evidence requires screenshot artifacts");
|
|
536
545
|
}
|
|
537
|
-
const
|
|
538
|
-
for (const artifactValue of
|
|
546
|
+
const artifacts2 = Array.isArray(evidence.artifacts) ? evidence.artifacts : [];
|
|
547
|
+
for (const artifactValue of artifacts2) {
|
|
539
548
|
if (!isRecord3(artifactValue) || typeof artifactValue["path"] !== "string" || typeof artifactValue["sha256"] !== "string") {
|
|
540
549
|
failures.push("artifact requires string path and sha256");
|
|
541
550
|
continue;
|
|
@@ -651,7 +660,7 @@ var createMachineMonitor = (sampleIntervalMs = 5e3, options2 = {}) => {
|
|
|
651
660
|
};
|
|
652
661
|
};
|
|
653
662
|
|
|
654
|
-
// src/verification.ts
|
|
663
|
+
// src/execution/verification.ts
|
|
655
664
|
var now2 = () => (/* @__PURE__ */ new Date()).toISOString();
|
|
656
665
|
var requireRun = (run) => run ?? fail("No verification run exists.", "NO_RUN");
|
|
657
666
|
var verificationProjection = (run) => ({ checks: run.checks, outcomes: run.outcomes, metrics: run.metrics });
|
|
@@ -796,10 +805,10 @@ var verifyRun = async ({ configPath }) => {
|
|
|
796
805
|
const required8 = outcome.checks.filter((id2) => loaded.config.checks.find((check) => check.id === id2)?.required);
|
|
797
806
|
return { ...outcome, status: required8.length === 0 ? "not-applicable" : required8.every((id2) => statuses.get(id2) === "passed") ? "passed" : "failed" };
|
|
798
807
|
}), metrics: { totalDurationMs, wallDurationMs: Date.now() - verificationStarted, peakConcurrency: observedPeakConcurrency, budgetExceeded, machine: machineMonitor.stop() } };
|
|
799
|
-
const
|
|
800
|
-
current = { ...current, verificationDigest:
|
|
808
|
+
const digest4 = verificationDigest(current);
|
|
809
|
+
current = { ...current, verificationDigest: digest4 };
|
|
801
810
|
saveRun2(loaded.stateDir, current);
|
|
802
|
-
new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest:
|
|
811
|
+
new FileEventStore(loaded.stateDir).append({ runId: current.runId, sourceRevision: current.sourceRevision, configHash: current.configHash, type: "verification.completed", payload: { verificationDigest: digest4, checkCount: current.checks.length, outcomeCount: current.outcomes.length, totalDurationMs, budgetExceeded } });
|
|
803
812
|
const automatic = allPassed && current.autonomy === "yolo" && !loaded.config.tracking.required && loaded.config.contract.ambiguities.length === 0;
|
|
804
813
|
const nextState = allPassed ? automatic ? "COMPLETE" : "AWAITING_HUMAN_APPROVAL" : "BLOCKED";
|
|
805
814
|
current = { ...transition(current, nextState, automatic ? "All applicable checks passed; YOLO policy permits automatic completion." : allPassed ? "All configured checks passed; human approval is required." : budgetExceeded ? "Verification budget was exceeded." : "A configured check failed or lacked structured evidence.", "harness") };
|
|
@@ -917,15 +926,17 @@ var createDocBridgeContextProvider = ({ root, indexPath = ".doc-bridge/index.jso
|
|
|
917
926
|
id: "doc-bridge",
|
|
918
927
|
version: "1.0.0",
|
|
919
928
|
resolve: async (query) => {
|
|
929
|
+
const started = Date.now();
|
|
920
930
|
const document = index(root, indexPath);
|
|
921
931
|
const contentHash = sourceHash(document);
|
|
922
932
|
const entries = Array.isArray(document.knowledge) ? document.knowledge.filter((value) => typeof value === "object" && value !== null && !Array.isArray(value)).filter((entry) => matches(entry, query)).sort((left, right) => String(left.id ?? "").localeCompare(String(right.id ?? ""))).slice(0, 8) : [];
|
|
923
|
-
const references = entries.flatMap((entry) => typeof entry.id === "string" && typeof entry.path === "string" ? [{ id: entry.id, uri: `doc-bridge://${entry.path}`, ...typeof entry.title === "string" ? { title: entry.title } : {}, contentHash }] : []);
|
|
924
|
-
|
|
933
|
+
const references = entries.flatMap((entry) => typeof entry.id === "string" && typeof entry.path === "string" ? [{ id: entry.id, uri: `doc-bridge://${entry.path}`, ...typeof entry.title === "string" ? { title: entry.title } : {}, contentHash: typeof entry.contentHash === "string" ? entry.contentHash : contentHash, relevance: 1 }] : []);
|
|
934
|
+
const telemetry = { status: "measured", durationMs: Date.now() - started, contextReferences: references.length, contextCostTokens: Math.max(1, Math.ceil(JSON.stringify(references).length / 4)) };
|
|
935
|
+
return { providerId: "doc-bridge", query, references, sourceHash: contentHash, snapshotHash: hashContextSnapshot({ providerId: "doc-bridge", query, references, sourceHash: contentHash }), resolvedAt: (/* @__PURE__ */ new Date()).toISOString(), assurance: "contract-tested", telemetry };
|
|
925
936
|
}
|
|
926
937
|
});
|
|
927
938
|
|
|
928
|
-
// src/discovery.ts
|
|
939
|
+
// src/kernel/discovery.ts
|
|
929
940
|
var required = (value, label) => {
|
|
930
941
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
931
942
|
return value.trim();
|
|
@@ -987,7 +998,7 @@ var assessDiscovery = (input) => {
|
|
|
987
998
|
return { ...base, digest: digest2(base) };
|
|
988
999
|
};
|
|
989
1000
|
|
|
990
|
-
// src/wip.ts
|
|
1001
|
+
// src/kernel/wip.ts
|
|
991
1002
|
var WIP_STATES = ["ready", "implementing", "blocked", "awaiting-decision", "awaiting-acceptance", "done", "cancelled"];
|
|
992
1003
|
var terminal = /* @__PURE__ */ new Set(["done", "cancelled"]);
|
|
993
1004
|
var required2 = (value, label) => {
|
|
@@ -1019,7 +1030,7 @@ var assessWip = ({ entries, candidate, maxInFlight = 3 }) => {
|
|
|
1019
1030
|
return { decision: "admit", inFlight, counts, reason: `WIP slot available (${inFlight.length}/${maxInFlight}).` };
|
|
1020
1031
|
};
|
|
1021
1032
|
|
|
1022
|
-
// src/experiment.ts
|
|
1033
|
+
// src/kernel/experiment.ts
|
|
1023
1034
|
var required3 = (value, label) => {
|
|
1024
1035
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1025
1036
|
return value.trim();
|
|
@@ -1046,7 +1057,7 @@ var selectRuntime = (candidates) => {
|
|
|
1046
1057
|
return { decision: "selected", selected, eligible, reason: "Selected by human minutes, duration, cost, then Orca tie-break." };
|
|
1047
1058
|
};
|
|
1048
1059
|
|
|
1049
|
-
// src/delivery.ts
|
|
1060
|
+
// src/delivery/index.ts
|
|
1050
1061
|
var required4 = (value, label) => {
|
|
1051
1062
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1052
1063
|
return value.trim();
|
|
@@ -1141,7 +1152,7 @@ var assessAcceptance = ({ production, acceptanceRequired, accepted, notApplicabl
|
|
|
1141
1152
|
return assessed("G5", "approved", acceptanceRequired ? [] : [`Acceptance is not applicable: ${notApplicableReason}.`], production.binding);
|
|
1142
1153
|
};
|
|
1143
1154
|
|
|
1144
|
-
// src/pilot.ts
|
|
1155
|
+
// src/kernel/pilot.ts
|
|
1145
1156
|
var required5 = (value, label) => {
|
|
1146
1157
|
if (typeof value !== "string" || !value.trim()) fail(`${label} is required.`, "INVALID_INPUT");
|
|
1147
1158
|
return value.trim();
|
|
@@ -1216,9 +1227,131 @@ var assessImprovementCycle = (input) => {
|
|
|
1216
1227
|
const reasons = complete ? ["All five cycle steps passed."] : iterations.length >= input.maxIterations ? ["Maximum cycle iterations reached; human adjustment is required."] : latest.adjustment ? ["A failed or blocked step remains; repeat with the recorded adjustment."] : ["A failed or blocked step remains; an explicit adjustment is required before repeating."];
|
|
1217
1228
|
const decision = complete ? "complete" : iterations.length >= input.maxIterations || !latest.adjustment ? "blocked" : "repeat";
|
|
1218
1229
|
const result = { type: "agentskit-harness-improvement-cycle", cycleId, decision, ...decision === "repeat" ? { nextIteration: latest.iteration + 1 } : {}, reasons, matrix };
|
|
1219
|
-
const
|
|
1220
|
-
return { ...result, digest:
|
|
1230
|
+
const digest4 = createHash("sha256").update(JSON.stringify(result)).digest("hex");
|
|
1231
|
+
return { ...result, digest: digest4 };
|
|
1232
|
+
};
|
|
1233
|
+
var ARTIFACT_SCHEMA_VERSION = 1;
|
|
1234
|
+
var ARTIFACT_TYPES = ["plan", "finding", "decision", "repair", "blocker", "approval", "phase"];
|
|
1235
|
+
var text2 = (value, label) => {
|
|
1236
|
+
if (typeof value !== "string" || !value.trim()) return fail(`${label} is required.`, "INVALID_INPUT");
|
|
1237
|
+
return value.trim();
|
|
1238
|
+
};
|
|
1239
|
+
var digest3 = (value, label) => {
|
|
1240
|
+
const result = text2(value, label);
|
|
1241
|
+
if (!/^[a-f0-9]{64}$/.test(result)) fail(`${label} must be a lowercase SHA-256 digest.`, "INVALID_INPUT");
|
|
1242
|
+
return result;
|
|
1243
|
+
};
|
|
1244
|
+
var artifactId = (value) => {
|
|
1245
|
+
const result = text2(value, "Artifact artifactId");
|
|
1246
|
+
if (!/^[A-Za-z0-9][A-Za-z0-9._:-]{0,127}$/.test(result)) fail("Artifact artifactId is invalid.", "INVALID_INPUT");
|
|
1247
|
+
return result;
|
|
1248
|
+
};
|
|
1249
|
+
var isRecord4 = (value) => typeof value === "object" && value !== null && !Array.isArray(value);
|
|
1250
|
+
var artifactBody = (artifact) => ({
|
|
1251
|
+
type: artifact.type,
|
|
1252
|
+
schemaVersion: artifact.schemaVersion,
|
|
1253
|
+
artifactId: artifact.artifactId,
|
|
1254
|
+
artifactType: artifact.artifactType,
|
|
1255
|
+
artifactVersion: artifact.artifactVersion,
|
|
1256
|
+
runId: artifact.runId,
|
|
1257
|
+
issueRef: artifact.issueRef,
|
|
1258
|
+
sourceRevision: artifact.sourceRevision,
|
|
1259
|
+
contractHash: artifact.contractHash,
|
|
1260
|
+
configHash: artifact.configHash,
|
|
1261
|
+
contextHash: artifact.contextHash,
|
|
1262
|
+
phase: artifact.phase,
|
|
1263
|
+
payload: artifact.payload,
|
|
1264
|
+
payloadHash: artifact.payloadHash
|
|
1265
|
+
});
|
|
1266
|
+
var expectedArtifactHash = (artifact) => hashJson(artifactBody(artifact));
|
|
1267
|
+
var validateArtifactEnvelope = (value) => {
|
|
1268
|
+
if (!isRecord4(value)) return fail("Artifact envelope must be an object.", "INVALID_INPUT");
|
|
1269
|
+
if (value["type"] !== "agentskit-harness-artifact" || value["schemaVersion"] !== ARTIFACT_SCHEMA_VERSION) fail("Artifact envelope type or schemaVersion is invalid.", "INVALID_INPUT");
|
|
1270
|
+
if (!ARTIFACT_TYPES.includes(value["artifactType"])) fail("Artifact artifactType is invalid.", "INVALID_INPUT");
|
|
1271
|
+
if (!Number.isInteger(value["artifactVersion"]) || value["artifactVersion"] < 1) fail("Artifact artifactVersion must be a positive integer.", "INVALID_INPUT");
|
|
1272
|
+
const createdAt = text2(value["createdAt"], "Artifact createdAt");
|
|
1273
|
+
if (!Number.isFinite(Date.parse(createdAt))) fail("Artifact createdAt must be a valid timestamp.", "INVALID_INPUT");
|
|
1274
|
+
const payloadHash = digest3(value["payloadHash"], "Artifact payloadHash");
|
|
1275
|
+
if (hashJson(value["payload"]) !== payloadHash) fail("Artifact payloadHash does not match payload.", "INVALID_INPUT");
|
|
1276
|
+
const artifact = {
|
|
1277
|
+
type: "agentskit-harness-artifact",
|
|
1278
|
+
schemaVersion: ARTIFACT_SCHEMA_VERSION,
|
|
1279
|
+
artifactId: artifactId(value["artifactId"]),
|
|
1280
|
+
artifactType: value["artifactType"],
|
|
1281
|
+
artifactVersion: value["artifactVersion"],
|
|
1282
|
+
runId: text2(value["runId"], "Artifact runId"),
|
|
1283
|
+
issueRef: text2(value["issueRef"], "Artifact issueRef"),
|
|
1284
|
+
sourceRevision: text2(value["sourceRevision"], "Artifact sourceRevision"),
|
|
1285
|
+
contractHash: digest3(value["contractHash"], "Artifact contractHash"),
|
|
1286
|
+
configHash: digest3(value["configHash"], "Artifact configHash"),
|
|
1287
|
+
contextHash: digest3(value["contextHash"], "Artifact contextHash"),
|
|
1288
|
+
phase: text2(value["phase"], "Artifact phase"),
|
|
1289
|
+
createdAt,
|
|
1290
|
+
payload: value["payload"],
|
|
1291
|
+
payloadHash
|
|
1292
|
+
};
|
|
1293
|
+
if (digest3(value["artifactHash"], "Artifact artifactHash") !== expectedArtifactHash(artifact)) fail("Artifact artifactHash does not match envelope.", "INVALID_INPUT");
|
|
1294
|
+
return { ...artifact, artifactHash: value["artifactHash"] };
|
|
1295
|
+
};
|
|
1296
|
+
var renderArtifactMarkdown = (artifact) => [
|
|
1297
|
+
`# ${artifact.artifactType} artifact ${artifact.artifactId}`,
|
|
1298
|
+
"",
|
|
1299
|
+
`- Schema: ${artifact.schemaVersion}`,
|
|
1300
|
+
`- Version: ${artifact.artifactVersion}`,
|
|
1301
|
+
`- Run: ${artifact.runId}`,
|
|
1302
|
+
`- Issue: ${artifact.issueRef}`,
|
|
1303
|
+
`- Phase: ${artifact.phase}`,
|
|
1304
|
+
`- Source revision: ${artifact.sourceRevision}`,
|
|
1305
|
+
`- Contract hash: ${artifact.contractHash}`,
|
|
1306
|
+
`- Configuration hash: ${artifact.configHash}`,
|
|
1307
|
+
`- Context hash: ${artifact.contextHash}`,
|
|
1308
|
+
`- Artifact hash: ${artifact.artifactHash}`,
|
|
1309
|
+
"",
|
|
1310
|
+
"## Payload",
|
|
1311
|
+
"",
|
|
1312
|
+
"```json",
|
|
1313
|
+
JSON.stringify(artifact.payload, null, 2),
|
|
1314
|
+
"```",
|
|
1315
|
+
""
|
|
1316
|
+
].join("\n");
|
|
1317
|
+
var artifactFilePath = (stateDir, runId, id2) => join(stateDir, "runs", runId, "artifacts", `${id2}.json`);
|
|
1318
|
+
var artifactMarkdownPath = (stateDir, runId, id2) => join(stateDir, "runs", runId, "artifacts", `${id2}.md`);
|
|
1319
|
+
var FileArtifactStore = class {
|
|
1320
|
+
constructor(stateDir) {
|
|
1321
|
+
this.stateDir = stateDir;
|
|
1322
|
+
}
|
|
1323
|
+
stateDir;
|
|
1324
|
+
write(input) {
|
|
1325
|
+
const artifact = validateArtifactEnvelope(input);
|
|
1326
|
+
const path = artifactFilePath(this.stateDir, artifact.runId, artifact.artifactId);
|
|
1327
|
+
mkdirSync(join(this.stateDir, "runs", artifact.runId, "artifacts"), { recursive: true });
|
|
1328
|
+
if (existsSync(path)) {
|
|
1329
|
+
const existing = validateArtifactEnvelope(JSON.parse(readFileSync(path, "utf8")));
|
|
1330
|
+
if (existing.artifactHash !== artifact.artifactHash) fail(`Artifact ${artifact.artifactId} already exists with different content.`, "HARNESS_ERROR");
|
|
1331
|
+
return existing;
|
|
1332
|
+
}
|
|
1333
|
+
writeFileSync(path, `${JSON.stringify(artifact, null, 2)}
|
|
1334
|
+
`, "utf8");
|
|
1335
|
+
writeFileSync(artifactMarkdownPath(this.stateDir, artifact.runId, artifact.artifactId), renderArtifactMarkdown(artifact), "utf8");
|
|
1336
|
+
new FileEventStore(this.stateDir).append({
|
|
1337
|
+
runId: artifact.runId,
|
|
1338
|
+
sourceRevision: artifact.sourceRevision,
|
|
1339
|
+
configHash: artifact.configHash,
|
|
1340
|
+
type: "artifact.recorded",
|
|
1341
|
+
payload: { artifactId: artifact.artifactId, artifactType: artifact.artifactType, artifactVersion: artifact.artifactVersion, artifactHash: artifact.artifactHash, phase: artifact.phase, representation: "json+markdown" }
|
|
1342
|
+
});
|
|
1343
|
+
return artifact;
|
|
1344
|
+
}
|
|
1345
|
+
read(runId, id2) {
|
|
1346
|
+
return validateArtifactEnvelope(JSON.parse(readFileSync(artifactFilePath(this.stateDir, runId, artifactId(id2)), "utf8")));
|
|
1347
|
+
}
|
|
1348
|
+
list(runId) {
|
|
1349
|
+
const directory = join(this.stateDir, "runs", runId, "artifacts");
|
|
1350
|
+
if (!existsSync(directory)) return [];
|
|
1351
|
+
return readdirSync(directory).filter((name) => name.endsWith(".json")).sort().map((name) => validateArtifactEnvelope(JSON.parse(readFileSync(join(directory, name), "utf8"))));
|
|
1352
|
+
}
|
|
1221
1353
|
};
|
|
1354
|
+
var readArtifactFile = (path) => validateArtifactEnvelope(JSON.parse(readFileSync(path, "utf8")));
|
|
1222
1355
|
var BENCHMARK_SCHEMA_VERSION = 1;
|
|
1223
1356
|
var percentage = (part, total) => total ? Number((part / total).toFixed(4)) : null;
|
|
1224
1357
|
var improvementRate = (baseline, current) => baseline === void 0 || current === void 0 || baseline === 0 ? null : Number(((baseline - current) / baseline).toFixed(4));
|
|
@@ -1504,10 +1637,10 @@ var createDispatchLedger = (stateDir) => {
|
|
|
1504
1637
|
},
|
|
1505
1638
|
recordDispatch: ({ lease, idempotencyKey, commandDigest }) => {
|
|
1506
1639
|
const id2 = required6(idempotencyKey, "idempotencyKey");
|
|
1507
|
-
const
|
|
1640
|
+
const digest4 = required6(commandDigest, "commandDigest");
|
|
1508
1641
|
const existing = records().find((record4) => record4.action === "dispatch" && record4.idempotencyKey === id2);
|
|
1509
1642
|
if (existing) return { decision: "duplicate", record: existing };
|
|
1510
|
-
const record3 = { ...lease, action: "dispatch", at: now3(), idempotencyKey: id2, commandDigest:
|
|
1643
|
+
const record3 = { ...lease, action: "dispatch", at: now3(), idempotencyKey: id2, commandDigest: digest4 };
|
|
1511
1644
|
append(record3);
|
|
1512
1645
|
return { decision: "recorded", record: record3 };
|
|
1513
1646
|
},
|
|
@@ -1566,9 +1699,9 @@ var planFilePreflight = (files, options2 = {}) => {
|
|
|
1566
1699
|
return { files: unique2, codeFiles, testFiles, docsOnly, checks: docsOnly ? [] : ["lint", "typecheck", ...testFiles.length ? ["test"] : []] };
|
|
1567
1700
|
};
|
|
1568
1701
|
|
|
1569
|
-
// src/block.ts
|
|
1702
|
+
// src/kernel/block.ts
|
|
1570
1703
|
var BLOCK_STATUSES = ["todo", "picked", "development", "validation", "pr-open", "merged", "post-merge", "done", "blocked", "scope-cut"];
|
|
1571
|
-
var
|
|
1704
|
+
var text3 = (value, label) => {
|
|
1572
1705
|
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1573
1706
|
};
|
|
1574
1707
|
var list = (value, label) => {
|
|
@@ -1595,16 +1728,16 @@ var validateBlockManifest = (value) => {
|
|
|
1595
1728
|
for (const key of ["maxMinutes", "maxAttempts"]) if (candidate[key] !== void 0 && (!Number.isInteger(candidate[key]) || candidate[key] < 1)) fail(`budget.${key} must be a positive integer.`, "INVALID_INPUT");
|
|
1596
1729
|
budget = { ...candidate["maxMinutes"] === void 0 ? {} : { maxMinutes: candidate["maxMinutes"] }, ...candidate["maxAttempts"] === void 0 ? {} : { maxAttempts: candidate["maxAttempts"] } };
|
|
1597
1730
|
}
|
|
1598
|
-
return { schemaVersion: 1, id:
|
|
1731
|
+
return { schemaVersion: 1, id: text3(raw["id"], "id"), title: text3(raw["title"], "title"), tracker: text3(raw["tracker"], "tracker"), repository: text3(raw["repository"], "repository"), acceptanceCriteria: criteria, dependencies, wave, status: status2, ...budget ? { budget } : {}, ...raw["humanGates"] === void 0 ? {} : { humanGates: list(raw["humanGates"], "humanGates") }, ...raw["sourceHash"] === void 0 ? {} : { sourceHash: text3(raw["sourceHash"], "sourceHash") } };
|
|
1599
1732
|
};
|
|
1600
1733
|
var assessBlock = (manifest, completedDependencies = []) => {
|
|
1601
1734
|
const value = validateBlockManifest(manifest);
|
|
1602
|
-
const completed = new Set(completedDependencies.map((item) =>
|
|
1735
|
+
const completed = new Set(completedDependencies.map((item) => text3(item, "completedDependencies[]")));
|
|
1603
1736
|
const blockers = value.dependencies.filter((dependency) => !completed.has(dependency));
|
|
1604
1737
|
const next = blockers.length ? [`Complete dependencies: ${blockers.join(", ")}`] : value.status === "blocked" ? ["Resolve the recorded blocker before dispatch."] : ["Dispatch the block with the frozen acceptance criteria."];
|
|
1605
1738
|
return { status: blockers.length || value.status === "blocked" ? "blocked" : "ready", manifestHash: hashJson(value), blockers, next };
|
|
1606
1739
|
};
|
|
1607
|
-
var
|
|
1740
|
+
var text4 = (value, label) => {
|
|
1608
1741
|
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1609
1742
|
};
|
|
1610
1743
|
var category = (heading) => {
|
|
@@ -1615,8 +1748,8 @@ var category = (heading) => {
|
|
|
1615
1748
|
return "other";
|
|
1616
1749
|
};
|
|
1617
1750
|
var parseRetro = (markdown, source, recordedAt = (/* @__PURE__ */ new Date()).toISOString()) => {
|
|
1618
|
-
const input =
|
|
1619
|
-
const origin =
|
|
1751
|
+
const input = text4(markdown, "markdown");
|
|
1752
|
+
const origin = text4(source, "source");
|
|
1620
1753
|
if (!Number.isFinite(Date.parse(recordedAt))) fail("recordedAt must be a valid timestamp.", "INVALID_INPUT");
|
|
1621
1754
|
const records = [];
|
|
1622
1755
|
let current = "other";
|
|
@@ -1635,7 +1768,7 @@ var parseRetro = (markdown, source, recordedAt = (/* @__PURE__ */ new Date()).to
|
|
|
1635
1768
|
return records;
|
|
1636
1769
|
};
|
|
1637
1770
|
|
|
1638
|
-
// src/status.ts
|
|
1771
|
+
// src/kernel/status.ts
|
|
1639
1772
|
var required7 = (value, label) => {
|
|
1640
1773
|
return typeof value === "string" && value.trim() ? value.trim() : fail(`${label} must be a non-empty string.`, "INVALID_INPUT");
|
|
1641
1774
|
};
|
|
@@ -1687,7 +1820,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
|
|
|
1687
1820
|
const loaded = loadConfig(configPath);
|
|
1688
1821
|
const run = requireRun2(runId ? readJson(join(loaded.stateDir, "runs", runId, "run.json")) : loadLatestRun(loaded.stateDir));
|
|
1689
1822
|
const reconciliation = await reconcileRun({ configPath, runId: run.runId });
|
|
1690
|
-
const
|
|
1823
|
+
const digest4 = run.verificationDigest ?? fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
|
|
1691
1824
|
if (reconciliation.state !== "COMPLETE") fail("Only a reconciled COMPLETE run can be exported.", "INVALID_STATE");
|
|
1692
1825
|
const eventLog = new FileEventStore(loaded.stateDir);
|
|
1693
1826
|
eventLog.read(run.runId);
|
|
@@ -1702,7 +1835,7 @@ var exportEvidenceBundle = async ({ configPath, runId, outputPath, privateKeyPat
|
|
|
1702
1835
|
return bundleFile(loaded.stateDir, path);
|
|
1703
1836
|
});
|
|
1704
1837
|
const privateKey = createPrivateKey(readFileSync(privateKeyPath));
|
|
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:
|
|
1838
|
+
const unsigned = { type: "agentskit-harness-evidence-bundle", schemaVersion: EVIDENCE_BUNDLE_SCHEMA_VERSION, runId: run.runId, signerKeyId: keyId, sourceRevision: run.sourceRevision, configHash: run.configHash, contractHash: run.contractHash, verificationDigest: digest4, eventLog: eventVerification, files };
|
|
1706
1839
|
const payloadHash = sha256(JSON.stringify(unsigned));
|
|
1707
1840
|
const publicKeyPem = createPublicKey(privateKey).export({ type: "spki", format: "pem" }).toString();
|
|
1708
1841
|
const bundle = { ...unsigned, payloadHash, signature: { algorithm: "ed25519", keyId, publicKeyPem, signatureBase64: sign(null, Buffer.from(payloadHash), privateKey).toString("base64") } };
|
|
@@ -1816,6 +1949,17 @@ coordination.command("claim <input>").action((input) => {
|
|
|
1816
1949
|
const loaded = loadConfig(options().config);
|
|
1817
1950
|
print(createDispatchLedger(loaded.stateDir).claim(readJsonInput(input, "coordination identity")));
|
|
1818
1951
|
});
|
|
1952
|
+
var artifacts = program.command("artifacts").description("Inspect versioned, provenance-bound run artifacts.");
|
|
1953
|
+
artifacts.command("inspect <path>").description("Validate and print one artifact as JSON or Markdown.").action((path) => {
|
|
1954
|
+
const artifact = readArtifactFile(path);
|
|
1955
|
+
print(options().json ? artifact : renderArtifactMarkdown(artifact));
|
|
1956
|
+
});
|
|
1957
|
+
artifacts.command("list [run-id]").description("List artifacts for the latest or selected run.").action((runId) => {
|
|
1958
|
+
const loaded = loadConfig(options().config);
|
|
1959
|
+
const run = runId ? { runId } : loadLatestRun(loaded.stateDir);
|
|
1960
|
+
print(new FileArtifactStore(loaded.stateDir).list(run?.runId ?? fail("No verification run exists.", "NO_RUN")));
|
|
1961
|
+
});
|
|
1962
|
+
artifacts.command("schema").description("Print the artifact schema version.").action(() => print({ schemaVersion: ARTIFACT_SCHEMA_VERSION, types: ["plan", "finding", "decision", "repair", "blocker", "approval", "phase"] }));
|
|
1819
1963
|
program.command("start").description("Move a planned run into implementation.").action(() => print(startRun(loadConfig(options().config))));
|
|
1820
1964
|
program.command("verify").description("Execute every configured check and record evidence.").action(async () => print(await verifyRun({ configPath: options().config })));
|
|
1821
1965
|
program.command("run").description("Alias for verify, compatible with the common protocol.").action(async () => print(await verifyRun({ configPath: options().config })));
|