@cassiomc1/forgeloop 1.14.0 → 2.0.0-rc.4

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 (250) 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 +30 -40
  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 +110 -10
  32. package/docs/SELF_HOSTED_TESTS.md +121 -0
  33. package/docs/TROUBLESHOOTING.md +33 -0
  34. package/docs/documentation-manifest.json +25 -12
  35. package/package.json +15 -10
  36. package/schemas/task-bundle.schema.json +7 -2
  37. package/scripts/generate-agent-protocol-summary.mjs +2 -2
  38. package/scripts/run-mcp-clean-tests.mjs +30 -0
  39. package/src/adapters/agent-browser/provider.js +14 -11
  40. package/src/adapters/emulated-services/provider.js +11 -4
  41. package/src/adapters/temporary-paths.js +18 -0
  42. package/src/cli.js +146 -210
  43. package/src/commands/action-show.js +5 -2
  44. package/src/commands/advance.js +5 -1
  45. package/src/commands/baseline.js +13 -11
  46. package/src/commands/checkpoint-revalidate.js +24 -8
  47. package/src/commands/complete.js +19 -3
  48. package/src/commands/contract-create.js +3 -1
  49. package/src/commands/doctor.js +36 -13
  50. package/src/commands/gate-record.js +20 -31
  51. package/src/commands/gate-revalidate.js +11 -13
  52. package/src/commands/handoff-list.js +6 -1
  53. package/src/commands/handoff-show.js +6 -1
  54. package/src/commands/migrate-protocol.js +2 -2
  55. package/src/commands/model-route.js +9 -1
  56. package/src/commands/next.js +7 -1
  57. package/src/commands/policy-diff.js +25 -4
  58. package/src/commands/progress.js +17 -11
  59. package/src/commands/record-diagnosis.js +3 -0
  60. package/src/commands/semantic-plan.js +26 -2
  61. package/src/commands/status.js +6 -1
  62. package/src/commands/storage-backup.js +15 -0
  63. package/src/commands/storage-migrate.js +5 -0
  64. package/src/commands/storage-migration-resume.js +5 -0
  65. package/src/commands/storage-migration-status.js +44 -0
  66. package/src/commands/storage-restore-resume.js +7 -0
  67. package/src/commands/storage-restore.js +8 -0
  68. package/src/commands/task-abandon.js +1 -1
  69. package/src/commands/task-list.js +8 -43
  70. package/src/commands/task-migrate.js +3 -3
  71. package/src/commands/task-recover.js +10 -2
  72. package/src/commands/task-scope.js +18 -2
  73. package/src/commands/task-show.js +16 -2
  74. package/src/commands/validate-protocol.js +29 -14
  75. package/src/commands/validate-receipt.js +8 -1
  76. package/src/config/guides.json +4 -0
  77. package/src/core/action-ledger-projection.js +10 -3
  78. package/src/core/actions.js +38 -75
  79. package/src/core/activation.js +11 -3
  80. package/src/core/approvals.js +17 -51
  81. package/src/core/artifact-registry.js +59 -2
  82. package/src/core/artifacts.js +81 -27
  83. package/src/core/attachment-paths.js +45 -0
  84. package/src/core/attestation-coverage.js +6 -1
  85. package/src/core/attestation-verifier.js +21 -2
  86. package/src/core/attestation.js +17 -6
  87. package/src/core/audit.js +6 -1
  88. package/src/core/bundles.js +160 -66
  89. package/src/core/checkpoint-revalidation.js +12 -10
  90. package/src/core/cli-command-definitions.js +109 -7
  91. package/src/core/code-manifest.js +9 -3
  92. package/src/core/command-executors.js +182 -126
  93. package/src/core/command-input.js +21 -1
  94. package/src/core/completion-artifacts.js +85 -36
  95. package/src/core/completion-ownership.js +19 -8
  96. package/src/core/completion-recovery-rebind.js +11 -1
  97. package/src/core/completion.js +60 -9
  98. package/src/core/conformance.js +2 -1
  99. package/src/core/continuity-lint.js +13 -2
  100. package/src/core/continuity-reconciliation.js +16 -3
  101. package/src/core/continuity.js +16 -8
  102. package/src/core/contract-bootstrap-recovery.js +42 -41
  103. package/src/core/contract-revision.js +8 -9
  104. package/src/core/decision/artifact.js +2 -1
  105. package/src/core/decision/events.js +9 -6
  106. package/src/core/decision/resolver.js +19 -11
  107. package/src/core/decision/service.js +63 -38
  108. package/src/core/decision/task-bindings.js +6 -1
  109. package/src/core/diagnosis-model.js +11 -0
  110. package/src/core/diagnosis.js +10 -1
  111. package/src/core/diagnostic-projection.js +29 -12
  112. package/src/core/diagnostic-record.js +28 -3
  113. package/src/core/efficiency.js +6 -1
  114. package/src/core/events.js +316 -97
  115. package/src/core/execution-prerequisites.js +22 -13
  116. package/src/core/execution.js +26 -40
  117. package/src/core/filesystem.js +11 -0
  118. package/src/core/gate-artifact.js +105 -19
  119. package/src/core/gate-provenance.js +6 -5
  120. package/src/core/handoff.js +26 -14
  121. package/src/core/history.js +39 -35
  122. package/src/core/hypothesis-projection.js +32 -0
  123. package/src/core/information-gain-projection.js +104 -100
  124. package/src/core/inspect.js +12 -3
  125. package/src/core/integration-invocation-policy.js +10 -0
  126. package/src/core/integration-resources.js +41 -34
  127. package/src/core/ledger-event-collection.js +189 -0
  128. package/src/core/ledger-relations.js +23 -0
  129. package/src/core/native-storage.js +12 -0
  130. package/src/core/next-action-continuity.js +31 -9
  131. package/src/core/next-action-phases.js +1 -1
  132. package/src/core/next-action-review-phase.js +2 -2
  133. package/src/core/next-action-verification-phase.js +2 -1
  134. package/src/core/next-action.js +21 -2
  135. package/src/core/phase.js +138 -93
  136. package/src/core/policy-engine.js +12 -13
  137. package/src/core/preflight-consistency.js +72 -59
  138. package/src/core/preflight-loaders.js +19 -12
  139. package/src/core/preflight-model.js +6 -1
  140. package/src/core/preflight.js +29 -12
  141. package/src/core/progress.js +21 -10
  142. package/src/core/protocol-info.js +3 -0
  143. package/src/core/protocol-migration.js +7 -6
  144. package/src/core/reconcile-closure.js +162 -24
  145. package/src/core/recovery-history.js +66 -47
  146. package/src/core/reflection.js +72 -77
  147. package/src/core/responsibility.js +25 -13
  148. package/src/core/route-artifact.js +13 -3
  149. package/src/core/runtime-context.js +5 -0
  150. package/src/core/schema-validation.js +8 -3
  151. package/src/core/settlement.js +12 -1
  152. package/src/core/signing/bundle-file.js +53 -0
  153. package/src/core/signing/bundle-reference.js +15 -0
  154. package/src/core/signing/sigstore.js +60 -33
  155. package/src/core/signing/statement-file.js +27 -0
  156. package/src/core/storage-runtime-registry.js +41 -0
  157. package/src/core/structural-quality/artifacts.js +20 -14
  158. package/src/core/structural-quality/service.js +81 -56
  159. package/src/core/target-layout.js +1 -1
  160. package/src/core/task-claim-state.js +52 -18
  161. package/src/core/task-command.js +21 -1
  162. package/src/core/task-conflict-inspection.js +112 -107
  163. package/src/core/task-descriptor.js +29 -22
  164. package/src/core/task-discovery.js +288 -90
  165. package/src/core/task-lock.js +66 -306
  166. package/src/core/task-migration-validation.js +5 -5
  167. package/src/core/task-migration.js +17 -287
  168. package/src/core/task-snapshot.js +51 -8
  169. package/src/core/task-storage-migration.js +18 -0
  170. package/src/core/templates.js +20 -8
  171. package/src/core/test-intelligence/prune.js +5 -1
  172. package/src/core/trace.js +154 -99
  173. package/src/core/trajectory-evaluation.js +15 -3
  174. package/src/core/trajectory-metrics.js +47 -32
  175. package/src/core/transaction.js +16 -288
  176. package/src/core/verification-scope.js +20 -12
  177. package/src/core/work-state.js +100 -59
  178. package/src/repository-index/binary-manager.js +15 -7
  179. package/src/storage/artifact-bytes.js +22 -0
  180. package/src/storage/attachment-binding.js +11 -0
  181. package/src/storage/attachment-files.js +82 -0
  182. package/src/storage/attachment-references.js +36 -0
  183. package/src/storage/backup.js +53 -0
  184. package/src/storage/capabilities.js +31 -0
  185. package/src/storage/connection-owner.js +119 -0
  186. package/src/storage/connection.js +376 -0
  187. package/src/storage/existing-project-scope.js +45 -0
  188. package/src/storage/export-destination.js +44 -0
  189. package/src/storage/exporter.js +284 -0
  190. package/src/storage/file-durability.js +46 -0
  191. package/src/storage/importer.js +461 -0
  192. package/src/storage/index.js +74 -0
  193. package/src/storage/leases.js +63 -0
  194. package/src/storage/ledger-event-snapshot.js +150 -0
  195. package/src/storage/ledger-relations.js +220 -0
  196. package/src/storage/legacy-maintenance-boundary.js +18 -0
  197. package/src/storage/legacy-signature.js +115 -0
  198. package/src/storage/maintenance-handoff.js +129 -0
  199. package/src/storage/maintenance-owner.js +42 -0
  200. package/src/storage/maintenance.js +310 -0
  201. package/src/storage/metadata-json.js +35 -0
  202. package/src/storage/migration-activate.js +59 -0
  203. package/src/storage/migration-archive.js +57 -0
  204. package/src/storage/migration-attachment-root.js +61 -0
  205. package/src/storage/migration-candidate.js +398 -0
  206. package/src/storage/migration-publication.js +143 -0
  207. package/src/storage/migration-rollback-admission.js +54 -0
  208. package/src/storage/migration-rollback-preparation.js +132 -0
  209. package/src/storage/migration-rollback-publication.js +183 -0
  210. package/src/storage/migration-rollback-release.js +66 -0
  211. package/src/storage/migration-rollback-source-stage.js +103 -0
  212. package/src/storage/migration-rollback.js +43 -0
  213. package/src/storage/migration-source-partition.js +56 -0
  214. package/src/storage/migration-source.js +255 -0
  215. package/src/storage/migration-terminal.js +39 -0
  216. package/src/storage/migration-validation.js +75 -0
  217. package/src/storage/migration.js +43 -0
  218. package/src/storage/operational-attachments.js +49 -0
  219. package/src/storage/operational-context.js +50 -0
  220. package/src/storage/project-backup.js +98 -0
  221. package/src/storage/project-bootstrap.js +24 -0
  222. package/src/storage/project-boundary.js +79 -0
  223. package/src/storage/project-read-snapshot.js +18 -0
  224. package/src/storage/project-replacement.js +137 -0
  225. package/src/storage/project-restore.js +203 -0
  226. package/src/storage/record-definitions.js +45 -0
  227. package/src/storage/repository.js +507 -0
  228. package/src/storage/restore-execution-provenance.js +26 -0
  229. package/src/storage/restore-journal.js +62 -0
  230. package/src/storage/restore-layout.js +30 -0
  231. package/src/storage/restore-preparation.js +84 -0
  232. package/src/storage/restore-replacement-archive.js +162 -0
  233. package/src/storage/restore-replacement-owner.js +31 -0
  234. package/src/storage/restore-replacement-preparation.js +128 -0
  235. package/src/storage/restore-replacement.js +142 -0
  236. package/src/storage/restore-terminal.js +32 -0
  237. package/src/storage/runtime.js +31 -0
  238. package/src/storage/schema.js +214 -0
  239. package/src/storage/singleton-import.js +84 -0
  240. package/src/storage/snapshot-continuity.js +16 -0
  241. package/src/storage/snapshot.js +36 -0
  242. package/src/storage/storage-marker.js +79 -0
  243. package/src/storage/task-guards.js +107 -0
  244. package/src/storage/topology.js +42 -0
  245. package/src/storage/transaction.js +179 -0
  246. package/src/storage/unit-of-work.js +667 -0
  247. package/src/storage/windows-process-incarnation.js +206 -0
  248. package/.forgeloop/forgeloop.gitignore +0 -4
  249. package/LICENSE-DOCS.md +0 -13
  250. package/src/core/transaction-maintenance.js +0 -70
@@ -2,8 +2,9 @@ import { classifyLoadedWorkState, readWorkState } from "./work-state.js";
2
2
  import { taskArtifactPath } from "./task-paths.js";
3
3
  import { readLockInfo, classifyLockStaleness } from "./task-lock.js";
4
4
  import { validateStateLedgerCoherence } from "./events.js";
5
+ import { ledgerEventAt, ledgerEventsOfTypes } from "./ledger-event-collection.js";
5
6
  import { currentRepositoryFingerprint } from "./repository.js";
6
- import { collectTaskClaimEvidence, classifyTaskClaimState } from "./task-claim-state.js";
7
+ import { withTaskClaimEvidence, classifyTaskClaimState } from "./task-claim-state.js";
7
8
 
8
9
  export const TASK_CONFLICT_CLASSIFICATIONS = Object.freeze([
9
10
  "ACTIVE",
@@ -55,22 +56,25 @@ function checkCount(state, status) {
55
56
  }
56
57
 
57
58
  function lastMeaningfulActivity(state, events) {
58
- const candidates = [
59
- ...(state?.lastUpdated ? [{ at: state.lastUpdated, type: "WORK_STATE_UPDATED" }] : []),
60
- ...events
61
- .filter((event) => MEANINGFUL_ACTIVITY_EVENTS.has(event.event))
62
- .map((event) => ({ at: event.at, type: event.event })),
63
- ].filter((candidate) => Number.isFinite(Date.parse(candidate.at)));
64
- return candidates.sort((left, right) => Date.parse(right.at) - Date.parse(left.at))[0] ?? null;
59
+ let latest = state?.lastUpdated && Number.isFinite(Date.parse(state.lastUpdated))
60
+ ? { at: state.lastUpdated, type: "WORK_STATE_UPDATED" }
61
+ : null;
62
+ for (const event of ledgerEventsOfTypes(events, [...MEANINGFUL_ACTIVITY_EVENTS])) {
63
+ const timestamp = Date.parse(event.at);
64
+ if (Number.isFinite(timestamp) && (!latest || timestamp > Date.parse(latest.at))) {
65
+ latest = { at: event.at, type: event.event };
66
+ }
67
+ }
68
+ return latest;
65
69
  }
66
70
 
67
71
  function isInitialTaskLedger(events, taskId) {
68
72
  return events.length >= 2
69
- && events[0]?.taskId === taskId
70
- && events[0]?.event === "TASK_RECEIVED"
71
- && events[1]?.taskId === taskId
72
- && events[1]?.event === "TRANSACTION_COMMITTED"
73
- && events[1]?.details?.operation === "task-create"
73
+ && ledgerEventAt(events, 0)?.taskId === taskId
74
+ && ledgerEventAt(events, 0)?.event === "TASK_RECEIVED"
75
+ && ledgerEventAt(events, 1)?.taskId === taskId
76
+ && ledgerEventAt(events, 1)?.event === "TRANSACTION_COMMITTED"
77
+ && ledgerEventAt(events, 1)?.details?.operation === "task-create"
74
78
  && !events.slice(2).some((event) => [
75
79
  "CONTRACT_VALIDATED", "ROUTE_VALIDATED", "PREFLIGHT_READY", "EXECUTION_STARTED",
76
80
  "VERIFICATION_STARTED", "VERIFICATION_RECORDED", "REVIEW_STARTED", "COMPLETION_VALIDATED",
@@ -129,7 +133,7 @@ export function classifyConflictEvidence(evidence, {
129
133
  };
130
134
  }
131
135
  const KNOWN_PHASES = new Set([
132
- "RECEIVED", "DISCOVERING", "CONTRACT_READY", "ROUTED", "DESIGNING", "PLANNED",
136
+ "RECEIVED", "DISCOVERING", "CONTRACT_READY", "ROUTED", "DESIGNING", "PLANNED", "BLOCKED",
133
137
  ...POST_EXECUTION_PHASES,
134
138
  ]);
135
139
  const effectivePhase = phase ?? earlyPhase;
@@ -247,101 +251,102 @@ export async function inspectTaskConflictState(target, {
247
251
  now = Date.now(),
248
252
  ignoredLockId = null,
249
253
  } = {}) {
250
- const [lockInfo, stateResult, ownershipEvidence] = await Promise.all([
251
- readLockInfo(target, taskId),
252
- readWorkState(target, { packageRoot, taskId })
253
- .then((value) => ({ value, error: null }))
254
- .catch((error) => ({ value: null, error })),
255
- collectTaskClaimEvidence(target, { packageRoot, taskId }),
256
- ]);
257
- const claimProjection = classifyTaskClaimState(ownershipEvidence);
258
- const state = stateResult.value;
259
- const recovery = claimProjection.recovery;
260
-
261
- const lockClassification = ignoredLockId && lockInfo?.lockId === ignoredLockId
262
- ? { status: "NONE", stale: false }
263
- : classifyLockStaleness(lockInfo, now);
264
-
265
- let freshness = null;
266
- if (state) {
267
- try {
268
- freshness = await classifyLoadedWorkState({
269
- target,
270
- state,
271
- contractFile: taskArtifactPath(taskId, "contract"),
272
- });
273
- } catch {
274
- freshness = { status: "UNKNOWN", reasons: ["E_STATE_UNREADABLE"] };
254
+ return withTaskClaimEvidence(target, { packageRoot, taskId }, async ownershipEvidence => {
255
+ const [lockInfo, stateResult] = await Promise.all([
256
+ readLockInfo(target, taskId),
257
+ readWorkState(target, { packageRoot, taskId })
258
+ .then((value) => ({ value, error: null }))
259
+ .catch((error) => ({ value: null, error })),
260
+ ]);
261
+ const claimProjection = classifyTaskClaimState(ownershipEvidence);
262
+ const state = stateResult.value;
263
+ const recovery = claimProjection.recovery;
264
+
265
+ const lockClassification = ignoredLockId && lockInfo?.lockId === ignoredLockId
266
+ ? { status: "NONE", stale: false }
267
+ : classifyLockStaleness(lockInfo, now);
268
+
269
+ let freshness = null;
270
+ if (state) {
271
+ try {
272
+ freshness = await classifyLoadedWorkState({
273
+ target,
274
+ state,
275
+ contractFile: taskArtifactPath(taskId, "contract"),
276
+ });
277
+ } catch {
278
+ freshness = { status: "UNKNOWN", reasons: ["E_STATE_UNREADABLE"] };
279
+ }
275
280
  }
276
- }
277
281
 
278
- // Reuse the ownership snapshot's already-validated ledger; no second full
279
- // ledger parse inside this immutable inspection.
280
- const coherenceErrors = ownershipEvidence.state && ownershipEvidence.ledger
281
- ? validateStateLedgerCoherence(ownershipEvidence.state, ownershipEvidence.ledger.events)
282
- : [];
283
- const ledgerValid = ownershipEvidence.ledger.valid && coherenceErrors.length === 0;
284
- const ledgerEvents = ownershipEvidence.ledger.events;
285
- const ledgerErrors = [...ownershipEvidence.ledger.errors, ...coherenceErrors];
286
-
287
- const repository = await currentRepositoryFingerprint(target);
288
- const initialTask = !state && ledgerValid && ownershipEvidence.descriptor
289
- && isInitialTaskLedger(ledgerEvents, taskId)
290
- && !ownershipEvidence.recovery;
291
- const meaningfulActivity = lastMeaningfulActivity(state, ledgerEvents);
292
- const recoveryConsistencyErrors = claimProjection.ownershipErrors ?? claimProjection.errors ?? [];
293
- const healthReasonCodes = [
294
- ...(stateResult.error ? ["E_STATE_UNREADABLE"] : []),
295
- ...(claimProjection.ownershipValid === false
296
- ? claimProjection.reasonCodes
297
- : []),
298
- ];
299
-
300
- const evidence = {
301
- healthy: healthReasonCodes.length === 0,
302
- healthReasonCodes,
303
- descriptorHealthy: true,
304
- phase: state?.phase ?? (initialTask ? deriveEarlyPhase(ledgerEvents, taskId) : null),
305
- lastUpdated: state?.lastUpdated ?? null,
306
- lastMeaningfulActivityAt: meaningfulActivity?.at ?? null,
307
- lastMeaningfulEventType: meaningfulActivity?.type ?? null,
308
- workStateRevision: state?.revision ?? 0,
309
- lockStatus: lockClassification.status,
310
- lockId: lockInfo?.lockId ?? null,
311
- lockExpiresAt: lockClassification.expiresAt ?? null,
312
- freshnessStatus: freshness?.status ?? (initialTask ? "FRESH" : "UNKNOWN"),
313
- freshnessReasons: freshness?.reasons ?? [],
314
- ledgerValid,
315
- ledgerLastSeq: ledgerEvents.at(-1)?.seq ?? 0,
316
- ledgerErrors,
317
- recordedChecks: passedCheckCount(state),
318
- passedCheckCount: passedCheckCount(state),
319
- failedCheckCount: checkCount(state, "failed"),
320
- blockedCheckCount: checkCount(state, "blocked"),
321
- totalChecks: state?.checks?.length ?? 0,
322
- verificationEvidenceCount: state?.verificationEvidence?.length ?? 0,
323
- recoveryStatus: claimProjection.claimState === "RELEASED_BY_RECOVERY"
324
- ? "RECOVERED"
325
- : recovery?.status ?? null,
326
- recoveryConsistencyErrors,
327
- claimState: claimProjection.claimState,
328
- historicalWriteClaims: claimProjection.historicalWriteClaims,
329
- effectiveWriteClaims: claimProjection.effectiveWriteClaims,
330
- mutationAllowed: claimProjection.mutationAllowed,
331
- ownershipValid: claimProjection.ownershipValid,
332
- ownershipErrors: recoveryConsistencyErrors,
333
- repositoryBranch: repository.branch,
334
- repositoryHead: repository.head,
335
- };
282
+ // Reuse the ownership snapshot's already-validated ledger; no second full
283
+ // ledger parse inside this immutable inspection.
284
+ const coherenceErrors = ownershipEvidence.state && ownershipEvidence.ledger
285
+ ? validateStateLedgerCoherence(ownershipEvidence.state, ownershipEvidence.ledger.events)
286
+ : [];
287
+ const ledgerValid = ownershipEvidence.ledger.valid && coherenceErrors.length === 0;
288
+ const ledgerEvents = ownershipEvidence.ledger.events;
289
+ const ledgerErrors = [...ownershipEvidence.ledger.errors, ...coherenceErrors];
290
+
291
+ const repository = await currentRepositoryFingerprint(target);
292
+ const initialTask = !state && ledgerValid && ownershipEvidence.descriptor
293
+ && isInitialTaskLedger(ledgerEvents, taskId)
294
+ && !ownershipEvidence.recovery;
295
+ const meaningfulActivity = lastMeaningfulActivity(state, ledgerEvents);
296
+ const recoveryConsistencyErrors = claimProjection.ownershipErrors ?? claimProjection.errors ?? [];
297
+ const healthReasonCodes = [
298
+ ...(stateResult.error ? ["E_STATE_UNREADABLE"] : []),
299
+ ...(claimProjection.ownershipValid === false
300
+ ? claimProjection.reasonCodes
301
+ : []),
302
+ ];
303
+
304
+ const evidence = {
305
+ healthy: healthReasonCodes.length === 0,
306
+ healthReasonCodes,
307
+ descriptorHealthy: true,
308
+ phase: state?.phase ?? (initialTask ? deriveEarlyPhase(ledgerEvents, taskId) : null),
309
+ lastUpdated: state?.lastUpdated ?? null,
310
+ lastMeaningfulActivityAt: meaningfulActivity?.at ?? null,
311
+ lastMeaningfulEventType: meaningfulActivity?.type ?? null,
312
+ workStateRevision: state?.revision ?? 0,
313
+ lockStatus: lockClassification.status,
314
+ lockId: lockInfo?.lockId ?? null,
315
+ lockExpiresAt: lockClassification.expiresAt ?? null,
316
+ freshnessStatus: freshness?.status ?? (initialTask ? "FRESH" : "UNKNOWN"),
317
+ freshnessReasons: freshness?.reasons ?? [],
318
+ ledgerValid,
319
+ ledgerLastSeq: ledgerEvents.at(-1)?.seq ?? 0,
320
+ ledgerErrors,
321
+ recordedChecks: passedCheckCount(state),
322
+ passedCheckCount: passedCheckCount(state),
323
+ failedCheckCount: checkCount(state, "failed"),
324
+ blockedCheckCount: checkCount(state, "blocked"),
325
+ totalChecks: state?.checks?.length ?? 0,
326
+ verificationEvidenceCount: state?.verificationEvidence?.length ?? 0,
327
+ recoveryStatus: claimProjection.claimState === "RELEASED_BY_RECOVERY"
328
+ ? "RECOVERED"
329
+ : recovery?.status ?? null,
330
+ recoveryConsistencyErrors,
331
+ claimState: claimProjection.claimState,
332
+ historicalWriteClaims: claimProjection.historicalWriteClaims,
333
+ effectiveWriteClaims: claimProjection.effectiveWriteClaims,
334
+ mutationAllowed: claimProjection.mutationAllowed,
335
+ ownershipValid: claimProjection.ownershipValid,
336
+ ownershipErrors: recoveryConsistencyErrors,
337
+ repositoryBranch: repository.branch,
338
+ repositoryHead: repository.head,
339
+ };
336
340
 
337
- const verdict = classifyConflictEvidence(evidence, { now });
341
+ const verdict = classifyConflictEvidence(evidence, { now });
338
342
 
339
- return {
340
- taskId,
341
- classification: verdict.classification,
342
- reasonCodes: verdict.reasonCodes,
343
- recoverable: verdict.recoverable ?? false,
344
- ...(verdict.recoveredBy ? { recoveredBy: verdict.recoveredBy } : {}),
345
- evidence,
346
- };
343
+ return {
344
+ taskId,
345
+ classification: verdict.classification,
346
+ reasonCodes: verdict.reasonCodes,
347
+ recoverable: verdict.recoverable ?? false,
348
+ ...(verdict.recoveredBy ? { recoveredBy: verdict.recoveredBy } : {}),
349
+ evidence,
350
+ };
351
+ });
347
352
  }
@@ -1,5 +1,5 @@
1
1
  import { PROTOCOL_VERSION } from "./protocol.js";
2
- import { readJsonArtifact, writeJsonArtifact } from "./artifacts.js";
2
+ import { readJsonArtifact, readPortableJsonArtifact, writeJsonArtifact } from "./artifacts.js";
3
3
  import { assertSchema, readSchema } from "./schema-validation.js";
4
4
  import { getPackageRoot } from "./templates.js";
5
5
  import { assertTaskDescriptorIdentity, assertTaskId, taskStorageKey } from "./task-identity.js";
@@ -7,6 +7,8 @@ import { normalizeWriteClaims } from "./task-scope.js";
7
7
  import { taskArtifactPath, TASK_STATE_ROOT } from "./task-paths.js";
8
8
  import { assertSafePath, ensureWithin, fileExists } from "./filesystem.js";
9
9
  import { E_TASK_DESCRIPTOR_INVALID, E_TASK_NOT_FOUND } from "./error-codes.js";
10
+ import { operationalArtifactExists } from "../storage/operational-context.js";
11
+ import { withNativeReadScope } from "./native-storage.js";
10
12
 
11
13
  export const TASK_DESCRIPTOR_SCHEMA_VERSION = 1;
12
14
 
@@ -42,29 +44,34 @@ export async function validateTaskDescriptor(descriptor, packageRoot = getPackag
42
44
  }
43
45
 
44
46
  export async function readTaskDescriptor(target, taskIdOrKey, packageRoot = getPackageRoot()) {
45
- // If it's a 64-char key, use it directly; otherwise derive storage key
46
- const isKey = typeof taskIdOrKey === "string" && /^[a-f0-9]{64}$/.test(taskIdOrKey);
47
- const relativePath = isKey
48
- ? `${TASK_STATE_ROOT}/${taskIdOrKey}/task.json`
49
- : taskArtifactPath(taskIdOrKey, "descriptor");
47
+ return withNativeReadScope(target, async () => {
48
+ // If it's a 64-char key, use it directly; otherwise derive storage key
49
+ const isKey = typeof taskIdOrKey === "string" && /^[a-f0-9]{64}$/.test(taskIdOrKey);
50
+ const relativePath = isKey
51
+ ? `${TASK_STATE_ROOT}/${taskIdOrKey}/task.json`
52
+ : taskArtifactPath(taskIdOrKey, "descriptor");
50
53
 
51
- await assertSafePath(target, relativePath);
52
- const fullPath = ensureWithin(target, relativePath);
53
- if (!(await fileExists(fullPath))) {
54
- const error = new Error(`Task descriptor not found: ${relativePath}`);
55
- error.code = E_TASK_NOT_FOUND;
56
- error.artifacts = [relativePath];
57
- throw error;
58
- }
54
+ const operationalExists = operationalArtifactExists(target, relativePath);
55
+ if (operationalExists === null) await assertSafePath(target, relativePath);
56
+ const fullPath = ensureWithin(target, relativePath);
57
+ if (operationalExists === false || (operationalExists === null && !(await fileExists(fullPath)))) {
58
+ const error = new Error(`Task descriptor not found: ${relativePath}`);
59
+ error.code = E_TASK_NOT_FOUND;
60
+ error.artifacts = [relativePath];
61
+ throw error;
62
+ }
59
63
 
60
- const artifact = await readJsonArtifact(target, relativePath, "task-descriptor", packageRoot);
61
- try {
62
- await validateTaskDescriptor(artifact.value, packageRoot);
63
- } catch (error) {
64
- error.code = E_TASK_DESCRIPTOR_INVALID;
65
- throw error;
66
- }
67
- return { ...artifact, ...artifact.value, value: artifact.value };
64
+ // An unselected legacy descriptor is inspection input, never writable authority.
65
+ const reader = operationalExists === null ? readPortableJsonArtifact : readJsonArtifact;
66
+ const artifact = await reader(target, relativePath, "task-descriptor", packageRoot);
67
+ try {
68
+ await validateTaskDescriptor(artifact.value, packageRoot);
69
+ } catch (error) {
70
+ error.code = E_TASK_DESCRIPTOR_INVALID;
71
+ throw error;
72
+ }
73
+ return { ...artifact, ...artifact.value, value: artifact.value };
74
+ });
68
75
  }
69
76
 
70
77
  export async function writeTaskDescriptor(target, descriptor, packageRoot = getPackageRoot(), options = {}) {