@cassiomc1/forgeloop 1.14.0 → 2.0.0-rc.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 (248) hide show
  1. package/CONTRIBUTING.md +11 -7
  2. package/DOCS_INDEX.md +2 -1
  3. package/ENG/c-development-eng.md +2 -2
  4. package/ENG/cpp-development-eng.md +2 -2
  5. package/ENG/dotnet-aspnetcore-development-eng.md +7 -1
  6. package/ENG/flutter-development-eng.md +4 -2
  7. package/ENG/go-development-eng.md +2 -2
  8. package/ENG/java-development-eng.md +3 -3
  9. package/ENG/nodejs-backend-development-eng.md +8 -4
  10. package/ENG/php-development-eng.md +3 -3
  11. package/ENG/python-development-eng.md +205 -0
  12. package/ENG/rust-development-eng.md +42 -1
  13. package/ENG/sql-development-eng.md +4 -4
  14. package/ENG/swift-development-eng.md +5 -4
  15. package/ENG/test-code-eng.md +1 -1
  16. package/ENG/typescript-development-eng.md +4 -4
  17. package/GUIDE_ROUTER.md +6 -0
  18. package/LICENSE +133 -21
  19. package/LOOP_SYSTEM_DESIGN.md +10 -10
  20. package/README.md +28 -39
  21. package/THIRD_PARTY_NOTICES.md +3 -4
  22. package/completions/_forgeloop +11 -3
  23. package/completions/forgeloop.bash +19 -3
  24. package/completions/forgeloop.fish +61 -1
  25. package/docs/AGENT_PROTOCOL_SUMMARY.md +50 -40
  26. package/docs/ARTIFACT_REFERENCE.md +50 -37
  27. package/docs/CLI_REFERENCE.md +147 -3
  28. package/docs/DOCUMENTATION_GUIDE.md +6 -4
  29. package/docs/GETTING_STARTED.md +3 -3
  30. package/docs/PACKAGE_CONTENTS.md +14 -13
  31. package/docs/RELEASE_CHECKLIST.md +81 -10
  32. package/docs/SELF_HOSTED_TESTS.md +121 -0
  33. package/docs/documentation-manifest.json +25 -12
  34. package/package.json +15 -10
  35. package/schemas/task-bundle.schema.json +7 -2
  36. package/scripts/generate-agent-protocol-summary.mjs +2 -2
  37. package/scripts/run-mcp-clean-tests.mjs +30 -0
  38. package/src/adapters/agent-browser/provider.js +14 -11
  39. package/src/adapters/emulated-services/provider.js +11 -4
  40. package/src/adapters/temporary-paths.js +18 -0
  41. package/src/cli.js +146 -210
  42. package/src/commands/action-show.js +5 -2
  43. package/src/commands/advance.js +5 -1
  44. package/src/commands/baseline.js +13 -11
  45. package/src/commands/checkpoint-revalidate.js +24 -8
  46. package/src/commands/complete.js +19 -3
  47. package/src/commands/contract-create.js +3 -1
  48. package/src/commands/doctor.js +36 -13
  49. package/src/commands/gate-record.js +20 -31
  50. package/src/commands/gate-revalidate.js +11 -13
  51. package/src/commands/handoff-list.js +6 -1
  52. package/src/commands/handoff-show.js +6 -1
  53. package/src/commands/migrate-protocol.js +2 -2
  54. package/src/commands/model-route.js +9 -1
  55. package/src/commands/next.js +7 -1
  56. package/src/commands/policy-diff.js +25 -4
  57. package/src/commands/progress.js +17 -11
  58. package/src/commands/record-diagnosis.js +3 -0
  59. package/src/commands/semantic-plan.js +26 -2
  60. package/src/commands/status.js +6 -1
  61. package/src/commands/storage-backup.js +15 -0
  62. package/src/commands/storage-migrate.js +5 -0
  63. package/src/commands/storage-migration-resume.js +5 -0
  64. package/src/commands/storage-migration-status.js +44 -0
  65. package/src/commands/storage-restore-resume.js +7 -0
  66. package/src/commands/storage-restore.js +8 -0
  67. package/src/commands/task-abandon.js +1 -1
  68. package/src/commands/task-list.js +8 -43
  69. package/src/commands/task-migrate.js +3 -3
  70. package/src/commands/task-recover.js +10 -2
  71. package/src/commands/task-scope.js +18 -2
  72. package/src/commands/task-show.js +16 -2
  73. package/src/commands/validate-protocol.js +29 -14
  74. package/src/commands/validate-receipt.js +8 -1
  75. package/src/config/guides.json +4 -0
  76. package/src/core/action-ledger-projection.js +10 -3
  77. package/src/core/actions.js +38 -75
  78. package/src/core/activation.js +11 -3
  79. package/src/core/approvals.js +17 -51
  80. package/src/core/artifact-registry.js +59 -2
  81. package/src/core/artifacts.js +81 -27
  82. package/src/core/attachment-paths.js +45 -0
  83. package/src/core/attestation-coverage.js +6 -1
  84. package/src/core/attestation-verifier.js +21 -2
  85. package/src/core/attestation.js +17 -6
  86. package/src/core/audit.js +6 -1
  87. package/src/core/bundles.js +160 -66
  88. package/src/core/checkpoint-revalidation.js +12 -10
  89. package/src/core/cli-command-definitions.js +109 -7
  90. package/src/core/code-manifest.js +9 -3
  91. package/src/core/command-executors.js +182 -126
  92. package/src/core/command-input.js +21 -1
  93. package/src/core/completion-artifacts.js +71 -29
  94. package/src/core/completion-ownership.js +19 -8
  95. package/src/core/completion-recovery-rebind.js +11 -1
  96. package/src/core/completion.js +60 -9
  97. package/src/core/conformance.js +2 -1
  98. package/src/core/continuity-lint.js +13 -2
  99. package/src/core/continuity-reconciliation.js +16 -3
  100. package/src/core/continuity.js +16 -8
  101. package/src/core/contract-bootstrap-recovery.js +42 -41
  102. package/src/core/contract-revision.js +8 -9
  103. package/src/core/decision/artifact.js +2 -1
  104. package/src/core/decision/events.js +9 -6
  105. package/src/core/decision/resolver.js +19 -11
  106. package/src/core/decision/service.js +63 -38
  107. package/src/core/decision/task-bindings.js +6 -1
  108. package/src/core/diagnosis-model.js +11 -0
  109. package/src/core/diagnosis.js +10 -1
  110. package/src/core/diagnostic-projection.js +29 -12
  111. package/src/core/diagnostic-record.js +28 -3
  112. package/src/core/efficiency.js +6 -1
  113. package/src/core/events.js +316 -97
  114. package/src/core/execution-prerequisites.js +22 -13
  115. package/src/core/execution.js +26 -40
  116. package/src/core/filesystem.js +11 -0
  117. package/src/core/gate-artifact.js +105 -19
  118. package/src/core/gate-provenance.js +6 -5
  119. package/src/core/handoff.js +26 -14
  120. package/src/core/history.js +39 -35
  121. package/src/core/hypothesis-projection.js +32 -0
  122. package/src/core/information-gain-projection.js +104 -100
  123. package/src/core/inspect.js +12 -3
  124. package/src/core/integration-invocation-policy.js +10 -0
  125. package/src/core/integration-resources.js +41 -34
  126. package/src/core/ledger-event-collection.js +189 -0
  127. package/src/core/ledger-relations.js +23 -0
  128. package/src/core/native-storage.js +12 -0
  129. package/src/core/next-action-continuity.js +31 -9
  130. package/src/core/next-action-phases.js +1 -1
  131. package/src/core/next-action-review-phase.js +2 -2
  132. package/src/core/next-action-verification-phase.js +2 -1
  133. package/src/core/next-action.js +21 -2
  134. package/src/core/phase.js +138 -93
  135. package/src/core/policy-engine.js +12 -13
  136. package/src/core/preflight-consistency.js +72 -59
  137. package/src/core/preflight-loaders.js +19 -12
  138. package/src/core/preflight-model.js +6 -1
  139. package/src/core/preflight.js +29 -12
  140. package/src/core/progress.js +21 -10
  141. package/src/core/protocol-info.js +3 -0
  142. package/src/core/protocol-migration.js +7 -6
  143. package/src/core/reconcile-closure.js +162 -24
  144. package/src/core/recovery-history.js +66 -47
  145. package/src/core/reflection.js +72 -77
  146. package/src/core/responsibility.js +25 -13
  147. package/src/core/route-artifact.js +13 -3
  148. package/src/core/runtime-context.js +5 -0
  149. package/src/core/schema-validation.js +8 -3
  150. package/src/core/settlement.js +12 -1
  151. package/src/core/signing/bundle-file.js +53 -0
  152. package/src/core/signing/bundle-reference.js +15 -0
  153. package/src/core/signing/sigstore.js +60 -33
  154. package/src/core/signing/statement-file.js +27 -0
  155. package/src/core/storage-runtime-registry.js +41 -0
  156. package/src/core/structural-quality/artifacts.js +20 -14
  157. package/src/core/structural-quality/service.js +81 -56
  158. package/src/core/target-layout.js +1 -1
  159. package/src/core/task-claim-state.js +52 -18
  160. package/src/core/task-command.js +21 -1
  161. package/src/core/task-conflict-inspection.js +112 -107
  162. package/src/core/task-descriptor.js +29 -22
  163. package/src/core/task-discovery.js +288 -90
  164. package/src/core/task-lock.js +66 -306
  165. package/src/core/task-migration-validation.js +5 -5
  166. package/src/core/task-migration.js +17 -287
  167. package/src/core/task-snapshot.js +51 -8
  168. package/src/core/task-storage-migration.js +18 -0
  169. package/src/core/templates.js +20 -8
  170. package/src/core/test-intelligence/prune.js +5 -1
  171. package/src/core/trace.js +154 -99
  172. package/src/core/trajectory-evaluation.js +15 -3
  173. package/src/core/trajectory-metrics.js +47 -32
  174. package/src/core/transaction.js +16 -288
  175. package/src/core/verification-scope.js +20 -12
  176. package/src/core/work-state.js +100 -59
  177. package/src/repository-index/binary-manager.js +7 -5
  178. package/src/storage/artifact-bytes.js +22 -0
  179. package/src/storage/attachment-binding.js +11 -0
  180. package/src/storage/attachment-files.js +82 -0
  181. package/src/storage/attachment-references.js +36 -0
  182. package/src/storage/backup.js +53 -0
  183. package/src/storage/capabilities.js +31 -0
  184. package/src/storage/connection-owner.js +119 -0
  185. package/src/storage/connection.js +376 -0
  186. package/src/storage/existing-project-scope.js +45 -0
  187. package/src/storage/export-destination.js +44 -0
  188. package/src/storage/exporter.js +284 -0
  189. package/src/storage/file-durability.js +46 -0
  190. package/src/storage/importer.js +461 -0
  191. package/src/storage/index.js +74 -0
  192. package/src/storage/leases.js +63 -0
  193. package/src/storage/ledger-event-snapshot.js +150 -0
  194. package/src/storage/ledger-relations.js +220 -0
  195. package/src/storage/legacy-maintenance-boundary.js +18 -0
  196. package/src/storage/legacy-signature.js +115 -0
  197. package/src/storage/maintenance-handoff.js +129 -0
  198. package/src/storage/maintenance-owner.js +42 -0
  199. package/src/storage/maintenance.js +310 -0
  200. package/src/storage/metadata-json.js +35 -0
  201. package/src/storage/migration-activate.js +59 -0
  202. package/src/storage/migration-archive.js +57 -0
  203. package/src/storage/migration-attachment-root.js +61 -0
  204. package/src/storage/migration-candidate.js +398 -0
  205. package/src/storage/migration-publication.js +143 -0
  206. package/src/storage/migration-rollback-admission.js +54 -0
  207. package/src/storage/migration-rollback-preparation.js +132 -0
  208. package/src/storage/migration-rollback-publication.js +183 -0
  209. package/src/storage/migration-rollback-release.js +66 -0
  210. package/src/storage/migration-rollback-source-stage.js +103 -0
  211. package/src/storage/migration-rollback.js +43 -0
  212. package/src/storage/migration-source-partition.js +56 -0
  213. package/src/storage/migration-source.js +255 -0
  214. package/src/storage/migration-terminal.js +39 -0
  215. package/src/storage/migration-validation.js +75 -0
  216. package/src/storage/migration.js +43 -0
  217. package/src/storage/operational-attachments.js +49 -0
  218. package/src/storage/operational-context.js +50 -0
  219. package/src/storage/project-backup.js +98 -0
  220. package/src/storage/project-bootstrap.js +24 -0
  221. package/src/storage/project-boundary.js +79 -0
  222. package/src/storage/project-read-snapshot.js +18 -0
  223. package/src/storage/project-replacement.js +137 -0
  224. package/src/storage/project-restore.js +203 -0
  225. package/src/storage/record-definitions.js +45 -0
  226. package/src/storage/repository.js +507 -0
  227. package/src/storage/restore-journal.js +62 -0
  228. package/src/storage/restore-layout.js +30 -0
  229. package/src/storage/restore-preparation.js +84 -0
  230. package/src/storage/restore-replacement-archive.js +162 -0
  231. package/src/storage/restore-replacement-owner.js +31 -0
  232. package/src/storage/restore-replacement-preparation.js +128 -0
  233. package/src/storage/restore-replacement.js +142 -0
  234. package/src/storage/restore-terminal.js +32 -0
  235. package/src/storage/runtime.js +31 -0
  236. package/src/storage/schema.js +214 -0
  237. package/src/storage/singleton-import.js +84 -0
  238. package/src/storage/snapshot-continuity.js +16 -0
  239. package/src/storage/snapshot.js +36 -0
  240. package/src/storage/storage-marker.js +79 -0
  241. package/src/storage/task-guards.js +107 -0
  242. package/src/storage/topology.js +42 -0
  243. package/src/storage/transaction.js +179 -0
  244. package/src/storage/unit-of-work.js +663 -0
  245. package/src/storage/windows-process-incarnation.js +206 -0
  246. package/.forgeloop/forgeloop.gitignore +0 -4
  247. package/LICENSE-DOCS.md +0 -13
  248. package/src/core/transaction-maintenance.js +0 -70
@@ -324,6 +324,25 @@ export function defaultCommandInputValues() {
324
324
  };
325
325
  }
326
326
 
327
+ function validateStorageRestoreInput(command, options, help) {
328
+ if (command === "storage-restore-resume" && !help && (options.writersQuiesced !== true || typeof options.operationId !== "string" || !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.operationId) || typeof options.expectedOwnerId !== "string" || !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.expectedOwnerId))) throw inputError("storage-restore-resume requires exact --operation and --expected-owner UUIDs and explicit --writers-quiesced");
329
+ if (["storage-restore", "storage-restore-resume"].includes(command) && options.replaceActive !== undefined && typeof options.replaceActive !== "boolean") throw inputError("Active replacement selection must be a boolean");
330
+ if (command === "storage-restore" && !help && (typeof options.source !== "string" || !options.source || options.writersQuiesced !== true)) throw inputError("storage-restore requires --source and explicit --writers-quiesced");
331
+ if (command === "storage-backup" && !help && (typeof options.destination !== "string" || !options.destination)) throw inputError("storage-backup requires --destination");
332
+ }
333
+
334
+ function validateStorageMigrationInput(command, options, help) {
335
+ if (command === "storage-rollback-resume" && !help && !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.expectedOwnerId ?? "")) throw inputError("storage-rollback-resume requires exact --expected-owner UUID");
336
+ if (["storage-rollback", "storage-rollback-resume"].includes(command) && !help && (typeof options.destination !== "string" || !options.destination || typeof options.legacyRoot !== "string" || !options.legacyRoot || options.writersQuiesced !== true || options.nativeWritesExcluded !== true)) throw inputError("storage-rollback requires --destination, --legacy-root, explicit --writers-quiesced and --native-writes-excluded");
337
+ if (command === "storage-migrate" && !help && (typeof options.destination !== "string" || !options.destination || options.writersQuiesced !== true)) throw inputError("storage-migrate requires --destination and explicit --writers-quiesced");
338
+ if (command === "storage-migration-resume" && !help && (typeof options.destination !== "string" || !options.destination || options.writersQuiesced !== true || typeof options.expectedOwnerId !== "string" || !/^[a-f0-9]{8}(?:-[a-f0-9]{4}){3}-[a-f0-9]{12}$/.test(options.expectedOwnerId))) throw inputError("storage-migration-resume requires --destination, exact --expected-owner UUID and explicit --writers-quiesced");
339
+ validateUnboundHandoffAcknowledgement(command, options);
340
+ }
341
+
342
+ function validateUnboundHandoffAcknowledgement(command, options) {
343
+ if (command === "storage-migration-resume" && options.acknowledgeUnboundHandoff !== undefined && typeof options.acknowledgeUnboundHandoff !== "boolean") throw inputError("--reconcile-unbound-handoff must be a boolean acknowledgement");
344
+ }
345
+
327
346
  /**
328
347
  * Canonical semantic validation shared by the CLI argv parser and
329
348
  * programmatic integrations. JSON-Schema style checks cannot express these
@@ -332,7 +351,8 @@ export function defaultCommandInputValues() {
332
351
  export function validateForgeLoopCommandInput({ command, input, help = false } = {}) {
333
352
  const options = input ?? {};
334
353
  if (!command) return;
335
-
354
+ validateStorageRestoreInput(command, options, help);
355
+ validateStorageMigrationInput(command, options, help);
336
356
  validatePolicyInput(command, options);
337
357
  validateTaskCreationInput(command, options, help);
338
358
  validateGateRecordInput(command, options, help);
@@ -1,4 +1,5 @@
1
1
  import path from "node:path";
2
+ import { getTaskTransaction, withTaskTransaction } from "./transaction.js";
2
3
  import {
3
4
  ARTIFACT_PATHS,
4
5
  canonicalFingerprint,
@@ -80,10 +81,18 @@ function requiredString(value, label) {
80
81
  return value;
81
82
  }
82
83
 
84
+ async function readBoundReceiptArtifact(target, receiptRel, packageRoot) {
85
+ try { return await readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot); }
86
+ catch (error) {
87
+ if (error.code === "E_STORAGE_PAYLOAD_MISMATCH" && error.taskIdentityMismatch) throw artifactError("E_RECEIPT_TASK_MISMATCH", "Execution receipt does not belong to the current contract task", [receiptRel]);
88
+ throw error;
89
+ }
90
+ }
91
+
83
92
  async function readCurrentReceipt(target, packageRoot, options = {}) {
84
93
  const receiptRel = options.receiptPath ?? (options.taskId ? taskArtifactPath(options.taskId, "receipt") : ARTIFACT_PATHS.receipt);
85
94
  try {
86
- return await readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot);
95
+ return await readBoundReceiptArtifact(target, receiptRel, packageRoot);
87
96
  } catch (error) {
88
97
  if (error.code === "ARTIFACT_MISSING") {
89
98
  throw artifactError(
@@ -245,7 +254,21 @@ export async function requiredEvidenceForTarget({
245
254
  ])].sort();
246
255
  }
247
256
 
248
- export async function prepareCompletion({
257
+ async function resolveCompletionTaskOptions(options) {
258
+ if (options.taskId) return options;
259
+ const state = await readWorkState(options.target, { packageRoot: options.packageRoot, statePath: options.statePath });
260
+ return { ...options, taskId: state?.taskId };
261
+ }
262
+
263
+ export async function prepareCompletion(options = {}) {
264
+ options = await resolveCompletionTaskOptions(options);
265
+ if (!options.taskId) return prepareSelectedCompletion(options);
266
+ return withTaskTransaction({ target: options.target, packageRoot: options.packageRoot,
267
+ taskId: options.taskId, operation: "prepare-completion", recordCommitEvent: true },
268
+ () => prepareSelectedCompletion(options));
269
+ }
270
+
271
+ async function prepareSelectedCompletion({
249
272
  target,
250
273
  packageRoot,
251
274
  authorityContext,
@@ -255,6 +278,7 @@ export async function prepareCompletion({
255
278
  routePath = null,
256
279
  statePath = null,
257
280
  receiptPath = null,
281
+ eventsPath,
258
282
  }) {
259
283
  const contract = await readContract(target, packageRoot, { taskId, contractPath });
260
284
  const route = await readPersistedRoute(target, packageRoot, { taskId, routePath });
@@ -266,12 +290,12 @@ export async function prepareCompletion({
266
290
  throw artifactError("E_STATE_MISSING", "Work state is required before preparing completion", [stateRel]);
267
291
  }
268
292
  if (hasExecutionStarted(state.phase)) {
269
- await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath });
293
+ await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath, eventsPath });
270
294
  }
271
295
 
272
296
  let existing = null;
273
297
  try {
274
- existing = await readJsonArtifact(target, receiptRel, "execution-receipt", packageRoot);
298
+ existing = await readBoundReceiptArtifact(target, receiptRel, packageRoot);
275
299
  await validateReceipt(existing.value, packageRoot, {
276
300
  target,
277
301
  taskId: contract.value.taskId,
@@ -463,7 +487,7 @@ export async function assertRecordCheckPrerequisites({
463
487
  [stateRel],
464
488
  );
465
489
  }
466
- await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath });
490
+ await assertExecutionPrerequisites({ target, state, packageRoot, taskId, statePath, contractPath, routePath, eventsPath });
467
491
 
468
492
  const contract = await readContract(target, packageRoot, { taskId, contractPath });
469
493
  const route = await readPersistedRoute(target, packageRoot, { taskId, routePath });
@@ -521,29 +545,15 @@ export async function assertRecordCheckPrerequisites({
521
545
  };
522
546
  }
523
547
 
524
- export async function recordCheck({
525
- target,
526
- packageRoot,
527
- id,
528
- kind = "command",
529
- requirement,
530
- status,
531
- evidenceKind,
532
- command,
533
- result,
534
- exitCode,
535
- details,
536
- executionRef,
537
- provenance,
538
- authorityContext,
539
- runtimeContext,
540
- taskId = null,
541
- contractPath = null,
542
- routePath = null,
543
- statePath = null,
544
- receiptPath = null,
545
- eventsPath = null,
546
- }) {
548
+ export async function recordCheck(options) {
549
+ validateRecordCheckInput(options);
550
+ options = await resolveCompletionTaskOptions(options);
551
+ if (!options.taskId) return recordValidatedCheck(options);
552
+ return withTaskTransaction({ target: options.target, packageRoot: options.packageRoot,
553
+ taskId: options.taskId, operation: "record-check", recordCommitEvent: true }, () => recordValidatedCheck(options));
554
+ }
555
+
556
+ function validateRecordCheckInput({ id, kind = "command", requirement, status, evidenceKind, command, result, details, executionRef, provenance }) {
547
557
  requiredString(id, "check id");
548
558
  requiredString(kind, "check kind");
549
559
  requiredString(requirement, "check requirement");
@@ -568,7 +578,31 @@ export async function recordCheck({
568
578
  && (typeof result !== "string" || result.trim() === "")) {
569
579
  throw artifactError("E_CHECK_INVALID", "record-check requires --command or --result");
570
580
  }
581
+ }
571
582
 
583
+ async function recordValidatedCheck({
584
+ target,
585
+ packageRoot,
586
+ id,
587
+ kind = "command",
588
+ requirement,
589
+ status,
590
+ evidenceKind,
591
+ command,
592
+ result,
593
+ exitCode,
594
+ details,
595
+ executionRef,
596
+ provenance,
597
+ authorityContext,
598
+ runtimeContext,
599
+ taskId = null,
600
+ contractPath = null,
601
+ routePath = null,
602
+ statePath = null,
603
+ receiptPath = null,
604
+ eventsPath = null,
605
+ }) {
572
606
  const context = await assertRecordCheckPrerequisites({
573
607
  target,
574
608
  packageRoot,
@@ -780,7 +814,15 @@ export async function recordCheck({
780
814
  };
781
815
  }
782
816
 
783
- export async function recordTerminalResult({
817
+ export async function recordTerminalResult(options = {}) {
818
+ if (!options.target) return recordTerminalResultInTransaction(options);
819
+ if (await getTaskTransaction(options.target)) return recordTerminalResultInTransaction(options);
820
+ const state = await readWorkState(options.target, { packageRoot: options.packageRoot, taskId: options.taskId, statePath: options.statePath });
821
+ if (!state) return recordTerminalResultInTransaction(options);
822
+ return withTaskTransaction({ target: options.target, taskId: state.taskId, packageRoot: options.packageRoot, operation: "record-terminal-result" }, () => recordTerminalResultInTransaction({ ...options, taskId: options.taskId ?? state.taskId }));
823
+ }
824
+
825
+ async function recordTerminalResultInTransaction({
784
826
  target,
785
827
  packageRoot,
786
828
  requirement,
@@ -1,7 +1,19 @@
1
+ import { isLedgerEventCollection } from "./ledger-event-collection.js";
1
2
  import { LIFECYCLE_MILESTONES, validateStateLedgerCoherence } from "./events.js";
2
3
 
3
4
  export const CANONICAL_COMPLETION_EVENT = "COMPLETION_VALIDATED";
4
5
 
6
+ function completionCandidates(events, taskId) {
7
+ let count = 0;
8
+ let candidate = null;
9
+ for (const event of events) {
10
+ if (event.event !== CANONICAL_COMPLETION_EVENT || event.taskId !== taskId) continue;
11
+ count += 1;
12
+ candidate = event;
13
+ }
14
+ return { count, candidate };
15
+ }
16
+
5
17
  /**
6
18
  * Canonical completion ownership proof: the minimal, validator-backed evidence
7
19
  * that the lifecycle itself officially reached COMPLETE. This is intentionally
@@ -28,31 +40,30 @@ export function validateCompletionOwnershipProof({ taskId, state, ledger }) {
28
40
  }
29
41
 
30
42
  let completionEvent = null;
31
- if (ledger && Array.isArray(ledger.events)) {
43
+ if (ledger && isLedgerEventCollection(ledger.events)) {
32
44
  if (ledger.events.some((event) => event.taskId !== taskId)) {
33
45
  errors.push({
34
46
  code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
35
47
  message: "Ledger contains an event belonging to a different task",
36
48
  });
37
49
  }
38
- const candidates = ledger.events
39
- .filter((event) => event.event === CANONICAL_COMPLETION_EVENT && event.taskId === taskId);
40
- if (candidates.length === 0) {
50
+ const { count, candidate } = completionCandidates(ledger.events, taskId);
51
+ if (count === 0) {
41
52
  errors.push({
42
53
  code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
43
54
  message: `No canonical ${CANONICAL_COMPLETION_EVENT} event exists for this task`,
44
55
  });
45
- } else if (candidates.length > 1) {
56
+ } else if (count > 1) {
46
57
  errors.push({
47
58
  code: "E_COMPLETION_OWNERSHIP_UNPROVEN",
48
59
  message: `Multiple ${CANONICAL_COMPLETION_EVENT} events exist; completion is ambiguous`,
49
60
  });
50
61
  } else {
51
- completionEvent = candidates[0];
62
+ completionEvent = candidate;
52
63
  }
53
64
  }
54
65
 
55
- if (state && ledger && Array.isArray(ledger.events)) {
66
+ if (state && ledger && isLedgerEventCollection(ledger.events)) {
56
67
  const coherenceErrors = validateStateLedgerCoherence(state, ledger.events);
57
68
  if (coherenceErrors.length > 0) {
58
69
  for (const error of coherenceErrors) {
@@ -67,7 +78,7 @@ export function validateCompletionOwnershipProof({ taskId, state, ledger }) {
67
78
  // No contradictory lifecycle activity may follow the canonical completion:
68
79
  // any milestone at or after VERIFICATION_RECORDED occurring after the
69
80
  // completion event means the lifecycle moved past terminal state.
70
- if (completionEvent && ledger && Array.isArray(ledger.events)) {
81
+ if (completionEvent && ledger && isLedgerEventCollection(ledger.events)) {
71
82
  const completionIndex = ledger.events.indexOf(completionEvent);
72
83
  const contradiction = ledger.events.slice(completionIndex + 1).find((event) => {
73
84
  const index = LIFECYCLE_MILESTONES.indexOf(event.event);
@@ -3,6 +3,7 @@ import { appendProtocolEvent, validateCompletionRecoveryAuthorization, validateE
3
3
  import { createReceipt } from "./receipt.js";
4
4
  import { taskArtifactPath } from "./task-paths.js";
5
5
  import { mutateWorkState, readWorkState } from "./work-state.js";
6
+ import { getTaskTransaction, withTaskTransaction } from "./transaction.js";
6
7
 
7
8
  function resolveArtifactPath(key, taskId, override) {
8
9
  if (override) return override;
@@ -65,7 +66,16 @@ function findMatchingRejectionEvent(events, attempt, cycle) {
65
66
  * rejection carrying the current fingerprints is appended,
66
67
  * - the execution receipt is re-bound to the current checkpoint when present.
67
68
  */
68
- export async function rebindCompletionRejectionSnapshot({
69
+ export async function rebindCompletionRejectionSnapshot(options = {}) {
70
+ if (await getTaskTransaction(options.target)) return rebindRejectionInTransaction(options);
71
+ const state = await readWorkState(options.target, { packageRoot: options.packageRoot,
72
+ taskId: options.taskId, statePath: resolveArtifactPath("state", options.taskId, options.statePath) });
73
+ if (!state) return { rebound: false };
74
+ return withTaskTransaction({ target: options.target, taskId: options.taskId ?? state.taskId,
75
+ packageRoot: options.packageRoot, operation: "rebind-completion-rejection" }, () => rebindRejectionInTransaction(options));
76
+ }
77
+
78
+ async function rebindRejectionInTransaction({
69
79
  target,
70
80
  packageRoot,
71
81
  taskId = null,
@@ -18,7 +18,9 @@ import { readConfig } from "./config.js";
18
18
  import { resolveResponsibilityStatus } from "./responsibility.js";
19
19
  import { createCodeManifest, readCodeManifest, validateCodeManifestBindings, writeCodeManifest } from "./code-manifest.js";
20
20
  import { getTaskTransaction, withTaskTransaction } from "./transaction.js";
21
+ import { operationalArtifactExists } from "../storage/operational-context.js";
21
22
  import { validateStructuralQualityCheckProvenance } from "./structural-quality/service.js";
23
+ import { withNativeReadScope } from "./native-storage.js";
22
24
 
23
25
  async function attestationConfiguration(target, packageRoot, errors) {
24
26
  try {
@@ -79,6 +81,12 @@ function issue(code, message, artifacts = [], details = {}) {
79
81
  return { code, message, artifacts, ...details };
80
82
  }
81
83
 
84
+ export function receiptReadFailure(error, relativePath) {
85
+ return error.taskIdentityMismatch
86
+ ? issue("E_RECEIPT_TASK_MISMATCH", "Execution receipt does not belong to the current contract task", [relativePath])
87
+ : issue("E_RECEIPT_INVALID", `Repair or remove the invalid execution receipt before continuing: ${error.message}`, [relativePath]);
88
+ }
89
+
82
90
  export function completionIdentityErrors({ contract, state, receipt } = {}) {
83
91
  return completionRelationshipErrors({ contract, state, receipt })
84
92
  .filter((error) => ["E_STATE_TASK_MISMATCH", "E_RECEIPT_TASK_MISMATCH"].includes(error.code));
@@ -226,6 +234,10 @@ async function loadRequired(readArtifact, errorCode, errorMessage, artifacts, er
226
234
  try {
227
235
  return await readArtifact();
228
236
  } catch (error) {
237
+ if (errorCode === "E_RECEIPT_MISSING" && error.taskIdentityMismatch) {
238
+ errors.push(receiptReadFailure(error, artifacts[0]));
239
+ return null;
240
+ }
229
241
  if (error.code === "ARTIFACT_MISSING") {
230
242
  errors.push(issue(errorCode, errorMessage, artifacts));
231
243
  return null;
@@ -235,6 +247,19 @@ async function loadRequired(readArtifact, errorCode, errorMessage, artifacts, er
235
247
  }
236
248
  }
237
249
 
250
+ async function loadCompletionActions(target, packageRoot, taskId, errors) {
251
+ const { evaluateRequiredActionReadiness } = await import("./action-readiness.js");
252
+ const evidence = taskId ? await loadRequired(async () => ({
253
+ actions: await listActions(target, { packageRoot, taskId }),
254
+ readiness: await evaluateRequiredActionReadiness({ target, packageRoot, taskId }),
255
+ }), "E_ACTION_INVALID", "Durable action evidence is not available", [taskArtifactPath(taskId, "actions")], errors) : null;
256
+ return {
257
+ actions: evidence?.actions ?? [],
258
+ readiness: evidence?.readiness
259
+ ?? { total: 0, satisfied: 0, unresolved: 0, ambiguous: 0, failed: 0, untrusted: 0, actions: [] },
260
+ };
261
+ }
262
+
238
263
  function publicationStatus(receipt) {
239
264
  if (receipt.publicationStatus) return receipt.publicationStatus;
240
265
  if (receipt.publication?.deployed) return "deployed";
@@ -246,9 +271,10 @@ function publicationStatus(receipt) {
246
271
  async function validateLedger(target, scopedTaskId, contractTaskId, state, errors, packageRoot, options = {}) {
247
272
  const eventsRel = options.eventsPath ?? (scopedTaskId ? taskArtifactPath(scopedTaskId, "events") : ARTIFACT_PATHS.events);
248
273
  const stateRel = options.statePath ?? (scopedTaskId ? taskArtifactPath(scopedTaskId, "state") : ARTIFACT_PATHS.state);
249
- await assertSafePath(target, eventsRel);
274
+ const operationalExists = operationalArtifactExists(target, eventsRel);
275
+ if (operationalExists === null) await assertSafePath(target, eventsRel);
250
276
  const eventsFilePath = ensureWithin(target, eventsRel);
251
- if (!(await fileExists(eventsFilePath))) {
277
+ if (operationalExists === false || (operationalExists === null && !(await fileExists(eventsFilePath)))) {
252
278
  errors.push(issue("E_PHASE_CHRONOLOGY_INVALID", "Protocol event ledger is required before completion", [eventsRel]));
253
279
  return { valid: false, events: [], errors: [] };
254
280
  }
@@ -286,6 +312,35 @@ export async function evaluateCompletion({
286
312
  receiptPath = null,
287
313
  eventsPath = null,
288
314
  preflightPath = null,
315
+ } = {}) {
316
+ const options = { target, packageRoot, strict, authorityContext, runtimeContext, taskId, contractPath, routePath, statePath, receiptPath, eventsPath, preflightPath };
317
+ return withNativeReadScope(target, async () => evaluateSelectedCompletion(await resolveCompletionTaskOptions(options)));
318
+ }
319
+
320
+ async function resolveCompletionTaskOptions(options) {
321
+ if (options.taskId) return options;
322
+ try {
323
+ const state = await readWorkState(options.target, { packageRoot: options.packageRoot, statePath: options.statePath });
324
+ return state ? { ...options, taskId: state.taskId } : options;
325
+ } catch {
326
+ // Preserve the evaluator's structured missing/invalid-state diagnostics.
327
+ return options;
328
+ }
329
+ }
330
+
331
+ async function evaluateSelectedCompletion({
332
+ target,
333
+ packageRoot,
334
+ strict = false,
335
+ authorityContext,
336
+ runtimeContext,
337
+ taskId = null,
338
+ contractPath = null,
339
+ routePath = null,
340
+ statePath = null,
341
+ receiptPath = null,
342
+ eventsPath = null,
343
+ preflightPath = null,
289
344
  } = {}) {
290
345
  const errors = [];
291
346
  const preflight = await evaluatePreflight({ target, packageRoot, strict, taskId, contractPath, routePath, statePath });
@@ -557,19 +612,14 @@ export async function evaluateCompletion({
557
612
  }
558
613
 
559
614
  const actionTaskId = contract?.value?.taskId ?? taskId;
560
- const durableActions = actionTaskId
561
- ? await listActions(target, { packageRoot, taskId: actionTaskId })
562
- : [];
615
+
563
616
  const contractRequirements = new Set([
564
617
  ...(contract?.value?.verification ?? []), ...(contract?.value?.successCriteria ?? []),
565
618
  ]);
566
619
  // Completion truth consumes the canonical action-readiness projection, not
567
620
  // raw state labels: a forged VERIFIED label can never satisfy a required
568
621
  // action (INV-VERIFY-02).
569
- const { evaluateRequiredActionReadiness } = await import("./action-readiness.js");
570
- const requiredActionReadiness = actionTaskId
571
- ? await evaluateRequiredActionReadiness({ target, packageRoot, taskId: actionTaskId })
572
- : { total: 0, satisfied: 0, unresolved: 0, ambiguous: 0, failed: 0, untrusted: 0, actions: [] };
622
+ const { actions: durableActions, readiness: requiredActionReadiness } = await loadCompletionActions(target, packageRoot, actionTaskId, errors);
573
623
  for (const readiness of requiredActionReadiness.actions) {
574
624
  if (readiness.status === "SATISFIED") continue;
575
625
  const action = durableActions.find((candidate) => candidate.actionId === readiness.actionId);
@@ -853,6 +903,7 @@ async function runCompleteInternal({
853
903
  }
854
904
 
855
905
  export async function runComplete(options = {}) {
906
+ options = await resolveCompletionTaskOptions(options);
856
907
  const taskId = options.taskId ?? null;
857
908
  if (options.persist !== false && taskId && !(await getTaskTransaction(options.target))) {
858
909
  return withTaskTransaction({
@@ -1,3 +1,4 @@
1
+ import { isLedgerEventCollection } from "./ledger-event-collection.js";
1
2
  import { PROTOCOL_VERSION } from "./protocol.js";
2
3
  import { createEvidence } from "./evidence.js";
3
4
  import { canonicalFingerprint } from "./artifacts.js";
@@ -36,7 +37,7 @@ export function delegationIsInScope({
36
37
  if (state?.delegatedTasks && state.delegatedTasks.length > 0) return true;
37
38
  if (state?.delegatedTaskIds && state.delegatedTaskIds.length > 0) return true;
38
39
  if (receipt?.delegatedTasks && receipt.delegatedTasks.length > 0) return true;
39
- if (Array.isArray(events) && events.some((event) => typeof event?.type === "string" && event.type.toLowerCase().includes("delegat"))) {
40
+ if (isLedgerEventCollection(events) && events.some((event) => typeof event?.type === "string" && event.type.toLowerCase().includes("delegat"))) {
40
41
  return true;
41
42
  }
42
43
  return false;
@@ -1,4 +1,6 @@
1
1
  import { assertSafePath, ensureWithin, fileExists } from "./filesystem.js";
2
+ import { operationalArtifactExists } from "../storage/operational-context.js";
3
+ import { withProjectReadSnapshot } from "../storage/project-read-snapshot.js";
2
4
 
3
5
  function finding(code, severity, field, itemId = null) {
4
6
  return { code, severity, field, itemId };
@@ -15,7 +17,16 @@ function hasOperationalHints(continuity) {
15
17
  );
16
18
  }
17
19
 
18
- export async function lintContinuity({ target, continuity, state } = {}) {
20
+ async function inspectPathExists(target, relativePath) {
21
+ return operationalArtifactExists(target, relativePath) ?? await fileExists(ensureWithin(target, relativePath));
22
+ }
23
+
24
+ export async function lintContinuity(options = {}) {
25
+ if (typeof options.target !== "string" || options.target.trim() === "") return lintSelectedContinuity(options);
26
+ return withProjectReadSnapshot(options.target, () => lintSelectedContinuity(options));
27
+ }
28
+
29
+ async function lintSelectedContinuity({ target, continuity, state } = {}) {
19
30
  const findings = [];
20
31
  if (!continuity || typeof continuity !== "object" || Array.isArray(continuity)) {
21
32
  return { status: "PASS", findings };
@@ -64,7 +75,7 @@ export async function lintContinuity({ target, continuity, state } = {}) {
64
75
  for (const [index, inspectPath] of (continuity.inspectFirst ?? []).entries()) {
65
76
  try {
66
77
  await assertSafePath(target, inspectPath);
67
- if (!(await fileExists(ensureWithin(target, inspectPath)))) {
78
+ if (!(await inspectPathExists(target, inspectPath))) {
68
79
  findings.push(finding(
69
80
  "CONTINUITY_INSPECT_PATH_MISSING",
70
81
  "WARN",
@@ -1,7 +1,10 @@
1
+ import { getOperationalStore } from "../storage/operational-context.js";
2
+ import { withEventLedgerAudit } from "./events.js";
1
3
  import { canonicalFingerprint } from "./artifacts.js";
2
4
  import { assertContinuitySemantics, readContinuity } from "./continuity.js";
3
5
  import { WORK_TRANSITIONS } from "./protocol.js";
4
6
  import { lintContinuity } from "./continuity-lint.js";
7
+ import { withNativeReadScope } from "./native-storage.js";
5
8
 
6
9
  const RECONCILIATION_CODE = "E_CONTINUITY_RECONCILIATION_REQUIRED";
7
10
 
@@ -171,6 +174,14 @@ export function classifyContinuity({
171
174
  }
172
175
 
173
176
  export async function reconcileContinuity({ target, packageRoot, taskId = null } = {}) {
177
+ const read = () => reconcileLoadedContinuity({ target, packageRoot, taskId });
178
+ return withNativeReadScope(target, () => {
179
+ if (getOperationalStore(target) && taskId) return withEventLedgerAudit(target, packageRoot, { taskId }, read);
180
+ return read();
181
+ });
182
+ }
183
+
184
+ async function reconcileLoadedContinuity({ target, packageRoot, taskId }) {
174
185
  const [{ readWorkState }, { readContract }, repository] = await Promise.all([
175
186
  import("./work-state.js"),
176
187
  import("./contract.js"),
@@ -267,12 +278,14 @@ async function resolveLatestHandoffSafe(target, { packageRoot, taskId } = {}) {
267
278
 
268
279
  async function deriveDiagnosticContextSafe({ target, packageRoot, state }) {
269
280
  try {
270
- const [{ readEvents }, { deriveDiagnosticContext }] = await Promise.all([
281
+ const [{ withEventLedgerAudit }, { deriveDiagnosticContext }] = await Promise.all([
271
282
  import("./events.js"),
272
283
  import("./reflection.js"),
273
284
  ]);
274
- const events = await readEvents(target, packageRoot, { taskId: state?.taskId ?? null });
275
- return { present: true, ...deriveDiagnosticContext(events, state) };
285
+ return await withEventLedgerAudit(target, packageRoot, { taskId: state?.taskId ?? null }, audit => {
286
+ if (!audit.valid) return { present: false };
287
+ return { present: true, ...deriveDiagnosticContext(audit.events, state) };
288
+ });
276
289
  } catch {
277
290
  return { present: false };
278
291
  }
@@ -1,4 +1,4 @@
1
- import { unlink } from "node:fs/promises";
1
+ import { withExistingProjectScope } from "../storage/existing-project-scope.js";
2
2
  import path from "node:path";
3
3
 
4
4
  import {
@@ -7,11 +7,12 @@ import {
7
7
  readJsonArtifact,
8
8
  writeJsonArtifact,
9
9
  } from "./artifacts.js";
10
- import { assertSafePath, ensureWithin, fileExists } from "./filesystem.js";
11
10
  import { PROTOCOL_VERSION, WORK_PHASES } from "./protocol.js";
12
11
  import { normalizePortableText, assertPortableContextSafe } from "./portable-context.js";
13
12
  import { getPackageRoot } from "./templates.js";
14
13
  import { taskArtifactPath } from "./task-paths.js";
14
+ import { getOperationalStore, readOperationalText } from "../storage/operational-context.js";
15
+ import { withTaskTransaction } from "./transaction.js";
15
16
 
16
17
  export const CONTINUITY_PATH = ARTIFACT_PATHS.continuity;
17
18
  export const CONTINUITY_SCHEMA_VERSION = 1;
@@ -242,10 +243,17 @@ export async function writeContinuity(target, operationalInput = {}, options = {
242
243
  }
243
244
 
244
245
  export async function clearContinuity(target, options = {}) {
245
- const relPath = options.continuityPath ?? options.relativePath ?? (options.taskId ? taskArtifactPath(options.taskId, "continuity") : CONTINUITY_PATH);
246
- await assertSafePath(target, relPath);
247
- const artifactPath = ensureWithin(target, relPath);
248
- if (!(await fileExists(artifactPath))) return { removed: false, path: relPath };
249
- await unlink(artifactPath);
250
- return { removed: true, path: relPath };
246
+ if (!options.taskId) throw Object.assign(new Error("Canonical artifact clearing requires a task identity"), { code: "E_STORAGE_OPERATION_UNSUPPORTED" });
247
+ const relPath = taskArtifactPath(options.taskId, "continuity");
248
+ const requested = options.continuityPath ?? options.relativePath ?? relPath;
249
+ if (requested !== relPath) throw Object.assign(new Error("Artifact clearing requires the canonical task path; portable exports are independent files"), { code: "E_STORAGE_OPERATION_UNSUPPORTED", artifacts: [requested] });
250
+ if (!getOperationalStore(target)) {
251
+ return withExistingProjectScope(target, () => clearContinuity(target, options));
252
+ }
253
+ const operational = readOperationalText(target, relPath);
254
+ if (operational.text === null) return { removed: false, path: relPath };
255
+ return withTaskTransaction({ target, taskId: options.taskId, operation: "clear-continuity", packageRoot: options.packageRoot }, async transaction => {
256
+ transaction.stageDelete(relPath);
257
+ return { removed: true, path: relPath };
258
+ });
251
259
  }