owlrunkit 0.19.1 → 0.19.2

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (34) hide show
  1. package/CHANGELOG.md +37 -0
  2. package/README.md +71 -0
  3. package/docs/architecture/OWLCODA_RUNKIT_0191_PROFILE_RECONCILE_PATCH_PRODUCT_DECISION_20260813.md +11 -5
  4. package/docs/architecture/OWLCODA_RUNKIT_0192_FORMAL_USABILITY_PRODUCT_DECISION_20260813.md +141 -0
  5. package/integrations/codex/skills/owlcoda-runkit/SKILL.md +50 -7
  6. package/integrations/codex/skills/owlcoda-runkit/assets/templates/config.json +3 -3
  7. package/package.json +1 -1
  8. package/packages/attest/distribution-manifest-v1.json +5 -0
  9. package/packages/attest/src/quick.mjs +37 -4
  10. package/registry-release-policy-v1.json +5 -5
  11. package/sbom.spdx.json +5 -5
  12. package/scripts/runkit-contract/core-contract.mjs +35 -2
  13. package/scripts/runkit-contract/efficiency-metrics.mjs +9 -7
  14. package/scripts/runkit-contract/formal-preflight.mjs +352 -0
  15. package/scripts/runkit-contract/formal-supplemental-evidence.mjs +713 -0
  16. package/scripts/runkit-contract/formal-workflow.mjs +529 -59
  17. package/scripts/runkit-contract/onboarding-doctor.mjs +32 -13
  18. package/scripts/runkit-contract/project-cli-resolver.mjs +30 -3
  19. package/scripts/runkit-contract/project-external-delivery.mjs +16 -9
  20. package/scripts/runkit-contract/quick-attest.mjs +24 -0
  21. package/scripts/runkit-contract/runkit-cli.mjs +365 -49
  22. package/scripts/runkit-contract/schemas/formal-check-v2.schema.json +231 -0
  23. package/scripts/runkit-contract/schemas/formal-envelope-preflight-v1.schema.json +212 -0
  24. package/scripts/runkit-contract/schemas/formal-evidence-summary-v1.schema.json +114 -0
  25. package/scripts/runkit-contract/schemas/formal-supplemental-evidence-v1.schema.json +103 -0
  26. package/scripts/runkit-contract/schemas/team-project-event-v5.schema.json +89 -0
  27. package/scripts/runkit-contract/schemas/team-project-status-v3.schema.json +173 -0
  28. package/scripts/runkit-contract/snapshot.mjs +125 -5
  29. package/scripts/runkit-contract/source-candidate.mjs +271 -69
  30. package/scripts/runkit-contract/sync-release-identity.mjs +39 -4
  31. package/scripts/runkit-contract/team-project-successor.mjs +7 -4
  32. package/scripts/runkit-contract/team-project.mjs +440 -28
  33. package/scripts/runkit-contract/usage-mode.mjs +9 -3
  34. package/scripts/runkit-contract/verification-envelope.mjs +425 -37
@@ -63,12 +63,21 @@ import {
63
63
  runHighLevelVerify,
64
64
  } from "./lifecycle-orchestration.mjs";
65
65
  import {
66
- finalizeFormalChecksV1,
66
+ finalizeFormalChecksWithinControlTransaction,
67
67
  recordFormalCheckV1,
68
68
  recordFormalEnvelopeCheckV1,
69
69
  resumeCompletedFormalFinishV1,
70
70
  validateLegacyVerificationSideEffectsV1,
71
71
  } from "./formal-workflow.mjs";
72
+ import {
73
+ formatFormalEnvelopePreflightHumanV1,
74
+ preflightFormalEnvelopeV1,
75
+ } from "./formal-preflight.mjs";
76
+ import {
77
+ attachFormalSupplementalEvidenceV1,
78
+ reattestFormalEvidenceSummaryV1,
79
+ summarizeFormalEvidenceWithinControlTransaction,
80
+ } from "./formal-supplemental-evidence.mjs";
72
81
  import {
73
82
  freezeSourceCandidateV1,
74
83
  freezeSourceCandidateV2,
@@ -162,7 +171,7 @@ import { OFFICIAL_NPM_REGISTRY } from "./registry-adoption-gate.mjs";
162
171
  import {
163
172
  appendTeamProjectEventV1,
164
173
  assignTeamProjectV1,
165
- buildTeamProjectTakeoverV2,
174
+ buildTeamProjectTakeoverV3,
166
175
  checkpointTeamProjectV1,
167
176
  closeTeamProjectVerificationV1,
168
177
  deferTeamProjectVerificationV1,
@@ -173,9 +182,11 @@ import {
173
182
  integrateTeamProjectV1,
174
183
  openTeamProjectDecisionV1,
175
184
  readTeamProjectStatusV2,
185
+ readTeamProjectStatusV3,
176
186
  rejectAndReturnTeamProjectV1,
177
187
  reopenUpstreamTeamProjectV1,
178
188
  resolveTeamProjectDecisionV1,
189
+ reviseTeamProjectWorkItemScopeV1,
179
190
  transferTeamProjectV1,
180
191
  } from "./team-project.mjs";
181
192
  import {
@@ -205,7 +216,7 @@ Quick Verification
205
216
  Low-risk, source-bound reusable evidence without writer leases or acceptance.
206
217
 
207
218
  Formal Delivery
208
- npx --no-install owlrunkit formal <start|check|finish> --workspace . --run-id <id> [...]
219
+ npx --no-install owlrunkit formal <start|preflight|check|attach-evidence|finish> --workspace . --run-id <id> [...]
209
220
  Use for multi-writer, permissions, security, funds, irreversible change,
210
221
  uncertain rollback, schema/data migration, or explicit Formal acceptance.
211
222
  Frozen dirty source
@@ -222,7 +233,7 @@ Fleet and status
222
233
  npx --no-install owlrunkit fleet discover; npx --no-install owlrunkit status --workspace .
223
234
 
224
235
  Agent team projects
225
- npx --no-install owlrunkit project <init|assign|checkpoint|handoff|reject-and-return|reopen-upstream|transfer|decision|verification|capture|target-snapshot|import-delivery|status|takeover|successor> [options]
236
+ npx --no-install owlrunkit project <init|assign|checkpoint|handoff|reject-and-return|reopen-upstream|transfer|work-item revise-scope|decision|verification|capture|target-snapshot|import-delivery|status|takeover|successor> [options]
226
237
  Durable Agent work, dependencies, decisions, gates, progress, and handoffs.
227
238
  Core successor
228
239
  npx --no-install owlrunkit core-successor <plan|apply|resume> --workspace . [...]
@@ -431,7 +442,9 @@ high-confidence unambiguous profile.
431
442
  `,
432
443
  formal: `Usage:
433
444
  npx --no-install owlrunkit formal start --workspace <directory> --run-id <id> [...]
445
+ npx --no-install owlrunkit formal preflight --workspace <directory> --envelope <file>
434
446
  npx --no-install owlrunkit formal check --workspace <directory> --run-id <id> -- <command>
447
+ npx --no-install owlrunkit formal attach-evidence --workspace <directory> --run-id <id> [...]
435
448
  npx --no-install owlrunkit formal finish --workspace <directory> --run-id <id>
436
449
  `,
437
450
  "formal start": `Usage:
@@ -439,18 +452,38 @@ high-confidence unambiguous profile.
439
452
  --goal <goal.json> --work-item <id> --owned-path <path>
440
453
 
441
454
  Creates the execution and exact writer lease.
455
+ `,
456
+ "formal preflight": `Usage:
457
+ npx --no-install owlrunkit formal preflight --workspace <directory>
458
+ --envelope <verification-envelope.json> [--json]
459
+
460
+ Reads the envelope, enforcement backend, known subprocess requirements, temporary
461
+ directory needs, and protected RunKit runtime scan conflicts without executing
462
+ the command or writing Formal evidence. It grants no authorization.
442
463
  `,
443
464
  "formal check": `Usage:
444
465
  npx --no-install owlrunkit formal check --workspace <directory> --run-id <id>
445
- --work-item <id> --check-id <id> --envelope <verification-envelope.json>
466
+ (--work-item <id> | --from-lease <id>) --check-id <id>
467
+ --envelope <verification-envelope.json>
446
468
  npx --no-install owlrunkit formal check --workspace <directory> --run-id <id>
447
- --work-item <id> --check-id <id> --cwd <directory> -- <built-in-command> [args...]
469
+ (--work-item <id> | --from-lease <id>) --check-id <id>
470
+ --cwd <directory> -- <built-in-command> [args...]
448
471
 
449
472
  Verification Envelopes admit real project commands only when file, network,
450
473
  process, environment, output, timeout, and cleanup constraints are enforced.
474
+ `,
475
+ "formal attach-evidence": `Usage:
476
+ npx --no-install owlrunkit formal attach-evidence --workspace <directory>
477
+ --run-id <id> --evidence-id <id> --kind supplemental-local|networked
478
+ --receipt <quick-receipt.json> [--json]
479
+
480
+ Attests one passing Quick receipt and binds it to the current Formal source
481
+ candidate as non-gating supplemental evidence. It cannot replace a Formal check
482
+ or grant Git, release, deployment, production, or business authorization.
451
483
  `,
452
484
  "formal finish": `Usage:
453
485
  npx --no-install owlrunkit formal finish --workspace <directory> --run-id <id>
486
+ [--decision accepted|rejected|blocked] [--finalize-id <id>]
454
487
 
455
488
  Runs the final source, evidence, permission, closeout, and lease-release path.
456
489
  `,
@@ -579,6 +612,13 @@ No self-reported percentage is accepted.
579
612
  Measured WorkItems require --completed-units on every checkpoint. A completed
580
613
  checkpoint must use the WorkItem's declared total. Example:
581
614
  npx --no-install owlrunkit project checkpoint --workspace . --checkpoint-id checkpoint-x --at 2026-08-11T00:00:00.000Z --assignment-id assign-x --work-item x --state completed --summary 'Completed x.' --completed-units 5 --evidence receipt:quick
615
+ `,
616
+ "project init": `Usage:
617
+ npx --no-install owlrunkit project init --workspace <directory>
618
+ --definition <project-definition.json> [--json]
619
+
620
+ Creates one project-owned definition and empty immutable event journal. It does
621
+ not grant Git, release, deployment, production, or business authorization.
582
622
  `,
583
623
  "project assign": `Usage:
584
624
  npx --no-install owlrunkit project assign --workspace <directory>
@@ -588,6 +628,55 @@ checkpoint must use the WorkItem's declared total. Example:
588
628
 
589
629
  Assigns one current WorkItem owner without granting Git, release, deployment,
590
630
  production, business, or other authorization.
631
+ `,
632
+ "project handoff": `Usage:
633
+ npx --no-install owlrunkit project handoff --workspace <directory>
634
+ --handoff-id <id> --at <ISO-UTC> --assignment-id <current>
635
+ --work-item <id> --from-agent <id> --to-agent <id>
636
+ --summary <text> --next <text> --evidence <ref> [--evidence <ref> ...] [--json]
637
+
638
+ Records an immutable handoff without assigning successor authority or granting
639
+ Git, release, deployment, production, or business authorization.
640
+ `,
641
+ "project decision": `Usage:
642
+ npx --no-install owlrunkit project decision --workspace <directory>
643
+ --open --decision-id <id> --at <ISO-UTC> --title <text> --question <text>
644
+ --owner-agent <id> [--blocking-work-item <id> ...]
645
+ --option <text> [--option <text> ...] [--json]
646
+ npx --no-install owlrunkit project decision --workspace <directory>
647
+ --resolve --decision-id <id> --at <ISO-UTC> --resolution <text>
648
+ --rationale <text> --evidence <ref> [--evidence <ref> ...] [--json]
649
+
650
+ Opens or resolves one durable project decision. Resolution records truth but
651
+ does not grant Git, release, deployment, production, or business authorization.
652
+ `,
653
+ "project verification": `Usage:
654
+ npx --no-install owlrunkit project verification --workspace <directory>
655
+ --defer --verification-id <id> --at <ISO-UTC> --work-item <id>
656
+ --owner-agent <id> --check <check-id> [--check <check-id> ...]
657
+ --reason <text> --due-gate <gate-id> [--json]
658
+ npx --no-install owlrunkit project verification --workspace <directory>
659
+ --close --verification-id <id> --at <ISO-UTC>
660
+ --disposition <verified|no_longer_required> --summary <text>
661
+ [--evidence <ref> ...] [--decision <resolved-decision-id> ...] [--json]
662
+
663
+ Records an intentionally deferred check or closes it with evidence. It does not
664
+ execute the check and grants no additional authorization.
665
+ `,
666
+ "project integrate": `Usage:
667
+ npx --no-install owlrunkit project integrate --workspace <directory>
668
+ --gate <gate-id> --at <ISO-UTC> --summary <text>
669
+ --evidence <ref> [--evidence <ref> ...] [--json]
670
+
671
+ Records one passed integration gate after its dependencies and deferred checks
672
+ are satisfied. It does not perform Git integration or grant release authority.
673
+ `,
674
+ "project capture": `Usage:
675
+ npx --no-install owlrunkit project capture --workspace <directory>
676
+ --request <legacy-event.json> [--json]
677
+
678
+ Compatibility-only raw event capture. External source delivery must use
679
+ project import-delivery. This action grants no repository or business authority.
591
680
  `,
592
681
  "project reject-and-return": `Usage:
593
682
  npx --no-install owlrunkit project reject-and-return --workspace <directory> --rework-id <id> --at <ISO-UTC>
@@ -622,6 +711,19 @@ authority is granted.
622
711
  Atomically records checkpoint, handoff, and successor-assignment facts inside one
623
712
  immutable Event V4 envelope. Terminal WorkItems cannot transfer. No Git, release,
624
713
  deployment, production, or business authority is granted.
714
+ `,
715
+ "project work-item revise-scope": `Usage:
716
+ npx --no-install owlrunkit project work-item revise-scope --workspace <directory>
717
+ --revision-id <id> --at <ISO-UTC> --assignment-id <current>
718
+ --work-item <id> --owner-agent <current-owner>
719
+ [--owned-path <path> ...]
720
+ [--measurable-unit <unit> --measurable-total <positive-int> | --unmeasured]
721
+ --reason <text> [--evidence <ref> ...] [--json]
722
+
723
+ Records an immutable Event V5 that supersedes the WorkItem's prior scope. Omitted
724
+ ownedPaths or measurable fields retain their current values. The event never
725
+ rewrites the project definition and grants no Git, release, deployment,
726
+ production, or business authority.
625
727
  `,
626
728
  "project target-snapshot": `Usage:
627
729
  npx --no-install owlrunkit project target-snapshot --workspace <controller>
@@ -843,7 +945,7 @@ function formatTeamProjectIntegrationHuman(result) {
843
945
  : String(result.nextAction).replace(/\s+/gu, " ").trim();
844
946
  return [
845
947
  `${disposition} project integration gate ${result.gateId}.`,
846
- `Project state: ${result.overall}; next: ${nextAction}${result.nextActorId ? ` (owner ${result.nextActorId})` : ""}`,
948
+ `Work ledger: ${result.overall}; delivery: ${result.deliveryDisposition}; next: ${nextAction}${result.nextActorId ? ` (owner ${result.nextActorId})` : ""}`,
847
949
  "Git/release not performed.",
848
950
  "",
849
951
  ].join("\n");
@@ -857,6 +959,9 @@ function formatTeamProjectCheckpointHuman(result) {
857
959
  return [
858
960
  `${disposition} checkpoint ${result.checkpointId} for WorkItem ${result.workItemId}: ${result.state}.`,
859
961
  `Project state: ${result.overall}; next: ${nextAction}${result.nextActorId ? ` (owner ${result.nextActorId})` : ""}`,
962
+ ...(result.warnings?.length > 0
963
+ ? [`Warning: ${result.warnings.map(row => row.code).join(", ")}.`]
964
+ : []),
860
965
  "",
861
966
  ].join("\n");
862
967
  }
@@ -1912,7 +2017,7 @@ function inspect(options) {
1912
2017
  const onboarding = onboardingProjection(root, recovery);
1913
2018
  const projectDefinitionPath = runtimePath(root, "project", "definition.json");
1914
2019
  const projectDriver = pathEntryExists(projectDefinitionPath)
1915
- ? readTeamProjectStatusV2({ workspaceRoot: root })
2020
+ ? readTeamProjectStatusV3({ workspaceRoot: root })
1916
2021
  : null;
1917
2022
  const summary = buildInspectSummary(result, {
1918
2023
  maintenanceNextAction: onboarding.maintenanceNextAction ?? null,
@@ -2014,6 +2119,20 @@ function closeout(options) {
2014
2119
  releasedLeaseIds,
2015
2120
  };
2016
2121
  }
2122
+ const formalEvidenceSummary = options["formal-evidence-summary"] ?? null;
2123
+ if (
2124
+ formalEvidenceSummary !== null
2125
+ && (
2126
+ decision !== "accepted"
2127
+ || formalEvidenceSummary.status !== "formal_evidence_summarized"
2128
+ || typeof formalEvidenceSummary.evidenceSummaryPath !== "string"
2129
+ || !/^[a-f0-9]{64}$/u.test(
2130
+ formalEvidenceSummary.evidenceSummarySha256 ?? "",
2131
+ )
2132
+ )
2133
+ ) {
2134
+ throw new Error("Formal evidence summary binding is invalid for closeout.");
2135
+ }
2017
2136
  const created = createCoreArtifact({
2018
2137
  core: pin,
2019
2138
  producer: { adapterKind: "codex", adapterVersion: "0.1.0" },
@@ -2023,7 +2142,24 @@ function closeout(options) {
2023
2142
  authorizationGranted: false,
2024
2143
  ...(acceptedVerification ? { verification: acceptedVerification } : {}),
2025
2144
  },
2026
- extensions: { "dev.owlcoda.adapter.codex": {} },
2145
+ extensions: {
2146
+ "dev.owlcoda.adapter.codex": {},
2147
+ ...(formalEvidenceSummary === null
2148
+ ? {}
2149
+ : {
2150
+ "dev.owlcoda.formal-evidence": {
2151
+ schemaVersion: "OwlCodaRunKitFormalEvidenceCloseoutBindingV1",
2152
+ summaryPath: formalEvidenceSummary.evidenceSummaryPath,
2153
+ summarySha256: formalEvidenceSummary.evidenceSummarySha256,
2154
+ formalCheckCount: formalEvidenceSummary.formalCheckCount,
2155
+ supplementalEvidenceCount:
2156
+ formalEvidenceSummary.supplementalEvidenceCount,
2157
+ staleSupplementalEvidenceCount:
2158
+ formalEvidenceSummary.staleSupplementalEvidenceCount,
2159
+ authorizationGranted: false,
2160
+ },
2161
+ }),
2162
+ },
2027
2163
  });
2028
2164
  const closeoutPath = path.join(executionRoot, "closeout-receipt.json");
2029
2165
  writeJsonExclusiveAtomically(closeoutPath, created);
@@ -2038,15 +2174,16 @@ function closeout(options) {
2038
2174
  return { status: "closed", exitCode: 0, runId, decision, authorizationGranted: false, ...created };
2039
2175
  }
2040
2176
 
2041
- function finishExecution(options) {
2177
+ function finishExecutionWithinControlTransaction(options, {
2178
+ formalEvidenceSummary = null,
2179
+ } = {}) {
2042
2180
  const root = workspace(options);
2043
2181
  const runId = safeRunId(requireOption(options, "run-id"));
2044
2182
  const decision = requireOption(options, "decision");
2045
2183
  if (!new Set(["accepted", "rejected", "blocked"]).has(decision)) {
2046
2184
  throw new Error("decision must be accepted, rejected, or blocked.");
2047
2185
  }
2048
- return withControlTransaction(root, () => {
2049
- const executionRoot = runtimePath(root, "executions", runId);
2186
+ const executionRoot = runtimePath(root, "executions", runId);
2050
2187
  const closeoutPath = path.join(executionRoot, "closeout-receipt.json");
2051
2188
  if (pathEntryExists(closeoutPath)) {
2052
2189
  if (lstatSync(closeoutPath).isSymbolicLink()) {
@@ -2090,6 +2227,9 @@ function finishExecution(options) {
2090
2227
  closed = closeout({
2091
2228
  ...options,
2092
2229
  ...(gate ? { "gate-input": gate.gateInputPath } : {}),
2230
+ ...(formalEvidenceSummary === null
2231
+ ? {}
2232
+ : { "formal-evidence-summary": formalEvidenceSummary }),
2093
2233
  });
2094
2234
  if (closed.status !== "closed") {
2095
2235
  rollbackReleasedLeases();
@@ -2109,7 +2249,14 @@ function finishExecution(options) {
2109
2249
  closeoutArtifactSha256: closed.artifactSha256,
2110
2250
  authorizationGranted: false,
2111
2251
  };
2112
- });
2252
+ }
2253
+
2254
+ function finishExecution(options) {
2255
+ const root = workspace(options);
2256
+ return withControlTransaction(
2257
+ root,
2258
+ () => finishExecutionWithinControlTransaction(options),
2259
+ );
2113
2260
  }
2114
2261
 
2115
2262
  function requestCommand(options, handler) {
@@ -2161,7 +2308,12 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2161
2308
  "profiles",
2162
2309
  "project",
2163
2310
  ]).has(command);
2164
- const [action, ...nestedOptions] = nested ? optionValues : [null, ...optionValues];
2311
+ let [action, ...nestedOptions] = nested ? optionValues : [null, ...optionValues];
2312
+ if (command === "project" && action === "work-item"
2313
+ && nestedOptions[0] === "revise-scope") {
2314
+ action = "work-item revise-scope";
2315
+ nestedOptions = nestedOptions.slice(1);
2316
+ }
2165
2317
  const helpRequested = optionValues.includes("--help")
2166
2318
  || optionValues.includes("-h");
2167
2319
  if (helpRequested) {
@@ -2213,6 +2365,8 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2213
2365
  ? ["evidence", "blocker", "decision"]
2214
2366
  : command === "project" && action === "decision"
2215
2367
  ? ["blocking-work-item", "option", "evidence"]
2368
+ : command === "project" && action === "work-item revise-scope"
2369
+ ? ["owned-path", "evidence"]
2216
2370
  : command === "efficiency" && action === "record"
2217
2371
  ? ["evidence"]
2218
2372
  : [],
@@ -2242,11 +2396,15 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2242
2396
  ? ["json"]
2243
2397
  : command === "bootstrap"
2244
2398
  ? ["json", "dry-run", "apply"]
2399
+ : command === "formal" && new Set(["preflight", "attach-evidence"]).has(action)
2400
+ ? ["json"]
2245
2401
  : command === "project" && action === "decision"
2246
2402
  ? ["json", "open", "resolve"]
2247
2403
  : command === "project" && action === "verification"
2248
2404
  ? ["json", "defer", "close"]
2249
- : command === "project" && new Set(["assign", "handoff", "reject-and-return", "reopen-upstream", "transfer", "checkpoint", "integrate", "status", "takeover", "successor"]).has(action)
2405
+ : command === "project" && action === "work-item revise-scope"
2406
+ ? ["json", "unmeasured"]
2407
+ : command === "project" && new Set(["init", "assign", "handoff", "reject-and-return", "reopen-upstream", "transfer", "checkpoint", "integrate", "capture", "status", "takeover", "successor"]).has(action)
2250
2408
  ? ["json"]
2251
2409
  : command === "project" && new Set(["target-snapshot", "import-delivery"]).has(action)
2252
2410
  ? ["json", "dry-run", "apply"]
@@ -2361,6 +2519,10 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2361
2519
  ]);
2362
2520
  const recommendation = recommendUsageModeV2({
2363
2521
  workspaceRoot: root,
2522
+ projectDriverPresent: existsSync(path.join(
2523
+ root,
2524
+ ".owlcoda/runkit/project/definition.json",
2525
+ )),
2364
2526
  requestedMode: options["requested-mode"] ?? "auto",
2365
2527
  needsDurableEvidence: options["needs-evidence"] === true,
2366
2528
  exactCommandCount: parseCanonicalNonnegativeSafeInteger(
@@ -2394,6 +2556,10 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2394
2556
  assertOnlyOptions(options, ["workspace", "json"]);
2395
2557
  const recommendation = recommendUsageModeV2({
2396
2558
  workspaceRoot: root,
2559
+ projectDriverPresent: existsSync(path.join(
2560
+ root,
2561
+ ".owlcoda/runkit/project/definition.json",
2562
+ )),
2397
2563
  requestedMode: "auto",
2398
2564
  needsDurableEvidence: false,
2399
2565
  exactCommandCount: 0,
@@ -2974,12 +3140,37 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2974
3140
  throw new Error("Usage: npx --no-install owlrunkit deployment <prepare|execute> [options]");
2975
3141
  }
2976
3142
  if (command === "formal") {
3143
+ const allowedFormalOptions = {
3144
+ start: ["workspace", "run-id", "goal", "work-item", "owned-path"],
3145
+ preflight: ["workspace", "envelope", "json"],
3146
+ check: ["workspace", "run-id", "work-item", "from-lease", "check-id", "envelope", "cwd"],
3147
+ "attach-evidence": ["workspace", "run-id", "evidence-id", "kind", "receipt", "json"],
3148
+ finish: ["workspace", "run-id", "decision", "finalize-id"],
3149
+ };
3150
+ if (!Object.hasOwn(allowedFormalOptions, action)) {
3151
+ throw new Error("Usage: npx --no-install owlrunkit formal <start|preflight|check|attach-evidence|finish> [options]");
3152
+ }
3153
+ assertOnlyOptions(options, allowedFormalOptions[action]);
2977
3154
  if (action === "start") {
2978
3155
  return startExecution({
2979
3156
  ...options,
2980
3157
  "work-item": options["work-item"] ?? "delivery",
2981
3158
  });
2982
3159
  }
3160
+ if (action === "preflight") {
3161
+ const root = workspace(options);
3162
+ const envelope = readJson(realpathSync(requireOption(options, "envelope")));
3163
+ const preflight = preflightFormalEnvelopeV1({
3164
+ workspaceRoot: root,
3165
+ envelope,
3166
+ });
3167
+ return options.json === true
3168
+ ? preflight
3169
+ : withHumanOutput(
3170
+ preflight,
3171
+ formatFormalEnvelopePreflightHumanV1(preflight),
3172
+ );
3173
+ }
2983
3174
  if (action === "check") {
2984
3175
  const workItemId = options["work-item"] ?? options["from-lease"] ?? "delivery";
2985
3176
  if (
@@ -2993,12 +3184,17 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
2993
3184
  if (commandArgv.length > 0) {
2994
3185
  throw new Error("formal check accepts either --envelope or an exact built-in command, not both.");
2995
3186
  }
3187
+ if (options.cwd !== undefined) {
3188
+ throw new Error("formal check --cwd is only valid with an exact built-in command.");
3189
+ }
3190
+ const root = workspace(options);
3191
+ const envelope = readJson(realpathSync(requireOption(options, "envelope")));
2996
3192
  return await recordFormalEnvelopeCheckV1({
2997
- workspaceRoot: workspace(options),
3193
+ workspaceRoot: root,
2998
3194
  runId: safeRunId(requireOption(options, "run-id")),
2999
3195
  workItemId,
3000
3196
  checkId: requireOption(options, "check-id"),
3001
- envelope: readJson(realpathSync(requireOption(options, "envelope"))),
3197
+ envelope,
3002
3198
  });
3003
3199
  }
3004
3200
  return recordFormalCheckV1({
@@ -3010,6 +3206,15 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
3010
3206
  commandArgv,
3011
3207
  });
3012
3208
  }
3209
+ if (action === "attach-evidence") {
3210
+ return attachFormalSupplementalEvidenceV1({
3211
+ workspaceRoot: workspace(options),
3212
+ runId: safeRunId(requireOption(options, "run-id")),
3213
+ evidenceId: requireOption(options, "evidence-id"),
3214
+ kind: requireOption(options, "kind"),
3215
+ receiptPath: requireOption(options, "receipt"),
3216
+ });
3217
+ }
3013
3218
  if (action === "finish") {
3014
3219
  const decision = options.decision ?? "accepted";
3015
3220
  if (!new Set(["accepted", "rejected", "blocked"]).has(decision)) {
@@ -3024,36 +3229,92 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
3024
3229
  finalizeId,
3025
3230
  decision,
3026
3231
  });
3027
- if (resumed !== null) return resumed;
3028
- let finalized = null;
3029
- if (decision === "accepted") {
3030
- finalized = finalizeFormalChecksV1({
3031
- workspaceRoot: root,
3032
- runId,
3033
- finalizeId,
3034
- });
3035
- if (finalized.status !== "accepted_passed") return finalized;
3232
+ if (resumed !== null) {
3233
+ const evidenceSummary = decision === "accepted"
3234
+ ? reattestFormalEvidenceSummaryV1({
3235
+ workspaceRoot: root,
3236
+ runId,
3237
+ summaryId: `${finalizeId}-evidence`,
3238
+ })
3239
+ : null;
3240
+ if (
3241
+ resumed.formalEvidenceBinding !== null
3242
+ && (
3243
+ evidenceSummary === null
3244
+ || evidenceSummary.evidenceSummaryPath
3245
+ !== resumed.formalEvidenceBinding.summaryPath
3246
+ || evidenceSummary.evidenceSummarySha256
3247
+ !== resumed.formalEvidenceBinding.summarySha256
3248
+ || evidenceSummary.formalCheckCount
3249
+ !== resumed.formalEvidenceBinding.formalCheckCount
3250
+ || evidenceSummary.supplementalEvidenceCount
3251
+ !== resumed.formalEvidenceBinding.supplementalEvidenceCount
3252
+ || evidenceSummary.staleSupplementalEvidenceCount
3253
+ !== resumed.formalEvidenceBinding.staleSupplementalEvidenceCount
3254
+ )
3255
+ ) {
3256
+ throw new Error(
3257
+ "Existing closeout does not match its Formal evidence summary.",
3258
+ );
3259
+ }
3260
+ const { formalEvidenceBinding: _formalEvidenceBinding, ...resumedResult } = resumed;
3261
+ return {
3262
+ ...resumedResult,
3263
+ supplementalEvidenceCount:
3264
+ evidenceSummary?.supplementalEvidenceCount ?? 0,
3265
+ staleSupplementalEvidenceCount:
3266
+ evidenceSummary?.staleSupplementalEvidenceCount ?? 0,
3267
+ evidenceSummaryPath: evidenceSummary?.evidenceSummaryPath,
3268
+ evidenceSummarySha256: evidenceSummary?.evidenceSummarySha256,
3269
+ };
3036
3270
  }
3037
- const finished = finishExecution({
3038
- ...options,
3039
- decision,
3271
+ return withControlTransaction(root, () => {
3272
+ let finalized = null;
3273
+ let evidenceSummary = null;
3274
+ if (decision === "accepted") {
3275
+ finalized = finalizeFormalChecksWithinControlTransaction({
3276
+ workspaceRoot: root,
3277
+ runId,
3278
+ finalizeId,
3279
+ });
3280
+ if (finalized.status !== "accepted_passed") return finalized;
3281
+ evidenceSummary = summarizeFormalEvidenceWithinControlTransaction({
3282
+ workspaceRoot: root,
3283
+ runId,
3284
+ summaryId: `${finalizeId}-evidence`,
3285
+ });
3286
+ if (evidenceSummary.status !== "formal_evidence_summarized") {
3287
+ return evidenceSummary;
3288
+ }
3289
+ }
3290
+ const finished = finishExecutionWithinControlTransaction({
3291
+ ...options,
3292
+ decision,
3293
+ }, { formalEvidenceSummary: evidenceSummary });
3294
+ if (finished.status !== "finished") return finished;
3295
+ return {
3296
+ status: "formal_finished",
3297
+ exitCode: 0,
3298
+ runId: finished.runId,
3299
+ decision,
3300
+ snapshotCount: finalized?.snapshotCount ?? 0,
3301
+ staleCheckCount: finalized?.staleCheckCount ?? 0,
3302
+ supplementalEvidenceCount:
3303
+ evidenceSummary?.supplementalEvidenceCount ?? 0,
3304
+ staleSupplementalEvidenceCount:
3305
+ evidenceSummary?.staleSupplementalEvidenceCount ?? 0,
3306
+ evidenceSummaryPath: evidenceSummary?.evidenceSummaryPath ?? null,
3307
+ evidenceSummarySha256:
3308
+ evidenceSummary?.evidenceSummarySha256 ?? null,
3309
+ sourceFingerprint: finalized?.sourceFingerprint ?? null,
3310
+ activeReceiptSha256: finished.activeReceiptSha256,
3311
+ closeoutArtifactSha256: finished.closeoutArtifactSha256,
3312
+ releasedLeaseIds: finished.releasedLeaseIds,
3313
+ authorizationGranted: false,
3314
+ };
3040
3315
  });
3041
- if (finished.status !== "finished") return finished;
3042
- return {
3043
- status: "formal_finished",
3044
- exitCode: 0,
3045
- runId: finished.runId,
3046
- decision,
3047
- snapshotCount: finalized?.snapshotCount ?? 0,
3048
- staleCheckCount: finalized?.staleCheckCount ?? 0,
3049
- sourceFingerprint: finalized?.sourceFingerprint ?? null,
3050
- activeReceiptSha256: finished.activeReceiptSha256,
3051
- closeoutArtifactSha256: finished.closeoutArtifactSha256,
3052
- releasedLeaseIds: finished.releasedLeaseIds,
3053
- authorizationGranted: false,
3054
- };
3055
3316
  }
3056
- throw new Error("Usage: npx --no-install owlrunkit formal <start|check|finish> [options]");
3317
+ throw new Error("Usage: npx --no-install owlrunkit formal <start|preflight|check|attach-evidence|finish> [options]");
3057
3318
  }
3058
3319
  if (command === "candidate") {
3059
3320
  if (action === "freeze") {
@@ -3123,7 +3384,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
3123
3384
  }
3124
3385
  if (command === "project") {
3125
3386
  const allowedProjectOptions = {
3126
- init: ["workspace", "definition"],
3387
+ init: ["workspace", "definition", "json"],
3127
3388
  assign: [
3128
3389
  "workspace", "request", "assignment-id", "at", "work-item", "agent",
3129
3390
  "supersedes", "execution-run-id", "execution-work-item", "json",
@@ -3145,6 +3406,11 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
3145
3406
  "from-agent", "to-agent", "state", "summary", "completed-units",
3146
3407
  "evidence", "blocker", "decision", "next", "source-fingerprint", "json",
3147
3408
  ],
3409
+ "work-item revise-scope": [
3410
+ "workspace", "revision-id", "at", "assignment-id", "work-item",
3411
+ "owner-agent", "owned-path", "measurable-unit", "measurable-total",
3412
+ "unmeasured", "reason", "evidence", "json",
3413
+ ],
3148
3414
  decision: [
3149
3415
  "workspace", "request", "open", "resolve", "decision-id", "at", "title",
3150
3416
  "question", "owner-agent", "blocking-work-item", "option", "resolution",
@@ -3161,7 +3427,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
3161
3427
  "disposition", "summary", "evidence", "decision", "json",
3162
3428
  ],
3163
3429
  integrate: ["workspace", "request", "gate", "at", "summary", "evidence", "json"],
3164
- capture: ["workspace", "request"],
3430
+ capture: ["workspace", "request", "json"],
3165
3431
  "target-snapshot": [
3166
3432
  "workspace", "snapshot-id", "at", "work-item", "target-workspace",
3167
3433
  "allowed-path", "dry-run", "apply", "json",
@@ -3371,6 +3637,56 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
3371
3637
  ? transferred
3372
3638
  : withHumanOutput(transferred, formatTeamProjectTransferHuman(transferred));
3373
3639
  }
3640
+ if (action === "work-item revise-scope") {
3641
+ const currentStatus = readTeamProjectStatusV3({ workspaceRoot: root });
3642
+ const workItemId = requireOption(options, "work-item");
3643
+ const currentWorkItem = currentStatus.workItems.find(
3644
+ row => row.workItemId === workItemId,
3645
+ );
3646
+ if (!currentWorkItem) throw new Error(`Unknown team project WorkItem: ${workItemId}.`);
3647
+ const hasUnit = options["measurable-unit"] !== undefined;
3648
+ const hasTotal = options["measurable-total"] !== undefined;
3649
+ if (options.unmeasured === true && (hasUnit || hasTotal)) {
3650
+ throw new Error("--unmeasured cannot be combined with measurable-unit or measurable-total.");
3651
+ }
3652
+ if (hasUnit !== hasTotal) {
3653
+ throw new Error("measurable-unit and measurable-total must be supplied together.");
3654
+ }
3655
+ const measurable = options.unmeasured === true
3656
+ ? null
3657
+ : hasUnit
3658
+ ? {
3659
+ unit: requireOption(options, "measurable-unit"),
3660
+ total: parseCanonicalNonnegativeSafeInteger(
3661
+ requireOption(options, "measurable-total"),
3662
+ "measurable-total",
3663
+ ),
3664
+ }
3665
+ : currentWorkItem.measurable;
3666
+ if (measurable?.total === 0) {
3667
+ throw new Error("measurable-total must be a positive safe integer.");
3668
+ }
3669
+ const revised = reviseTeamProjectWorkItemScopeV1({
3670
+ workspaceRoot: root,
3671
+ revisionId: requireOption(options, "revision-id"),
3672
+ occurredAt: requireOption(options, "at"),
3673
+ assignmentId: requireOption(options, "assignment-id"),
3674
+ workItemId,
3675
+ ownerAgentId: requireOption(options, "owner-agent"),
3676
+ ownedPaths: options["owned-path"] ?? currentWorkItem.ownedPaths,
3677
+ measurable,
3678
+ reason: requireOption(options, "reason"),
3679
+ evidenceRefs: options.evidence ?? [],
3680
+ });
3681
+ return options.json === true
3682
+ ? revised
3683
+ : withHumanOutput(revised, [
3684
+ `Revised WorkItem ${revised.workItemId} scope as ${revised.revisionId}.`,
3685
+ `Owned paths: ${revised.ownedPaths.join(", ")}.`,
3686
+ "Project definition remains immutable; no Git or release authority was granted.",
3687
+ "",
3688
+ ].join("\n"));
3689
+ }
3374
3690
  if (action === "decision") {
3375
3691
  const directDecisionFields = [
3376
3692
  "open",
@@ -3667,13 +3983,13 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
3667
3983
  });
3668
3984
  }
3669
3985
  if (action === "status") {
3670
- const status = readTeamProjectStatusV2({ workspaceRoot: root });
3986
+ const status = readTeamProjectStatusV3({ workspaceRoot: root });
3671
3987
  return options.json === true
3672
3988
  ? status
3673
3989
  : withHumanOutput(status, formatTeamProjectStatusHumanV2(status));
3674
3990
  }
3675
3991
  if (action === "takeover") {
3676
- const takeover = buildTeamProjectTakeoverV2({
3992
+ const takeover = buildTeamProjectTakeoverV3({
3677
3993
  workspaceRoot: root,
3678
3994
  agentId: requireOption(options, "agent"),
3679
3995
  });
@@ -3694,7 +4010,7 @@ export async function runCli(argv = process.argv.slice(2), runtimeHooks = {}) {
3694
4010
  ? successor
3695
4011
  : withHumanOutput(successor, formatTeamProjectSuccessorHumanV1(successor));
3696
4012
  }
3697
- throw new Error("Usage: npx --no-install owlrunkit project <init|assign|checkpoint|handoff|reject-and-return|reopen-upstream|transfer|decision|verification|integrate|capture|target-snapshot|import-delivery|status|takeover|successor> [options]");
4013
+ throw new Error("Usage: npx --no-install owlrunkit project <init|assign|checkpoint|handoff|reject-and-return|reopen-upstream|transfer|work-item revise-scope|decision|verification|integrate|capture|target-snapshot|import-delivery|status|takeover|successor> [options]");
3698
4014
  }
3699
4015
  if (command === "profiles") {
3700
4016
  const root = requireOption(options, "workspace");