@cassiomc1/forgeloop 1.3.0 → 1.6.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (163) hide show
  1. package/.github/copilot-instructions.md +1 -0
  2. package/AGENTS.md +1 -0
  3. package/CLAUDE.md +1 -0
  4. package/DOCS_INDEX.md +20 -8
  5. package/EXECUTION_STATE.md +60 -0
  6. package/LOOP_ENGINEERING.md +135 -5
  7. package/LOOP_SYSTEM_DESIGN.md +54 -1
  8. package/PROTOCOL_INTEGRATION.md +87 -0
  9. package/QUALITY_SCORECARD.md +2 -0
  10. package/README.md +69 -9
  11. package/TERMINOLOGY.md +15 -0
  12. package/THIRD_PARTY_NOTICES.md +30 -0
  13. package/THREAT_MODEL.md +59 -1
  14. package/docs/ARTIFACT_REFERENCE.md +183 -0
  15. package/docs/CLI_REFERENCE.md +391 -6
  16. package/docs/CROSS_HARNESS_CONTINUITY.md +23 -0
  17. package/docs/DIAGNOSTIC_MODEL.md +181 -0
  18. package/docs/DOCUMENTATION_GUIDE.md +36 -13
  19. package/docs/EXECUTION_TRACE.md +76 -0
  20. package/docs/GETTING_STARTED.md +1 -0
  21. package/docs/MCP.md +159 -0
  22. package/docs/RECIPES.md +149 -0
  23. package/docs/RELEASE_CHECKLIST_1_4.md +38 -0
  24. package/docs/RELEASE_CHECKLIST_1_5_MCP.md +78 -0
  25. package/docs/TROUBLESHOOTING.md +217 -3
  26. package/docs/UNIVERSAL_INTEGRATION.md +48 -0
  27. package/docs/assets/diagrams/forgeloop-engineering-flow.html +13797 -0
  28. package/docs/assets/diagrams/forgeloop-engineering-flow.receipt.json +37 -0
  29. package/docs/assets/diagrams/forgeloop-engineering-flow.svg +5002 -0
  30. package/docs/diagrams/README.md +55 -0
  31. package/docs/diagrams/forgeloop-engineering-flow.workflow.json +122 -0
  32. package/docs/diagrams/manifest.json +42 -0
  33. package/docs/diagrams/reviews/forgeloop-engineering-flow.review.json +20 -0
  34. package/package.json +21 -8
  35. package/schemas/action.schema.json +100 -0
  36. package/schemas/approval.schema.json +51 -0
  37. package/schemas/capability-policy.schema.json +41 -0
  38. package/schemas/diagnostic-case.schema.json +85 -0
  39. package/schemas/execution-receipt.schema.json +16 -0
  40. package/schemas/hypothesis-disposition.schema.json +16 -0
  41. package/schemas/intervention.schema.json +27 -0
  42. package/schemas/policy-lock.schema.json +1 -0
  43. package/schemas/policy-snapshot.schema.json +2 -0
  44. package/schemas/task-recovery.schema.json +61 -0
  45. package/schemas/trajectory-evaluation.schema.json +64 -0
  46. package/schemas/trajectory-scenario.schema.json +42 -0
  47. package/src/cli.js +267 -347
  48. package/src/commands/action-authorize.js +41 -0
  49. package/src/commands/action-propose.js +10 -0
  50. package/src/commands/action-reconcile.js +10 -0
  51. package/src/commands/action-record.js +47 -0
  52. package/src/commands/action-show.js +10 -0
  53. package/src/commands/action-verify.js +10 -0
  54. package/src/commands/advance.js +7 -2
  55. package/src/commands/approval-request.js +64 -0
  56. package/src/commands/approval-resolve.js +10 -0
  57. package/src/commands/audit.js +5 -0
  58. package/src/commands/baseline.js +3 -3
  59. package/src/commands/eval.js +6 -0
  60. package/src/commands/history.js +18 -0
  61. package/src/commands/init.js +2 -2
  62. package/src/commands/inspect.js +55 -0
  63. package/src/commands/metrics.js +7 -0
  64. package/src/commands/next.js +8 -2
  65. package/src/commands/policy-discover.js +2 -2
  66. package/src/commands/progress.js +6 -2
  67. package/src/commands/record-diagnosis.js +37 -1
  68. package/src/commands/record-hypothesis-disposition.js +45 -0
  69. package/src/commands/record-intervention.js +35 -0
  70. package/src/commands/reflect.js +38 -0
  71. package/src/commands/report.js +9 -1
  72. package/src/commands/run-action.js +18 -0
  73. package/src/commands/status.js +17 -0
  74. package/src/commands/task-create.js +39 -1
  75. package/src/commands/task-list.js +14 -1
  76. package/src/commands/task-lock-status.js +2 -2
  77. package/src/commands/task-recover.js +202 -0
  78. package/src/commands/task-repair-legacy-recovery.js +417 -0
  79. package/src/commands/task-resume.js +172 -0
  80. package/src/commands/task-scope.js +23 -4
  81. package/src/commands/task-show.js +18 -4
  82. package/src/commands/trace.js +34 -0
  83. package/src/commands/validate-protocol.js +40 -15
  84. package/src/core/action-authorization.js +106 -0
  85. package/src/core/action-constants.js +86 -0
  86. package/src/core/action-execution.js +105 -0
  87. package/src/core/action-ledger-projection.js +302 -0
  88. package/src/core/action-model.js +581 -0
  89. package/src/core/action-readiness.js +141 -0
  90. package/src/core/action-reconciliation-policy.js +49 -0
  91. package/src/core/action-reconciliation.js +66 -0
  92. package/src/core/action-verification.js +111 -0
  93. package/src/core/actions.js +462 -0
  94. package/src/core/approvals.js +405 -0
  95. package/src/core/artifact-registry.js +60 -0
  96. package/src/core/audit.js +45 -4
  97. package/src/core/bundles.js +30 -0
  98. package/src/core/capability-policy.js +226 -0
  99. package/src/core/cli-command-definitions.js +260 -5
  100. package/src/core/command-executors.js +543 -0
  101. package/src/core/command-input.js +107 -0
  102. package/src/core/command-runtime.js +117 -0
  103. package/src/core/completion-artifacts.js +39 -15
  104. package/src/core/completion-ownership.js +88 -0
  105. package/src/core/completion-recovery-rebind.js +194 -0
  106. package/src/core/completion.js +70 -0
  107. package/src/core/continuity-reconciliation.js +24 -5
  108. package/src/core/diagnostic-model.js +396 -0
  109. package/src/core/diagnostic-projection.js +51 -0
  110. package/src/core/diagnostic-record.js +360 -0
  111. package/src/core/error-codes.js +461 -1
  112. package/src/core/events.js +171 -2
  113. package/src/core/execution-prerequisites.js +4 -1
  114. package/src/core/execution.js +26 -188
  115. package/src/core/failure-signature.js +70 -0
  116. package/src/core/failure-surface.js +57 -0
  117. package/src/core/filesystem.js +55 -6
  118. package/src/core/history.js +110 -0
  119. package/src/core/hypothesis-projection.js +85 -0
  120. package/src/core/information-gain-projection.js +283 -0
  121. package/src/core/information-gain.js +138 -0
  122. package/src/core/inspect.js +132 -7
  123. package/src/core/integration-invocation-policy.js +217 -0
  124. package/src/core/integration-limits.js +20 -0
  125. package/src/core/integration-resources.js +178 -0
  126. package/src/core/next-action-model.js +94 -0
  127. package/src/core/next-action.js +490 -3
  128. package/src/core/phase.js +42 -22
  129. package/src/core/policy-engine.js +113 -6
  130. package/src/core/preflight-consistency.js +31 -5
  131. package/src/core/preflight.js +19 -2
  132. package/src/core/prepared-execution.js +227 -0
  133. package/src/core/progress.js +41 -4
  134. package/src/core/project-root.js +21 -0
  135. package/src/core/protocol-info.js +61 -0
  136. package/src/core/protocol.js +14 -0
  137. package/src/core/receipt.js +1 -0
  138. package/src/core/reconcile-closure.js +35 -10
  139. package/src/core/recovery-history.js +116 -0
  140. package/src/core/reflection.js +305 -0
  141. package/src/core/resumability.js +57 -3
  142. package/src/core/schema-validation.js +9 -0
  143. package/src/core/strategy-analysis.js +97 -0
  144. package/src/core/task-claim-state.js +272 -0
  145. package/src/core/task-command.js +5 -1
  146. package/src/core/task-conflict-inspection.js +321 -0
  147. package/src/core/task-context.js +32 -29
  148. package/src/core/task-discovery.js +14 -1
  149. package/src/core/task-lock.js +216 -22
  150. package/src/core/task-paths.js +31 -2
  151. package/src/core/task-recovery-migration.js +192 -0
  152. package/src/core/task-recovery.js +205 -0
  153. package/src/core/task-scope.js +33 -1
  154. package/src/core/task-snapshot.js +53 -0
  155. package/src/core/templates.js +9 -0
  156. package/src/core/trace.js +548 -0
  157. package/src/core/trajectory-evaluation.js +71 -0
  158. package/src/core/trajectory-metrics.js +80 -0
  159. package/src/core/transaction.js +36 -2
  160. package/src/core/work-state.js +10 -5
  161. package/src/integration.js +47 -0
  162. package/docs/assets/forgeloop-flow.svg +0 -1
  163. package/docs/forgeloop-flow.mmd +0 -51
package/README.md CHANGED
@@ -75,6 +75,15 @@ forgeloop next --task demo --json
75
75
  O último comando informa a ação segura seguinte; ele não executa código nem
76
76
  agenda agentes.
77
77
 
78
+ ### Ações externas duráveis
79
+
80
+ Para efeitos externos, registre a intenção com `action-propose`, aplique a
81
+ política de capacidade e a aprovação vinculada ao fingerprint, e execute apenas
82
+ com `run-action` usando argv exato. Um resultado `COMMIT_UNKNOWN` nunca é
83
+ repetido automaticamente: observe o sistema externo e use `action-reconcile`.
84
+ As métricas mantêm tokens/custos como desconhecidos quando o host não os
85
+ fornece, e eficiência só existe quando um cenário de referência foi declarado.
86
+
78
87
  Antes de um harness criar ou retomar uma tarefa, ele pode confirmar a
79
88
  compatibilidade pública sem depender de detalhes internos:
80
89
 
@@ -195,6 +204,15 @@ forgeloop task-create --task auth-feature --claim src/auth --claim tests/auth --
195
204
  # List active and completed tasks
196
205
  forgeloop task-list --json
197
206
 
207
+ # Ask for deterministic conflict/recovery guidance
208
+ forgeloop next --task auth-feature --json
209
+
210
+ # Only for a task classified STALE or ABANDONED: release effective claims
211
+ forgeloop task-recover --task auth-feature --acknowledge-recovery --json
212
+
213
+ # Reacquire conflict-free claims before mutating a recovered task again
214
+ forgeloop task-resume --task auth-feature --json
215
+
198
216
  # Run standard lifecycle commands targeting the task
199
217
  forgeloop route --task auth-feature --work clean-code --surface backend
200
218
  forgeloop preflight --task auth-feature --json
@@ -205,6 +223,16 @@ forgeloop complete --task auth-feature --json
205
223
  forgeloop task-migrate --json
206
224
  ```
207
225
 
226
+ Recovery is not completion. Effective claims become empty only when the
227
+ canonical claim-state resolver validates the relationship between `task.json`,
228
+ `work-state.json`, `recovery.json`, and the complete hash-chained recovery
229
+ history. Fake, missing, corrupt, deleted, or mismatched recovery evidence is
230
+ `INCONSISTENT`: historical claims remain reserved and mutation remains
231
+ disabled. The standalone acknowledgement flag is not host-attested authority.
232
+ `task-resume` removes recovery state only after validated ownership, stale-lock
233
+ settlement, normal claim-overlap, and clean-checkout checks succeed. Never
234
+ create, edit, or delete `recovery.json` manually.
235
+
208
236
  ### Executable policy verification & brownfield baselines
209
237
 
210
238
  ForgeLoop enforces automated, non-interactive verification rules (`rules.json`) with zero interactive dependencies:
@@ -241,12 +269,24 @@ See [`docs/CLI_REFERENCE.md`](./docs/CLI_REFERENCE.md) and [`LOOP_SYSTEM_DESIGN.
241
269
 
242
270
  ## Architecture flow
243
271
 
244
- The canonical source is [`docs/forgeloop-flow.mmd`](./docs/forgeloop-flow.mmd),
245
- and the committed render is [`docs/assets/forgeloop-flow.svg`](./docs/assets/forgeloop-flow.svg).
272
+ The canonical source is the typed Archify workflow
273
+ [`docs/diagrams/forgeloop-engineering-flow.workflow.json`](./docs/diagrams/forgeloop-engineering-flow.workflow.json).
274
+ The committed animated interactive explorer is
275
+ [`docs/assets/diagrams/forgeloop-engineering-flow.html`](./docs/assets/diagrams/forgeloop-engineering-flow.html),
276
+ which opens in the dark presentation stage and traces the workflow. The
277
+ animated, self-contained SVG fallback is
278
+ [`docs/assets/diagrams/forgeloop-engineering-flow.svg`](./docs/assets/diagrams/forgeloop-engineering-flow.svg),
279
+ and the deterministic hash receipt is
280
+ [`docs/assets/diagrams/forgeloop-engineering-flow.receipt.json`](./docs/assets/diagrams/forgeloop-engineering-flow.receipt.json).
281
+ The governance source is [`docs/diagrams/manifest.json`](./docs/diagrams/manifest.json),
282
+ and the source-bound visual approval is kept in
283
+ [`docs/diagrams/reviews/forgeloop-engineering-flow.review.json`](./docs/diagrams/reviews/forgeloop-engineering-flow.review.json).
246
284
  The broader architecture and boundaries are in
247
285
  [`LOOP_SYSTEM_DESIGN.md`](./LOOP_SYSTEM_DESIGN.md).
248
286
 
249
- ![ForgeLoop evidence-first engineering flow](./docs/assets/forgeloop-flow.svg)
287
+ [Open the animated ForgeLoop evidence-first engineering flow](./docs/assets/diagrams/forgeloop-engineering-flow.html)
288
+
289
+ ![ForgeLoop evidence-first engineering flow (animated SVG fallback)](./docs/assets/diagrams/forgeloop-engineering-flow.svg)
250
290
 
251
291
  Text-only fallback: discovery creates the contract and route; required gates
252
292
  and `PREFLIGHT_READY` authorize execution; verification creates structured
@@ -272,6 +312,11 @@ Consumers must reject unknown artifact fields rather than silently treating
272
312
  unrecognized protocol data as valid. The compatibility marker is
273
313
  [`tests/fixtures/compatibility/protocol-v1.json`](./tests/fixtures/compatibility/protocol-v1.json).
274
314
 
315
+ A project containing active task recovery state requires ForgeLoop 1.4.0 or
316
+ newer. A reader that does not advertise
317
+ `features.taskClaimRecovery.validatedClaimProjection=true` must fail closed;
318
+ it must not infer current ownership from `task.json` or `recovery.json` alone.
319
+
275
320
  ## Security and dependency boundary
276
321
 
277
322
  The runtime uses Node built-ins only and does not install agents, providers,
@@ -281,9 +326,10 @@ values are checked; and install-capable verification requires trusted host
281
326
  authority. See [`THREAT_MODEL.md`](./THREAT_MODEL.md) for the full inventory.
282
327
 
283
328
  Development tooling is intentionally separate from runtime dependencies. The
284
- repository policy allows only ESLint, c8, and Mermaid CLI as development
285
- dependencies; `npm run dependency:policy` fails if runtime or unapproved
286
- dependencies appear.
329
+ repository policy allows only ESLint and c8 as development dependencies;
330
+ `npm run dependency:policy` fails if runtime or unapproved dependencies
331
+ appear. The documentation renderer is vendored and pinned under
332
+ `vendor/archify/v2.15.0/` rather than installed as a package dependency.
287
333
 
288
334
  Para reportar vulnerabilidades ou contribuir com alterações, consulte
289
335
  [`SECURITY.md`](./SECURITY.md) e [`CONTRIBUTING.md`](./CONTRIBUTING.md).
@@ -353,7 +399,7 @@ npm run lint
353
399
  npm run coverage
354
400
  npm run pack:check
355
401
  npm run dependency:policy
356
- npm run docs:flow
402
+ npm run docs:diagrams
357
403
  npm run docs:check
358
404
  ```
359
405
 
@@ -363,8 +409,8 @@ npm run docs:check
363
409
  src/ npm CLI and protocol implementation
364
410
  schemas/ versioned artifact schemas
365
411
  ENG/ package-source engineering guides
366
- docs/forgeloop-flow.mmd canonical Mermaid source
367
- docs/assets/ committed diagram render
412
+ docs/diagrams/ typed Archify diagram source and inventory
413
+ docs/assets/diagrams/ committed HTML, SVG, and generation receipt
368
414
  scripts/ checks, renderer, release identity, CI notes
369
415
  tests/ Node and Python regression coverage
370
416
  .forgeloop/ project-scoped ForgeLoop configuration
@@ -378,3 +424,17 @@ Task-scoped mutable protocol state is stored under
378
424
 
379
425
  For document ownership, guide routing, capability degradation, and integration
380
426
  details, start at [`DOCS_INDEX.md`](./DOCS_INDEX.md).
427
+
428
+ ## Programmatic integration and MCP
429
+
430
+ ForgeLoop exposes a stable programmatic surface:
431
+
432
+ ```js
433
+ import { executeForgeLoopCommand } from "@cassiomc1/forgeloop/integration";
434
+ ```
435
+
436
+ The local MCP server (`@cassiomc1/forgeloop-mcp`, stdio) is an adapter over
437
+ this exact API — it never reimplements ForgeLoop. See
438
+ [`docs/UNIVERSAL_INTEGRATION.md`](./docs/UNIVERSAL_INTEGRATION.md) and
439
+ [`docs/MCP.md`](./docs/MCP.md). MCP is optional; the CLI and instruction-only
440
+ hosts remain fully supported without it.
package/TERMINOLOGY.md CHANGED
@@ -18,9 +18,24 @@
18
18
  | Runtime | A process that owns execution, scheduling, model calls, or persistence; `ForgeLoop` intentionally does not provide one. |
19
19
  | Evidence kind | One of `OBSERVED`, `INFERRED`, `NOT_VERIFIED`, or `BLOCKED`; evidence never upgrades an unverified claim by itself. |
20
20
  | Required artifact | A checkpoint-recorded relative path and SHA-256 hash that must still match before resume. |
21
+ | Integration API | The stable programmatic surface (`@cassiomc1/forgeloop/integration`) used by structured consumers instead of parsing CLI output. |
22
+ | MCP adapter | The local-first Model Context Protocol server package; an adapter over canonical ForgeLoop commands, never a second implementation. |
23
+ | Server mode | The MCP launch policy tier (`readonly`, `safe`, `full`) that determines which tool classes are available. |
24
+ | Launch capability | A process-scoped, immutable MCP flag (`--allow-*`) required by higher-risk invocation classes; tool input cannot grant it. |
25
+ | Claim state | Canonical ownership classification (`ACTIVE`, `RELEASED_BY_COMPLETION`, `RELEASED_BY_RECOVERY`, `INCONSISTENT`) produced only by the validated claim resolver. |
26
+ | Historical write claims | Claims recorded as evidence in the task descriptor/recovery history after validated release. |
27
+ | Effective write claims | The claims currently enforced against overlapping acquisition; empty only for validated completion or recovery. |
28
+ | Completion ownership proof | The validated lifecycle/ledger evidence (canonical `COMPLETION_VALIDATED` + coherence) required before COMPLETE releases claims. |
29
+ | Caller acknowledgement | Explicit current-caller authorization for recovery actions; never equivalent to host attestation. |
30
+ | Legacy recovery migration | The narrow append-only repair that materializes one recognized historical recovery boundary into the modern durable representation. |
21
31
  | Conformance | Relationship validation across route, state, receipt, task brief, and delegated-result artifacts. |
22
32
  | Universal applicability | ForgeLoop applies whenever an execution environment discovers a project adapter, regardless of model, provider, agent, IDE, or tool name. |
23
33
  | Integration level | The capability tier of an execution environment (`INSTRUCTION_DISCOVERED`, `PROTOCOL_CAPABLE`, `PROTOCOL_LIMITED`, `CONFORMANCE_VERIFIED`). |
34
+ | Recovered task | A non-terminal task whose ordinary mutation authority is suspended and whose effective write claims are released by durable `recovery.json` state. |
35
+ | Recovery acknowledgement | A caller declaration that it intends to recover a task classified `STALE` or `ABANDONED`; it is not a host-attested authority grant. |
36
+ | Historical claims | The write claims retained in `task.json` as task history, including while recovery releases their active ownership. |
37
+ | Effective claims | The claims currently enforced for ownership conflicts: descriptor claims for an active task, or an empty set after validator-backed completion or active recovery. |
38
+ | Claim reacquisition | The serialized `task-resume` operation that rechecks conflicts and checkout cleanliness before removing recovery state and restoring mutation authority. |
24
39
 
25
40
  | Execution continuity | Bounded current-task implementation context used to resume the same ForgeLoop task across sessions or harnesses. |
26
41
  | Continuity artifact | `.forgeloop/continuity.json`; non-evidence operational context bound to canonical work state. |
@@ -64,6 +64,21 @@ inclusion does not install, bundle, or declare any project as a dependency of
64
64
  this collection. Check the specific project's current license, terms,
65
65
  dependencies, version, and distribution conditions before adoption.
66
66
 
67
+ ### Archify v2.15.0 — vendored documentation renderer
68
+
69
+ - Project/source: [tt-a1i/archify](https://github.com/tt-a1i/archify/tree/v2.15.0).
70
+ - Pinned source commit: `e1ac748f19cf805e44bf74fb93c796662152e273`.
71
+ - License declared by the upstream project: MIT; the vendored license is at
72
+ `vendor/archify/v2.15.0/archify/LICENSE`.
73
+ - Use in this collection: deterministic generation and validation of the
74
+ typed workflow diagram under `docs/diagrams/`.
75
+ - Boundary: this is a documentation-only vendored toolchain, not a ForgeLoop
76
+ runtime dependency. It is not installed from a registry, and its exact pin,
77
+ source, license, and raw-byte tree hashes are recorded in
78
+ `vendor/archify/v2.15.0/PIN.json`; output hashes are recorded in the receipt.
79
+ The vendor integrity check rejects modified, missing, extra, renamed, or
80
+ symlinked files before the renderer is trusted.
81
+
67
82
  ### Superpowers
68
83
 
69
84
  - Project: [Superpowers](https://github.com/obra/superpowers).
@@ -195,3 +210,18 @@ The contextual frontend taste guide is informed by Taste Skill:
195
210
  ForgeLoop includes a short, adapted guide under `ENG/taste-frontend-eng.md`.
196
211
  It does not vendor upstream runtime code, depend on its repository at runtime,
197
212
  or make its prescriptive examples universal.
213
+
214
+ ## Runtime dependencies with upstream notices
215
+
216
+ ### Model Context Protocol SDK (MCP package only)
217
+
218
+ - Packages: `@modelcontextprotocol/server` and `@modelcontextprotocol/client`
219
+ (test/smoke only), used by `integrations/mcp`. Published from the canonical
220
+ upstream repository:
221
+ [modelcontextprotocol/typescript-sdk](https://github.com/modelcontextprotocol/typescript-sdk).
222
+ - License declared by the upstream project: MIT.
223
+ - Use in this collection: official SDK transport/server primitives for the
224
+ local stdio ForgeLoop MCP adapter.
225
+ - Boundary: the ForgeLoop core package (`@cassiomc1/forgeloop`) has no
226
+ dependency on the MCP SDK; the SDK ships only inside
227
+ `@cassiomc1/forgeloop-mcp`. Review upstream terms before redistribution.
package/THREAT_MODEL.md CHANGED
@@ -8,6 +8,45 @@ not an agent runtime. It does not execute commands found in those files, call
8
8
  an LLM, or publish on behalf of a target. The controls below describe the
9
9
  remaining trust boundaries and their executable evidence.
10
10
 
11
+ ## Durable action boundary
12
+
13
+ Durable actions make external side effects explicit, but they do not make an
14
+ arbitrary remote system transactional. Side-effecting actions require an
15
+ immutable idempotency key; `COMMIT_UNKNOWN` forbids automatic retry and can be
16
+ resolved only by explicit reconciliation. Capability policy is project-local
17
+ configuration and never creates `HOST_ATTESTED` authority. Current capability
18
+ policy is the source of truth for guidance only after its identity is bound to
19
+ the active policy epoch: historical approvals are consulted
20
+ only when the current policy returns `REQUIRE_APPROVAL`. Approvals bind to the
21
+ action fingerprint, contract fingerprint, task revision, and capability; drift
22
+ makes them stale. Host-bound authorization is exposed as structured guidance
23
+ with no standalone CLI command that could lose the trusted context. `run-action`
24
+ accepts exact argv with `shell: false`, and host-reported actions remain distinct
25
+ from ForgeLoop-executed actions.
26
+
27
+ Residual limitation: ForgeLoop cannot atomically commit local protocol state
28
+ and an arbitrary external system in one transaction. Idempotency, durable
29
+ intent, evidence, and reconciliation reduce duplicate-effect risk but do not
30
+ provide a universal exactly-once guarantee.
31
+
32
+ ### Hardened durable-action threats (T-DURABLE-01 … T-DURABLE-13)
33
+
34
+ | ID | Threat | Mitigation | Test evidence |
35
+ | --- | --- | --- | --- |
36
+ | T-DURABLE-01 | Caller walks `PROPOSED -> AUTHORIZED -> STARTED -> COMMITTED -> VERIFIED` through the generic record surface | Generic transitions refuse `AUTHORIZED`/`VERIFIED`; dedicated core services own those transitions and require complete policy-bound or canonical-evidence details | `tests/action-security.test.js`, `tests/action-authorization.test.js`, `tests/action-cli.test.js` |
37
+ | T-DURABLE-02 | Actor marks an externally committed action `NOT_COMMITTED` to enable a duplicate retry | Settling `COMMITTED`/`NOT_COMMITTED` requires trusted out-of-band host attestation plus bounded evidence; actor observations may only record `UNKNOWN` | `tests/action-reconciliation.test.js`, `tests/integration-authority-context.test.js` |
38
+ | T-DURABLE-03 | Actor weakens `capabilities.json` immediately before an action launch | Current capability policy must match the persisted policy lock and task policy snapshot before authorization; drift fails closed before `ACTION_STARTED` (`E_ACTION_POLICY_DRIFT`) | `tests/action-authorization.test.js`, `tests/policy-lock.test.js` |
39
+ | T-DURABLE-04 | Model places `authorityContext` inside tool arguments or command input | Authority context is a separate host-provided parameter excluded from command input schemas; MCP tool registries strip it from args and accept it only from an embedding-controlled provider | `tests/integration-authority-context.test.js`, `integrations/mcp/tests/authority-context.test.js` |
40
+ | T-DURABLE-05 | Command validation failures are misclassified as external ambiguity | Deterministic pre-launch preparation runs before authorization; `ACTION_STARTED` is recorded only after argv normalization, resolution, installation authority, policy identity, and approval validation succeed, so pre-launch failure leaves the action `PROPOSED` with no ambiguity | `tests/run-action.test.js`, `tests/durable-action-hardening.test.js` |
41
+ | T-DURABLE-06 | Exit code 0 is mistaken for verified external state | `COMMITTED` and `VERIFIED` remain separate states; verification requires an independent passed ForgeLoop execution artifact and rejects the action's own commit execution as evidence | `tests/action-verification.test.js`, `tests/action-readiness.test.js` |
42
+ | T-DURABLE-07 | Partial ledger forgery: artifact edits or incomplete transition-like events | Deterministic ledger replay validates revision continuity, fingerprint constancy, legal chronology, modern authorization/verification evidence, reconciliation ordering, and artifact/projection equivalence during audit and completion | `tests/action-ledger-replay.test.js`, `tests/action-security.test.js` |
43
+ | T-DURABLE-08 | Reconciliation mirror replay confusion: one settlement is represented by two events and replay double-applies it | `ACTION_RECONCILED(outcome=COMMITTED)` owns the transition; a following `ACTION_COMMIT_RECORDED(reconciled=true)` is validated as a same-revision non-transition mirror and never increments state/revision; mismatched or orphaned mirrors invalidate the ledger | `tests/action-ledger-replay.test.js`, `tests/durable-action-chains.test.js` |
44
+ | T-DURABLE-09 | Cross-requirement verification substitution: a passed check for requirement A verifies action requirement B | Verification evidence must be an independent passed execution whose immutable `requirement` exactly equals the action's requirement; new required actions cannot omit their requirement | `tests/action-verification.test.js`, `tests/durable-action-chains.test.js`, `tests/action-readiness.test.js` |
45
+ | T-DURABLE-10 | Authority dropped between host and reconciliation core: trusted host supplies authority but an adapter discards it | Top-level `authorityContext` propagates through every command executor (including `action-reconcile` and `action-authorize`) to the core service; actor-controlled input remains stripped and cannot mint trust | `tests/integration-authority-context.test.js`, `integrations/mcp/tests/authority-context.test.js` |
46
+ | T-DURABLE-11 | Post-authorization approval mutation: the approval artifact changes after its fingerprint was bound into `ACTION_AUTHORIZED` | Readiness and audit recompute the canonical approval fingerprint via `validateBoundApprovalFingerprint()` for `REQUIRE_APPROVAL` authorizations; any mismatch yields UNTRUSTED / invalid audit | `tests/approval-fingerprint-integrity.test.js` |
47
+ | T-DURABLE-12 | Stale approval precedence: a pending approval created under an older policy blocks, resurrects, or obscures the current decision | `next` selects the required `PROPOSED` action and evaluates current capability policy first; only `REQUIRE_APPROVAL` validates the approval binding tuple before resolving it. `ALLOW`, `DENY`, and `REQUIRE_AUTHORITY` ignore historical approval state, while `approval-request` refuses unnecessary or weaker approvals | `tests/next-action-policy-guidance.test.js`, `tests/approval-request-policy.test.js` |
48
+ | T-DURABLE-13 | Guidance or approval creation trusts a modified `capabilities.json` before checking the active task policy epoch | `next`, `approval-request`, and `action-authorize` all validate the canonical `loadPolicyIdentity()` result before capability decisions can produce authorization-related behavior; drift fails closed with `E_ACTION_POLICY_DRIFT` and no approval artifact is persisted | `tests/next-action-policy-guidance.test.js`, `tests/approval-request-policy.test.js`, `tests/action-authorization.test.js` |
49
+
11
50
  | Threat | Impact | Trust boundary | Mitigation | Residual limitation | Test evidence |
12
51
  | --- | --- | --- | --- | --- | --- |
13
52
  | Path traversal | Writes or reads outside the selected target | Target path and every managed relative path | `ensureWithin`, safe-path checks, realpath containment, Windows-drive rejection | A separately privileged process can change the filesystem after validation | `tests/core.test.js`, `tests/portability.test.js`, `tests/fixtures/protocol/invalid/path-traversal.json` |
@@ -18,7 +57,26 @@ remaining trust boundaries and their executable evidence.
18
57
  | Forged preparation | Makes an agent's prose claim look like a completed preflight | Contract, route, gate, and preflight artifacts | Canonical SHA-256 fingerprints, guide-declared gate requirements, stale-artifact checks, and `E_*` failures | The CLI cannot stop a separate process from writing project files before preflight | `tests/protocol-artifacts.test.js`, `tests/preflight.test.js` |
19
58
  | Chronology rewrite | Hides execution before route, gates, or verification | `.forgeloop/events.ndjson` | Append-only local ledger, sequence numbers, hash chaining, and chronology validation without prompts or hidden reasoning | A privileged process can still replace the ledger after validation | `tests/lifecycle.test.js` |
20
59
  | Concurrent protocol mutation | Two writers read the same state or ledger tail and silently overwrite each other | Task state, task event ledger, and transaction journal | Mutations acquire a lease-bearing task lock, stage writes in `.forgeloop/.txn/`, preserve a recovery manifest, and publish only after the callback completes; state mutators use an expected revision, while ledger appends validate a tail checkpoint and stage only a synchronized suffix | The filesystem does not provide a multi-file atomic commit primitive; a process killed during append is recovered by truncating to the journaled pre-append size rather than treated as complete | `tests/state-revision.test.js`, `tests/concurrent-ledger.test.js`, `tests/scale-ledger.test.js`, `tests/transaction.test.js` |
21
- | Stale lock theft | A live process loses exclusive ownership because another process removes its lock | `.forgeloop/locks/<taskKey>.lock` | Locks record hostname, owner instance ID, heartbeat, and lease; inspection classifies `LIVE`, `STALE`, or `UNKNOWN`; ordinary recovery releases only an expired lease and `--force` is explicit | A malicious or separately privileged actor can still delete local locks | `tests/task-cli.test.js`, `tests/task-foundation.test.js` |
60
+ | Stale lock theft | A live process loses exclusive ownership because another process removes or replaces its lock | `.forgeloop/locks/<taskKey>.lock` | Locks record hostname, owner instance ID, heartbeat, and lease; inspection distinguishes `NONE`, `LIVE`, `STALE`, `UNKNOWN`, and `CORRUPT`; stale-only release quarantines the observed inode and compares lock ID, heartbeat, and owner instance before deletion | A malicious or separately privileged actor can still delete local locks; an expired lease remains a recovery heuristic rather than remote liveness proof | `tests/task-lock.test.js`, `tests/task-recover.test.js` |
61
+ | Forged recovery tombstone | A schema-valid fake `recovery.json` makes historical claims disappear without an official recovery event | Task descriptor, recovery artifact, and complete event ledger | The canonical claim-state resolver releases claims only when every artifact field matches one unresolved recovery history cycle; a tombstone alone is `INCONSISTENT`, retains historical claims, and disables mutation | A privileged actor can replace every linked local artifact; validation proves consistency, not remote attestation | `tests/task-claim-state.test.js`, `tests/task-claim-ownership-integration.test.js`, `tests/task-recovery-invariants.test.js` |
62
+ | Forged completion claim release | An actor changes `work-state.phase` to `COMPLETE` to make write claims disappear without canonical completion | Work state, canonical completion event, and the complete validated event ledger | Claim ownership releases COMPLETE claims only when state and a validated lifecycle ledger prove canonical completion (`COMPLETION_VALIDATED` bound to the task, coherent state/ledger, no contradicting later lifecycle event); otherwise ownership is `INCONSISTENT` with historical claims retained, mutation disabled, and overlapping acquisition blocked (`E_COMPLETION_OWNERSHIP_UNPROVEN`) | A separately privileged actor can rewrite all local artifacts consistently; ForgeLoop provides consistency verification, not external cryptographic attestation | `tests/completion-claim-ownership.test.js`, `tests/task-claim-state.test.js` |
63
+ | Incomplete task-lock identity theft | A structurally incomplete persisted task lock (missing lockId, owner instance, operation, or lease) with plausible timestamps is classified LIVE/STALE and removed as stale | `.forgeloop/locks/<taskKey>.lock` identity validation | Task lock identity requires `taskId`, `lockId`, `ownerInstanceId`, `operation`, heartbeat, and a positive integer lease; incomplete metadata classifies `UNKNOWN` (never stale-releasable), lease values are never defaulted at validation time, and CAS release additionally requires `lock.taskId === requested taskId` plus unchanged observed identity | A privileged writer can still forge a fully identified lock; classification proves structure, not liveness | `tests/task-lock.test.js` |
64
+ | Partial already-repaired relationship | A tampered recovery artifact (claims, classification, revision, fingerprint, or authority edited after migration) is accepted as an idempotent already-repaired no-op | Full canonical relationship validation in `alreadyRepaired` | Idempotency requires the complete validated ownership projection (`RELEASED_BY_RECOVERY` with matching recovery id/seq) and every artifact field agreeing with the canonical migration event; any mismatch fails closed | A separately privileged actor can rewrite all local artifacts consistently; validation proves consistency, not attestation | `tests/task-repair-legacy-recovery.test.js` |
65
+ | Forged legacy-migration authority | A forged `LEGACY_RECOVERY_MIGRATION_RECORDED` claims `HOST_ATTESTED` authority to impersonate a host grant | Migration event authority validation | Legacy migration v1 accepts only `CALLER_ACKNOWLEDGED`; any other authority kind makes the event invalid and the ledger INCONSISTENT | Normal recovery events retain their own host-attestation boundary with trusted grant references | `tests/task-repair-legacy-recovery.test.js` |
66
+ | MCP project-root substitution | A tool call supplies a different project root to read or mutate an unintended target | Immutable server-pinned project context | The ForgeLoop MCP server realpaths the project root once at startup and freezes it; project root is never a tool input | A privileged local process can still target other roots by launching its own server | `integrations/mcp/tests/` |
67
+ | MCP claim-projection fork | An adapter derives claim ownership from raw artifacts (task.json, recovery.json) and disagrees with the canonical resolver | Canonical ownership resource | The `task/ownership` resource is derived exclusively from `resolveTaskClaimState()`; forged COMPLETE stays INCONSISTENT with retained claims through the resource surface | Presentation bugs could still mislabel values; the resolver remains the single authority | `integrations/mcp/tests/ownership.test.js`, `tests/integration-resources.test.js` |
68
+ | MCP capability escalation via tool input | Tool input (e.g. `force: true`, `acknowledgeRecovery: true`) elevates a server started without the matching capability | Launch-level capability gates re-checked per invocation | Risk classification is invocation-level; disabled capabilities refuse with `E_MCP_CAPABILITY_DISABLED`; recovery acknowledgement never upgrades launch policy; legacy repair stays hidden by default; deprecated `operatorAuthorized` is absent from schemas | A separately authorized local actor can restart the server in full mode | `integrations/mcp/tests/policy.test.js`, `integrations/mcp/tests/safety.test.js` |
69
+ | MCP HTTP unauthenticated remote bind | A network-bound MCP endpoint exposes ForgeLoop operations to any reachable client without authentication | Loopback-only bind policy | The HTTP transport refuses every non-loopback bind with `E_MCP_REMOTE_NOT_SUPPORTED`; Host/Origin validation is defense against DNS rebinding, not authentication; remote access stays disabled until a separately designed authenticated boundary exists | A same-host process can still reach the loopback endpoint | `integrations/mcp/tests/http.test.js` |
70
+ | MCP protocol downgrade / legacy fallback | Legacy-era traffic is silently served, weakening the declared 2026 security posture | Strict modern mode | The HTTP handler is constructed with the SDK strict-modern setting (`legacy: "reject"`); legacy handshakes are answered with an unsupported-protocol-version rejection instead of being served | Stdio remains available for clients that only speak older protocol generations | `integrations/mcp/tests/http.test.js` |
71
+ | MCP HTTP resource exhaustion | Slow headers, slow bodies, oversized bodies, or connection floods exhaust server resources | Bounded transport controls | Header/request/keepalive timeouts are set on the HTTP server; request bodies are hard-bounded at 4 MiB (413 on exceed); an in-flight ceiling sheds load with 503 `E_MCP_HTTP_BUSY`; only POST is served | Bounds protect availability, not authorization | `integrations/mcp/tests/http.test.js` |
72
+ | Transport metadata as authority | An MCP session id, HTTP source address, Origin/Host header, or tool-supplied acknowledgement is treated as ForgeLoop authority | Authority-free adapter design | No session identity is issued or consumed; capability policy is fixed at launch and re-checked per invocation; acknowledgement fields satisfy canonical command semantics only after launch-level capability was granted | None within the adapter boundary; host-level network controls remain external | `integrations/mcp/tests/policy.test.js` |
73
+ | MCP stdout corruption / shell injection | Protocol transport polluted by diagnostics, or a generic shell tool enabling arbitrary execution | Transport discipline and exact-argv policy | stdout carries only MCP protocol; logging goes to stderr; no shell/exec tools exist; external execution passes exact argv arrays through canonical provenance | Compromised dependencies remain out of scope of transport discipline | `scripts/mcp-package-smoke.mjs` |
74
+ | Recovery tombstone deletion / claim resurrection | Deleting `recovery.json` lets a recovered task mutate after another task adopts the released scope | Recovery history and all ordinary task mutation entry points | The complete ledger derives unresolved recovery independently; a missing tombstone with unresolved history is `INCONSISTENT`, and the canonical mutation guard rejects it with `E_TASK_CLAIM_OWNERSHIP_INCONSISTENT` | A separately privileged process can still deny service by corrupting local artifacts | `tests/task-claim-state.test.js`, `tests/task-claim-ownership-integration.test.js` |
75
+ | Corrupt task namespace claim disappearance | An unreadable descriptor or recovery artifact is interpreted as claim-free during task creation or scope change | Modern task namespace discovery and project claim acquisition | Readable historical claims are retained conservatively; an unhealthy namespace with unknown ownership blocks claim mutation globally with `E_TASK_CLAIM_OWNERSHIP_INCONSISTENT` | Recovery requires repairing protocol-owned evidence; ForgeLoop does not invent unknown claims | `tests/task-claim-ownership-integration.test.js`, `tests/task-recovery-tamper.test.js` |
76
+ | Recovery event-tail eviction | Later ledger events push recovery history beyond a bounded read window and reactivate old claims | Recovery state versus append-only event history | Claim ownership validates the complete ledger and binds the artifact to its exact recovery event, recovery ID, claims, classification, authority, revision, and repository fingerprint | Full validation is intentionally correctness-first and may be optimized only with verdict-equivalent checkpoints | `tests/task-recover.test.js`, `tests/task-recovery-validation.test.js`, `tests/task-recovery-invariants.test.js` |
77
+ | Project claims lock deadlock or theft | A crashed process blocks every ownership transition, or stale cleanup removes a replacement owner | `.forgeloop/.claims.lock` | Project claim locks classify `NONE`, `LIVE`, `STALE`, `UNKNOWN`, and `CORRUPT`; acquisition may quarantine and remove only an unchanged stale inode after matching lock ID, heartbeat, and owner instance, while unknown/corrupt/CAS-mismatch state fails closed | Lease expiry is a recovery heuristic; separately privileged filesystem writers remain outside the boundary | `tests/project-claims-lock.test.js`, `tests/task-recovery-concurrency.test.js` |
78
+ | Self-asserted recovery authority | An actor labels its own recovery request operator- or host-authorized | Caller input, recovery authority metadata, and host integration boundary | Standalone recovery records only `CALLER_ACKNOWLEDGED`; the deprecated operator flag is only an alias; `HOST_ATTESTED` requires a host-owned grant reference and cannot be minted by the CLI | Host attestation remains only as strong as the host-controlled boundary and grant source | `tests/task-recovery-cli.test.js`, `tests/task-recovery-validation.test.js` |
79
+ | Recovery TOCTOU and reacquisition collision | State changes between inspection and claim release, or two tasks acquire the same released path | Project claims lock, task lock, revision, ledger sequence, and scope checks | Project-claims lock precedes task lock; recovery revalidates validated ownership, classification, revision, phase, and ledger sequence; resume validates the same ownership, CAS-settles stale task locks, and reuses normal conflict and clean-scope enforcement; concurrent tests assert at most one active owner | The filesystem cannot make repository content and multi-file metadata globally atomic against separately privileged writers | `tests/task-recovery-concurrency.test.js`, `tests/project-claims-lock.test.js`, `tests/task-lock.test.js`, `tests/task-resume.test.js` |
22
80
  | Lifecycle artifact repair | Direct state or receipt edits fabricate a legal recovery or terminal phase | Work state, receipt, evidence checks, and event ledger | New verification cycles record phase events and fingerprints; validators reject state/ledger divergence and future lifecycle evidence | Local artifacts are detection-oriented, not cryptographically tamper-proof against a privileged process rewriting every linked artifact | `tests/lifecycle-evidence-recovery.test.js`, `tests/completion-ergonomics.test.js` |
23
81
  | Unsupported profile fact | Turns an agent decision into a durable user fact | `PROJECT_PROFILE.md` and `.forgeloop/sources.json` | Source IDs, source-kind validation, unknown-reference rejection, and explicit misclassification failures | Arbitrary Markdown semantics still require a human or host-specific parser | `tests/profile-provenance.test.js`, `src/core/profile.js` |
24
82
  | Weak verification | Treats a vague or inferred claim as observed evidence | Receipt checks and coverage | Versioned check schema, contradictory-status rejection, observed-evidence requirements, and coverage matrix | Evidence remains local and declarative; it is not a remote attestation service | `tests/evidence-coverage.test.js`, `tests/completion.test.js` |
@@ -30,6 +30,11 @@ All artifact schemas are defined in `schemas/*.schema.json`. Persisted artifact
30
30
  | `policy/baseline.json` | `policy-baseline` | Protocol Generated Or Operator | Monotonic Ratchet Down | Brownfield Baseline |
31
31
  | `policy/policy.lock` | `policy-lock` | Protocol Generated | Atomic Digest Compilation | Policy Integrity Lock |
32
32
  | `task-state/<task-key>/policy-snapshot.json` | `policy-snapshot` | Protocol Generated | Mutable Before Execution | Task Policy Attestation |
33
+ | `task-state/<task-key>/recovery.json` | `task-recovery` | Protocol Generated | Recovery State Transitions | Task Recovery State |
34
+ | `task-state/<task-key>/actions/action-<id>.json` | `action` | Protocol Managed | State Machine Transitions | External Action Provenance |
35
+ | `task-state/<task-key>/approvals/approval-<id>.json` | `approval` | Protocol Managed | Append Decision Once | Action Approval Attestation |
36
+ | `policy/capabilities.json` | `capability-policy` | Operator Or Agent | Mutable Configuration | Capability Policy Specification |
37
+ | `task-state/<task-key>/evaluations/eval-<id>.json` | `trajectory-evaluation` | Protocol Compiled | Immutable Once Written | Trajectory Evaluation |
33
38
 
34
39
  <!-- END FORGELOOP GENERATED: artifact-registry -->
35
40
 
@@ -370,6 +375,16 @@ The cryptographically compiled verification receipt required for task completion
370
375
  - `selectedGuides` *(array<string>, required)*
371
376
  - `changedPaths` *(array<string>, required)*
372
377
  - `checks` *(array<object>, required)*
378
+ - `actions` *(object, optional)*
379
+ - `count` *(integer, required, minimum: 0)*
380
+ - `required` *(integer, required, minimum: 0)*
381
+ - `verified` *(integer, required, minimum: 0)*
382
+ - `trustedSatisfied` *(integer, optional, minimum: 0)*
383
+ - `unresolvedRequired` *(integer, optional, minimum: 0)*
384
+ - `failed` *(integer, required, minimum: 0)*
385
+ - `ambiguous` *(integer, required, minimum: 0)*
386
+ - `pending` *(integer, required, minimum: 0)*
387
+ - `actionRefs` *(array<string>, required)*
373
388
  - `evidence` *(array<object>, optional)*
374
389
  - `schemaVersion` *(number, optional, const: 1)*
375
390
  - `kind` *(string, required, enum: `OBSERVED`, `INFERRED`, `NOT_VERIFIED`, `BLOCKED`)*
@@ -570,6 +585,7 @@ silently ignored.
570
585
  - `digest` *(string, required, minLength: 1)*
571
586
  - `rulesDigest` *(string, required)*
572
587
  - `baselineDigest` *(string, required)*
588
+ - `capabilityPolicyDigest` *(string, optional, pattern: `^sha256:[a-f0-9]{64}$`)*
573
589
  - `capturedAt` *(string, optional)*
574
590
 
575
591
  <!-- END FORGELOOP GENERATED: schema:policy-lock -->
@@ -598,6 +614,173 @@ comparison explicitly `UNKNOWN` rather than assuming an empty baseline.
598
614
  - `rules` *(array<string,object>, required)*
599
615
  - `baseline` *(object, optional)*
600
616
  - `baselineDigest` *(string, optional)*
617
+ - `capabilityPolicyDigest` *(string, optional, pattern: `^sha256:[a-f0-9]{64}$`)*
618
+ - `capabilityPolicyFingerprint` *(string, optional, pattern: `^[a-f0-9]{64}$`)*
601
619
  - `capturedAt` *(string, optional)*
602
620
 
603
621
  <!-- END FORGELOOP GENERATED: schema:policy-snapshot -->
622
+
623
+ ---
624
+
625
+ ### 2.19 `task-state/<taskKey>/recovery.json`
626
+
627
+ <!-- forgeloop-doc: schema=task-recovery artifact=.forgeloop/task-state/<task-key>/recovery.json -->
628
+
629
+ Durable current-state input for claim-release recovery. It records the
630
+ classification and exact claims released while leaving lifecycle work state,
631
+ receipts, failures, policy, and continuity unchanged. Structural validity alone
632
+ does not release claims: ForgeLoop must also validate the descriptor, work
633
+ state, full ledger, referenced recovery event, and absence of a later matching
634
+ resume. A mismatch is `INCONSISTENT`, preserves historical claims, and suspends
635
+ ordinary mutation until the protocol-owned evidence is repaired.
636
+
637
+ `CALLER_ACKNOWLEDGED` is not host attestation. `HOST_ATTESTED` requires a
638
+ host-owned `grantRef`; the standalone CLI does not self-issue that authority.
639
+
640
+ #### Canonical Fields
641
+
642
+ <!-- BEGIN FORGELOOP GENERATED: schema:task-recovery -->
643
+
644
+ - `schemaVersion` *(number, required, const: 1)*
645
+ - `protocolVersion` *(number, required, const: 1)*
646
+ - `taskId` *(string, required, minLength: 1)*
647
+ - `status` *(string, required, const: `RECOVERED`)*
648
+ - `recoveredAt` *(string, required)*
649
+ - `recoveryId` *(string, required, pattern: `^recovery-[A-Za-z0-9-]+$`)*
650
+ - `recoveryEventSeq` *(integer, required, minimum: 1)*
651
+ - `classificationAtRecovery` *(string, required, enum: `STALE`, `ABANDONED`, `LEGACY_BOUNDARY_MIGRATED`)*
652
+ - `reasonCodes` *(array<string>, required)*
653
+ - `releasedClaims` *(array<string>, required)*
654
+ - `previousPhase` *(string, required, minLength: 1)*
655
+ - `previousRevision` *(integer, required, minimum: 0)*
656
+ - `repositoryFingerprint` *(object, required)*
657
+ - `branch` *(string,null, required)*
658
+ - `head` *(string,null, required)*
659
+ - `authority` *(object, required)*
660
+ - `kind` *(string, required, enum: `CALLER_ACKNOWLEDGED`, `HOST_ATTESTED`)*
661
+ - `grantRef` *(string, optional, minLength: 1)*
662
+
663
+ <!-- END FORGELOOP GENERATED: schema:task-recovery -->
664
+
665
+ ---
666
+
667
+ ### 2.20 `task-state/<taskKey>/actions/action-<id>.json`
668
+
669
+ <!-- forgeloop-doc: schema=action artifact=.forgeloop/task-state/<task-key>/actions/action-<id>.json -->
670
+
671
+ Durable external action artifact recording intent, capability policy binding,
672
+ authority, execution provenance, ambiguity, and reconciliation state for a
673
+ side-effecting operation. The `actionFingerprint` covers immutable identity
674
+ fields only; mutable state lives in `state` and `revision`.
675
+
676
+ #### Canonical Fields
677
+
678
+ <!-- BEGIN FORGELOOP GENERATED: schema:action -->
679
+
680
+ - `schemaVersion` *(number, required, const: 1)*
681
+ - `taskId` *(string, required, minLength: 1, maxLength: 256)*
682
+ - `actionId` *(string, required, pattern: `^action-[A-Za-z0-9_-]+$`)*
683
+ - `actionFingerprint` *(string, required, pattern: `^[a-f0-9]{64}$`)*
684
+ - `effectClass` *(string, required, enum: `READ_ONLY`, `REVERSIBLE_WRITE`, `IRREVERSIBLE_WRITE`, `EXTERNAL_PUBLICATION`, `DESTRUCTIVE`)*
685
+ - `capability` *(string, required, enum: `filesystem.read`, `filesystem.write`, `process.execute`, `dependency.install`, `network.read`, `network.write`, `repository.commit`, `repository.push`, `repository.pull_request`, `external.publish`, `external.delete`, `deployment.execute`)*
686
+ - `operation` *(string, required, minLength: 1, maxLength: 512)*
687
+ - `target` *(string, required, minLength: 1, maxLength: 512)*
688
+ - `idempotencyKey` *(string or null, required)*
689
+ - `requiredForCompletion` *(boolean, required)*
690
+ - `requirement` *(string or null, required)*
691
+ - `provenance` *(string, required, enum: `FORGELOOP_EXECUTED`, `HOST_ATTESTED`, `CALLER_REPORTED`, `HOST_REPORTED`, `EXTERNAL_OBSERVED`)*
692
+ - `state` *(string, required, enum: `PROPOSED`, `AUTHORIZED`, `STARTED`, `COMMITTED`, `VERIFIED`, `FAILED`, `COMMIT_UNKNOWN`, `CANCELLED`)*
693
+ - `revision` *(integer, required, minimum: 0)*
694
+ - `createdAt` *(string, required, minLength: 1)*
695
+ - `updatedAt` *(string, required, minLength: 1)*
696
+ - `lastEvidenceRef` *(string or null, optional)*
697
+ - `lastReconciliationAt` *(string or null, optional)*
698
+ - `commitResultCode` *(object or null, optional)*
699
+
700
+ <!-- END FORGELOOP GENERATED: schema:action -->
701
+
702
+ ---
703
+
704
+ ### 2.21 `task-state/<taskKey>/approvals/approval-<id>.json`
705
+
706
+ <!-- forgeloop-doc: schema=approval artifact=.forgeloop/task-state/<task-key>/approvals/approval-<id>.json -->
707
+
708
+ Crash-safe durable approval request cryptographically bound to the exact action
709
+ fingerprint, contract fingerprint, task revision, and capability. Any drift
710
+ makes the approval stale. Resolution is one-time; approvals persist across
711
+ process and harness boundaries.
712
+
713
+ #### Canonical Fields
714
+
715
+ <!-- BEGIN FORGELOOP GENERATED: schema:approval -->
716
+
717
+ - `schemaVersion` *(number, required, const: 1)*
718
+ - `taskId` *(string, required, minLength: 1, maxLength: 256)*
719
+ - `approvalId` *(string, required, pattern: `^approval-[A-Za-z0-9_-]+$`)*
720
+ - `actionId` *(string, required, pattern: `^action-[A-Za-z0-9_-]+$`)*
721
+ - `actionFingerprint` *(string, required, pattern: `^[a-f0-9]{64}$`)*
722
+ - `contractFingerprint` *(string, required, pattern: `^[a-f0-9]{64}$`)*
723
+ - `taskRevision` *(integer, required, minimum: 0)*
724
+ - `capability` *(string, required, enum: `filesystem.read`, `filesystem.write`, `process.execute`, `dependency.install`, `network.read`, `network.write`, `repository.commit`, `repository.push`, `repository.pull_request`, `external.publish`, `external.delete`, `deployment.execute`)*
725
+ - `status` *(string, required, enum: `PENDING`, `APPROVED`, `REJECTED`)*
726
+ - `requestedAt` *(string, required, minLength: 1)*
727
+ - `reason` *(string or null, optional)*
728
+ - `decision` *(string, optional, enum: `APPROVED`, `REJECTED`)*
729
+ - `resolvedAt` *(string or null, optional)*
730
+ - `authorityKind` *(string, optional, enum: `CALLER_ACKNOWLEDGED`, `HOST_ATTESTED`)*
731
+ - `hostGrantRef` *(string or null, optional)*
732
+
733
+ <!-- END FORGELOOP GENERATED: schema:approval -->
734
+
735
+ ---
736
+
737
+ ### 2.22 `policy/capabilities.json`
738
+
739
+ <!-- forgeloop-doc: schema=capability-policy artifact=.forgeloop/policy/capabilities.json -->
740
+
741
+ Project-local machine-readable capability policy mapping canonical capability
742
+ values to ALLOW, DENY, REQUIRE_AUTHORITY, or REQUIRE_APPROVAL decisions. This
743
+ artifact is policy specification only; it can never mint host authority.
744
+
745
+ #### Canonical Fields
746
+
747
+ <!-- BEGIN FORGELOOP GENERATED: schema:capability-policy -->
748
+
749
+ - `schemaVersion` *(number, required, const: 1)*
750
+ - `defaultDecision` *(string, required, enum: `ALLOW`, `DENY`)*
751
+ - `rules` *(array<object>, required)*
752
+ - `capability` *(string, required, enum: `filesystem.read`, `filesystem.write`, `process.execute`, `dependency.install`, `network.read`, `network.write`, `repository.commit`, `repository.push`, `repository.pull_request`, `external.publish`, `external.delete`, `deployment.execute`)*
753
+ - `decision` *(string, required, enum: `ALLOW`, `DENY`, `REQUIRE_AUTHORITY`, `REQUIRE_APPROVAL`)*
754
+
755
+ <!-- END FORGELOOP GENERATED: schema:capability-policy -->
756
+
757
+ ---
758
+
759
+ ### 2.23 `task-state/<taskKey>/evaluations/eval-<id>.json`
760
+
761
+ <!-- forgeloop-doc: schema=trajectory-evaluation artifact=.forgeloop/task-state/<task-key>/evaluations/eval-<id>.json -->
762
+
763
+ Immutable trajectory evaluation result compiled from the canonical trace against
764
+ a project-local reference scenario. Evaluations are projections over canonical
765
+ evidence and never override lifecycle validation.
766
+
767
+ #### Canonical Fields
768
+
769
+ <!-- BEGIN FORGELOOP GENERATED: schema:trajectory-evaluation -->
770
+
771
+ - `schemaVersion` *(number, required, const: 1)*
772
+ - `evaluationId` *(string, required, pattern: `^eval-[A-Za-z0-9_-]+$`)*
773
+ - `scenarioId` *(string, required, minLength: 1, maxLength: 128)*
774
+ - `scenarioFingerprint` *(string, optional, pattern: `^[a-f0-9]{64}$`)*
775
+ - `taskId` *(string, required, minLength: 1, maxLength: 256)*
776
+ - `result` *(string, required, enum: `PASS`, `FAIL`)*
777
+ - `completionValid` *(boolean, required)*
778
+ - `safetyValid` *(boolean, required)*
779
+ - `missingMilestones` *(array<string>, optional)*
780
+ - `limits` *(object, optional)*
781
+ - `efficiency` *(object or null, optional)*
782
+ - `computedAt` *(string, optional, minLength: 1)*
783
+ - `source` *(string, optional, enum: `PROJECT_LOCAL_REFERENCE`)*
784
+ - `evaluationFingerprint` *(string, required, pattern: `^[a-f0-9]{64}$`)*
785
+
786
+ <!-- END FORGELOOP GENERATED: schema:trajectory-evaluation -->