@cassiomc1/forgeloop 1.2.4 → 1.5.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 (92) hide show
  1. package/.github/copilot-instructions.md +1 -0
  2. package/AGENTS.md +1 -0
  3. package/AGENT_COMPATIBILITY.md +4 -0
  4. package/CLAUDE.md +1 -0
  5. package/DOCS_INDEX.md +14 -0
  6. package/EXECUTION_STATE.md +48 -0
  7. package/LOOP_ENGINEERING.md +55 -6
  8. package/LOOP_SYSTEM_DESIGN.md +32 -1
  9. package/PROTOCOL_INTEGRATION.md +71 -0
  10. package/README.md +86 -0
  11. package/TERMINOLOGY.md +15 -0
  12. package/THIRD_PARTY_NOTICES.md +15 -0
  13. package/THREAT_MODEL.md +22 -1
  14. package/docs/ARTIFACT_REFERENCE.md +54 -0
  15. package/docs/CLI_REFERENCE.md +177 -5
  16. package/docs/CROSS_HARNESS_CONTINUITY.md +34 -0
  17. package/docs/DOCUMENTATION_GUIDE.md +31 -0
  18. package/docs/GETTING_STARTED.md +10 -0
  19. package/docs/MCP.md +126 -0
  20. package/docs/RECIPES.md +87 -1
  21. package/docs/RELEASE_CHECKLIST_1_4.md +38 -0
  22. package/docs/RELEASE_CHECKLIST_1_5_MCP.md +78 -0
  23. package/docs/TROUBLESHOOTING.md +243 -40
  24. package/docs/UNIVERSAL_INTEGRATION.md +48 -0
  25. package/package.json +17 -3
  26. package/schemas/execution.schema.json +11 -1
  27. package/schemas/task-recovery.schema.json +61 -0
  28. package/schemas/work-state.schema.json +1 -0
  29. package/src/cli.js +182 -337
  30. package/src/commands/audit.js +5 -0
  31. package/src/commands/doctor.js +22 -0
  32. package/src/commands/inspect.js +6 -0
  33. package/src/commands/migrate-protocol.js +18 -0
  34. package/src/commands/progress.js +6 -2
  35. package/src/commands/protocol-info.js +16 -0
  36. package/src/commands/run-check.js +2 -0
  37. package/src/commands/status.js +17 -0
  38. package/src/commands/task-create.js +42 -3
  39. package/src/commands/task-list.js +14 -1
  40. package/src/commands/task-lock-status.js +29 -0
  41. package/src/commands/task-recover.js +202 -0
  42. package/src/commands/task-repair-legacy-recovery.js +417 -0
  43. package/src/commands/task-resume.js +172 -0
  44. package/src/commands/task-scope.js +23 -4
  45. package/src/commands/task-show.js +21 -7
  46. package/src/commands/task-unlock.js +8 -6
  47. package/src/commands/validate-protocol.js +19 -2
  48. package/src/core/artifact-registry.js +12 -0
  49. package/src/core/artifacts.js +17 -4
  50. package/src/core/audit.js +20 -4
  51. package/src/core/bundles.js +15 -0
  52. package/src/core/cli-command-definitions.js +94 -4
  53. package/src/core/command-executors.js +387 -0
  54. package/src/core/command-input.js +107 -0
  55. package/src/core/command-runtime.js +106 -0
  56. package/src/core/completion-artifacts.js +17 -6
  57. package/src/core/completion-ownership.js +88 -0
  58. package/src/core/completion.js +15 -3
  59. package/src/core/diagnosis.js +15 -11
  60. package/src/core/error-codes.js +136 -1
  61. package/src/core/events.js +239 -9
  62. package/src/core/execution.js +73 -9
  63. package/src/core/filesystem.js +75 -8
  64. package/src/core/inspect.js +27 -0
  65. package/src/core/integration-invocation-policy.js +170 -0
  66. package/src/core/integration-limits.js +20 -0
  67. package/src/core/integration-resources.js +127 -0
  68. package/src/core/next-action-model.js +60 -0
  69. package/src/core/next-action.js +31 -0
  70. package/src/core/phase.js +10 -3
  71. package/src/core/project-root.js +21 -0
  72. package/src/core/protocol-info.js +54 -0
  73. package/src/core/protocol-migration.js +59 -0
  74. package/src/core/reconcile-closure.js +54 -14
  75. package/src/core/recovery-history.js +116 -0
  76. package/src/core/resumability.js +8 -6
  77. package/src/core/schema-validation.js +1 -0
  78. package/src/core/task-claim-state.js +272 -0
  79. package/src/core/task-command.js +8 -4
  80. package/src/core/task-conflict-inspection.js +321 -0
  81. package/src/core/task-context.js +32 -29
  82. package/src/core/task-discovery.js +14 -1
  83. package/src/core/task-lock.js +248 -18
  84. package/src/core/task-migration.js +24 -1
  85. package/src/core/task-paths.js +6 -3
  86. package/src/core/task-recovery-migration.js +192 -0
  87. package/src/core/task-recovery.js +205 -0
  88. package/src/core/task-scope.js +33 -1
  89. package/src/core/templates.js +1 -0
  90. package/src/core/transaction.js +285 -0
  91. package/src/core/work-state.js +70 -6
  92. package/src/integration.js +47 -0
package/THREAT_MODEL.md CHANGED
@@ -17,6 +17,27 @@ remaining trust boundaries and their executable evidence.
17
17
  | Malicious work-state | Resumes stale, secret-bearing, or invalid work | `.forgeloop/work-state.json` | Schema/semantic checks, version checks, transition checks, contract/HEAD/artifact freshness, secret scan, size/depth bounds | Freshness cannot prove that an external process did not alter a file immediately afterward | `tests/work-state.test.js`, `tests/checkpoint-freshness.test.js`, `tests/security-limits.test.js` |
18
18
  | 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
19
  | 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
+ | 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 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` |
22
+ | 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` |
23
+ | 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` |
24
+ | 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` |
25
+ | 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` |
26
+ | 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` |
27
+ | 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/` |
28
+ | 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` |
29
+ | 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` |
30
+ | 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` |
31
+ | 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` |
32
+ | 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` |
33
+ | 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` |
34
+ | 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` |
35
+ | 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` |
36
+ | 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` |
37
+ | 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` |
38
+ | 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` |
39
+ | 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` |
40
+ | 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` |
20
41
  | 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` |
21
42
  | 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` |
22
43
  | 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` |
@@ -53,7 +74,7 @@ remaining trust boundaries and their executable evidence.
53
74
  | Lock regeneration or subdigest tampering | An actor modifies rules or baseline, omits subdigests, or regenerates `policy.lock` to hide tampering | Effective policy lock verification (`verifyPolicyLock`) | Lock derives deterministically from effective rules + baseline. Schema-invalid lock artifacts (malformed JSON or missing required fields such as `rulesDigest`/`baselineDigest`) fail closed with `E_POLICY_INVALID`; schema-valid locks whose semantic digest components (`algorithm`, `digest`, `rulesDigest`, `baselineDigest`) no longer match effective policy state fail with `E_POLICY_LOCK_MISMATCH` before any relocking. Changing only `capturedAt` does not change semantic identity | Explicit update commands (`policy-discover --write`, `baseline`) update lock after authorization | `tests/policy-hardening.test.js` |
54
75
  | Baseline re-record bypass | An actor uses `baseline --record` during an active task to convert newly introduced violations into tolerated debt | Active task baseline protection in `runBaseline` | `baseline --record` is rejected during active policy-bound tasks with `E_BASELINE_RECORD_DURING_ACTIVE_TASK`; only monotonic `--update` is permitted | Explicit `--policy-reset-authorized` flag required for intentional operator resets | `tests/policy-hardening.test.js` |
55
76
  | Legacy snapshot semantic confusion | A legacy task snapshot lacking baseline state is assumed to have empty baseline, creating false `WEAKEN` drift | Semantic baseline snapshotting in `policy-snapshot.json` | Snapshots retain full semantic baseline entries; snapshots lacking baseline state classify drift as `UNKNOWN` rather than inventing state | Modern tasks retain semantic baseline entries for exact diff | `tests/policy-hardening.test.js` |
56
- | Cross-task state confusion | Concurrent processes or alternative harnesses mutate or read the wrong task's state in a multi-task workspace | Task-scoped isolation in `.forgeloop/task-state/<taskKey>/` | Deterministic SHA-256 task directory namespacing, explicit `--task` / `FORGELOOP_TASK` selectors, file-level mutex locking (`.lock`), and claim overlap detection in `task-create` | Tasks must declare non-overlapping write claims or operate in isolated worktrees | `tests/task-namespace.test.js`, `tests/task-scope.test.js` |
77
+ | Cross-task state confusion | Concurrent processes or alternative harnesses mutate or read the wrong task's state in a multi-task workspace | Task-scoped isolation in `.forgeloop/task-state/<taskKey>/` | Deterministic SHA-256 task directory namespacing, explicit `--task` / `FORGELOOP_TASK` selectors, separate file-level lease locks under `.forgeloop/locks/`, and claim overlap detection in `task-create` | Tasks must declare non-overlapping write claims or operate in isolated worktrees | `tests/task-namespace.test.js`, `tests/task-scope.test.js` |
57
78
  | Modern namespace descriptor deletion | Corruption or an attacker removes `task.json` from a modern task namespace (leaving contract/work-state/receipt/events), the resolver ignores the namespace, and stale legacy singleton state becomes authoritative | Descriptor boundary between modern task namespaces and the legacy singleton | Descriptor-less 64-hex directories are classified by contents: directories containing modern task artifacts (or empty directories) fail closed with `E_TASK_DESCRIPTOR_INVALID`; only directories containing exclusively the explicitly recognized legacy-incidental artifact (`policy-snapshot.json`) are ignored; `resolveTaskContext` fails closed when all namespaces are corrupt | A separately privileged process can rewrite task-state directories after validation | `tests/validate-receipt-task.test.js` |
58
79
 
59
80
  ## Boundary rules
@@ -30,6 +30,7 @@ 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 |
33
34
 
34
35
  <!-- END FORGELOOP GENERATED: artifact-registry -->
35
36
 
@@ -287,6 +288,7 @@ The canonical, authoritative lifecycle work state. Represents current checkpoint
287
288
  - `failures` *(array<object>, required)*
288
289
  - `blockers` *(array<object>, required)*
289
290
  - `lastUpdated` *(string, required, minLength: 1)*
291
+ - `revision` *(integer, optional, minimum: 0)*
290
292
  - `previousPhase` *(string, optional)*
291
293
  - `diagnosedHypothesis` *(string, optional, minLength: 1)*
292
294
  - `verificationEvidence` *(array<object>, optional)*
@@ -421,6 +423,16 @@ Attested command execution provenance artifact generated by `forgeloop run-check
421
423
  - `finishedAt` *(string, required, minLength: 1)*
422
424
  - `status` *(string, required, enum: `passed`, `failed`)*
423
425
  - `exitCode` *(integer or null, required)*
426
+ - `durationMs` *(integer, optional, minimum: 0)*
427
+ - `termination` *(string, optional, enum: `exit`, `signal`, `timeout`, `spawn-error`)*
428
+ - `signal` *(string or null, optional)*
429
+ - `stdoutSha256` *(string, optional, pattern: `^[a-f0-9]{64}$`)*
430
+ - `stderrSha256` *(string, optional, pattern: `^[a-f0-9]{64}$`)*
431
+ - `stdoutBytes` *(integer, optional, minimum: 0)*
432
+ - `stderrBytes` *(integer, optional, minimum: 0)*
433
+ - `outputTruncated` *(boolean, optional)*
434
+ - `timeoutMs` *(integer, optional, minimum: 1)*
435
+ - `terminationGraceMs` *(integer, optional, minimum: 1)*
424
436
 
425
437
  <!-- END FORGELOOP GENERATED: schema:execution -->
426
438
 
@@ -590,3 +602,45 @@ comparison explicitly `UNKNOWN` rather than assuming an empty baseline.
590
602
  - `capturedAt` *(string, optional)*
591
603
 
592
604
  <!-- END FORGELOOP GENERATED: schema:policy-snapshot -->
605
+
606
+ ---
607
+
608
+ ### 2.19 `task-state/<taskKey>/recovery.json`
609
+
610
+ <!-- forgeloop-doc: schema=task-recovery artifact=.forgeloop/task-state/<task-key>/recovery.json -->
611
+
612
+ Durable current-state input for claim-release recovery. It records the
613
+ classification and exact claims released while leaving lifecycle work state,
614
+ receipts, failures, policy, and continuity unchanged. Structural validity alone
615
+ does not release claims: ForgeLoop must also validate the descriptor, work
616
+ state, full ledger, referenced recovery event, and absence of a later matching
617
+ resume. A mismatch is `INCONSISTENT`, preserves historical claims, and suspends
618
+ ordinary mutation until the protocol-owned evidence is repaired.
619
+
620
+ `CALLER_ACKNOWLEDGED` is not host attestation. `HOST_ATTESTED` requires a
621
+ host-owned `grantRef`; the standalone CLI does not self-issue that authority.
622
+
623
+ #### Canonical Fields
624
+
625
+ <!-- BEGIN FORGELOOP GENERATED: schema:task-recovery -->
626
+
627
+ - `schemaVersion` *(number, required, const: 1)*
628
+ - `protocolVersion` *(number, required, const: 1)*
629
+ - `taskId` *(string, required, minLength: 1)*
630
+ - `status` *(string, required, const: `RECOVERED`)*
631
+ - `recoveredAt` *(string, required)*
632
+ - `recoveryId` *(string, required, pattern: `^recovery-[A-Za-z0-9-]+$`)*
633
+ - `recoveryEventSeq` *(integer, required, minimum: 1)*
634
+ - `classificationAtRecovery` *(string, required, enum: `STALE`, `ABANDONED`, `LEGACY_BOUNDARY_MIGRATED`)*
635
+ - `reasonCodes` *(array<string>, required)*
636
+ - `releasedClaims` *(array<string>, required)*
637
+ - `previousPhase` *(string, required, minLength: 1)*
638
+ - `previousRevision` *(integer, required, minimum: 0)*
639
+ - `repositoryFingerprint` *(object, required)*
640
+ - `branch` *(string,null, required)*
641
+ - `head` *(string,null, required)*
642
+ - `authority` *(object, required)*
643
+ - `kind` *(string, required, enum: `CALLER_ACKNOWLEDGED`, `HOST_ATTESTED`)*
644
+ - `grantRef` *(string, optional, minLength: 1)*
645
+
646
+ <!-- END FORGELOOP GENERATED: schema:task-recovery -->
@@ -39,9 +39,9 @@ ForgeLoop uses a definition-driven command-line parser:
39
39
 
40
40
  | Category | Commands |
41
41
  | --- | --- |
42
- | **Setup & Maintenance** | [`init`](#init), [`update`](#update), [`task-migrate`](#task-migrate), [`task-unlock`](#task-unlock) |
43
- | **Inspection & Diagnostics** | [`doctor`](#doctor), [`progress`](#progress), [`profile-interview`](#profile-interview), [`inspect`](#inspect), [`status`](#status), [`validate-state`](#validate-state), [`validate-protocol`](#validate-protocol) |
44
- | **Lifecycle & State** | [`activate`](#activate), [`route`](#route), [`preflight`](#preflight), [`advance`](#advance), [`next`](#next), [`record-diagnosis`](#record-diagnosis), [`record-decision-criterion`](#record-decision-criterion), [`complete`](#complete), [`clear-state`](#clear-state), [`reconcile-closure`](#reconcile-closure), [`task-create`](#task-create), [`task-list`](#task-list), [`task-show`](#task-show), [`task-scope`](#task-scope) |
42
+ | **Inspection & Diagnostics** | [`protocol-info`](#protocol-info), [`doctor`](#doctor), [`progress`](#progress), [`profile-interview`](#profile-interview), [`inspect`](#inspect), [`status`](#status), [`validate-state`](#validate-state), [`validate-protocol`](#validate-protocol) |
43
+ | **Setup & Maintenance** | [`init`](#init), [`update`](#update), [`task-migrate`](#task-migrate), [`migrate-protocol`](#migrate-protocol), [`task-unlock`](#task-unlock), [`task-recover`](#task-recover), [`task-repair-legacy-recovery`](#task-repair-legacy-recovery), [`task-resume`](#task-resume) |
44
+ | **Lifecycle & State** | [`activate`](#activate), [`route`](#route), [`preflight`](#preflight), [`advance`](#advance), [`next`](#next), [`record-diagnosis`](#record-diagnosis), [`record-decision-criterion`](#record-decision-criterion), [`complete`](#complete), [`clear-state`](#clear-state), [`reconcile-closure`](#reconcile-closure), [`task-create`](#task-create), [`task-list`](#task-list), [`task-show`](#task-show), [`task-lock-status`](#task-lock-status), [`task-scope`](#task-scope) |
45
45
  | **Cross-Harness Continuity** | [`continuity`](#continuity), [`record-continuity`](#record-continuity), [`reconcile-continuity`](#reconcile-continuity), [`clear-continuity`](#clear-continuity) |
46
46
  | **Verification & Completion** | [`prepare-completion`](#prepare-completion), [`run-check`](#run-check), [`record-check`](#record-check), [`record-terminal-result`](#record-terminal-result), [`audit`](#audit), [`report`](#report), [`validate-receipt`](#validate-receipt) |
47
47
  | **Policy & Auditing** | [`policy`](#policy), [`policy-discover`](#policy-discover), [`policy-status`](#policy-status), [`policy-diff`](#policy-diff), [`rule-verify`](#rule-verify), [`baseline`](#baseline), [`bundle`](#bundle) |
@@ -52,6 +52,30 @@ ForgeLoop uses a definition-driven command-line parser:
52
52
 
53
53
  ## 1. Setup & Maintenance
54
54
 
55
+ ### `protocol-info`
56
+
57
+ Reports the public compatibility handshake required by external ForgeLoop harnesses.
58
+
59
+ - **Purpose**: Publishes protocol/schema versioning, lifecycle metadata, command metadata, guide registry, and the documented error registry.
60
+ - **When to use**: Before a harness creates or resumes a ForgeLoop task, and when verifying compatibility without reading internal source.
61
+ - **Mutation**: Read-only.
62
+ - **Options**:
63
+
64
+ <!-- BEGIN FORGELOOP GENERATED: cli:protocol-info:options -->
65
+
66
+ - `--path <directory>`: target project directory (default: current directory)
67
+ - `--json`: emit complete machine-readable protocol metadata
68
+
69
+ <!-- END FORGELOOP GENERATED: cli:protocol-info:options -->
70
+
71
+ - **Example**:
72
+
73
+ <!-- FORGELOOP EXAMPLE: cli-reference:protocol-info | exit=0 | json.commands.0.name=protocol-info -->
74
+ ```bash
75
+ forgeloop protocol-info --json
76
+ ```
77
+ <!-- END FORGELOOP EXAMPLE -->
78
+
55
79
  ### `init`
56
80
 
57
81
  Initializes ForgeLoop in a target repository.
@@ -407,6 +431,7 @@ Executes a verification command with ForgeLoop-attested provenance.
407
431
  - `--id <id>`: stable check identifier
408
432
  - `--requirement <id>`: completion requirement covered by the check
409
433
  - `--details <json>`: additional structured check details
434
+ - `--timeout-ms <number>`: maximum command duration before termination
410
435
  - `-- <argv...>`: exact command argv to classify, execute, and attest
411
436
  - `--json`: emit structured output as JSON
412
437
 
@@ -1139,6 +1164,27 @@ Displays details of a specific task by ID or storage key.
1139
1164
  forgeloop task-show --task task-001 --json
1140
1165
  ```
1141
1166
 
1167
+ ### `task-lock-status`
1168
+
1169
+ Reports the lock owner and lease-based staleness classification for a specific task without mutating it.
1170
+
1171
+ - **Mutation**: Read-only.
1172
+ - **Options**:
1173
+
1174
+ <!-- BEGIN FORGELOOP GENERATED: cli:task-lock-status:options -->
1175
+
1176
+ - `--path <directory>`: target project directory (default: current directory)
1177
+ - `--task <id>`: task ID to operate on (when omitted, resolved from context or single active task)
1178
+ - `--json`: emit structured output as JSON
1179
+
1180
+ <!-- END FORGELOOP GENERATED: cli:task-lock-status:options -->
1181
+
1182
+ - **Example**:
1183
+
1184
+ ```bash
1185
+ forgeloop task-lock-status --task task-001 --json
1186
+ ```
1187
+
1142
1188
  ### `task-scope`
1143
1189
 
1144
1190
  Updates or inspects write claims for a task.
@@ -1184,11 +1230,42 @@ Migrates a legacy 1.0 single-task `.forgeloop/` layout into a namespaced task di
1184
1230
  forgeloop task-migrate --json
1185
1231
  ```
1186
1232
 
1233
+ ### `migrate-protocol`
1234
+
1235
+ Plans or applies an explicitly supported persisted-state migration to a target
1236
+ protocol version.
1237
+
1238
+ - **Purpose**: Provides a fail-closed compatibility migration surface. In the
1239
+ current release, target protocol `1` either needs no change or converts a
1240
+ detected legacy singleton layout through the verified `task-migrate` flow.
1241
+ - **When to use**: Before upgrading persisted ForgeLoop state when a release
1242
+ documents a new protocol migration. Run the dry-run first and retain the
1243
+ resulting migration receipt after applying a legacy conversion.
1244
+ - **Mutation**: Does not write with `--dry-run`. A supported legacy conversion
1245
+ writes the namespaced task state and its `migration-receipt.json` before
1246
+ removing legacy artifacts.
1247
+ - **Options**:
1248
+
1249
+ <!-- BEGIN FORGELOOP GENERATED: cli:migrate-protocol:options -->
1250
+
1251
+ - `--path <directory>`: target project directory (default: current directory)
1252
+ - `--to <protocolVersion>`: target supported protocol version
1253
+ - `--dry-run`: show migration actions without writing or deleting artifacts
1254
+ - `--json`: emit structured migration result as JSON
1255
+
1256
+ <!-- END FORGELOOP GENERATED: cli:migrate-protocol:options -->
1257
+
1258
+ - **Example**:
1259
+
1260
+ ```bash
1261
+ forgeloop migrate-protocol --to 1 --dry-run --json
1262
+ ```
1263
+
1187
1264
  ### `task-unlock`
1188
1265
 
1189
- Forces the release of a stale task lock.
1266
+ Forces the release of a task lock or CAS-safely releases an unchanged stale lease.
1190
1267
 
1191
- - **Purpose**: Removes `.lock` file from the task directory when process crashed.
1268
+ - **Purpose**: Removes `.forgeloop/locks/<taskKey>.lock` when its owner is no longer valid. Prefer `--stale-only`; `--force` is an explicit unconditional maintenance action.
1192
1269
  - **Mutation**: Deletes task lock file.
1193
1270
  - **Options**:
1194
1271
 
@@ -1197,6 +1274,7 @@ Forces the release of a stale task lock.
1197
1274
  - `--path <directory>`: target project directory (default: current directory)
1198
1275
  - `--task <id>`: task ID to operate on (when omitted, resolved from context or single active task)
1199
1276
  - `--force`: force release of an orphaned task lock
1277
+ - `--stale-only`: release only a lock whose lease is expired
1200
1278
  - `--json`: emit structured output as JSON
1201
1279
 
1202
1280
  <!-- END FORGELOOP GENERATED: cli:task-unlock:options -->
@@ -1206,3 +1284,97 @@ Forces the release of a stale task lock.
1206
1284
  ```bash
1207
1285
  forgeloop task-unlock --task task-001 --force --json
1208
1286
  ```
1287
+
1288
+ ### `task-recover`
1289
+
1290
+ Suspends mutation and releases effective claims for a task deterministically classified `STALE` or `ABANDONED`.
1291
+
1292
+ - **Purpose**: For a task classified only `STALE` or `ABANDONED`, persists `recovery.json` plus a linked append-only event without changing work state or fabricating completion. The canonical claim-state resolver must validate both before claims become effective-empty. `RECOVERABLE` tasks must use `reconcile-closure`.
1293
+ - **Mutation**: Transactionally writes recovery state and appends the recovery event. Historical descriptor claims and all lifecycle evidence remain intact; ordinary mutations return `E_TASK_RECOVERED`.
1294
+ - **Options**:
1295
+
1296
+ <!-- BEGIN FORGELOOP GENERATED: cli:task-recover:options -->
1297
+
1298
+ - `--path <directory>`: target project directory (default: current directory)
1299
+ - `--task <id>`: task ID to operate on (when omitted, resolved from context or single active task)
1300
+ - `--acknowledge-recovery`: acknowledge release of claims for a STALE or ABANDONED task (required; not host attestation)
1301
+ - `--operator-authorized`: deprecated alias for --acknowledge-recovery; does not attest operator authority
1302
+ - `--json`: emit structured output as JSON
1303
+
1304
+ <!-- END FORGELOOP GENERATED: cli:task-recover:options -->
1305
+
1306
+ - **Example**:
1307
+
1308
+ ```bash
1309
+ forgeloop task-recover --task task-001 --acknowledge-recovery --json
1310
+ ```
1311
+
1312
+ `--acknowledge-recovery` is caller acknowledgement only. The deprecated
1313
+ `--operator-authorized` alias has the same semantics and is not host attestation.
1314
+ Fake, missing, corrupt, or mismatched recovery state is
1315
+ `E_TASK_CLAIM_OWNERSHIP_INCONSISTENT`/`E_TASK_RECOVERY_INCONSISTENT`; historical
1316
+ claims remain reserved.
1317
+
1318
+ ### `task-resume`
1319
+
1320
+ Reacquires a recovered task's write claims and restores ordinary mutation authority.
1321
+
1322
+ - **Purpose**: Validates active recovery ownership, CAS-settles only an unchanged stale task lease, reuses normal claim-overlap and clean-checkout enforcement under project/task serialization, then removes `recovery.json` transactionally.
1323
+ - **Mutation**: Optionally updates historical claims in `task.json`, appends `TASK_RECOVERY_RESUMED`, and removes `recovery.json` in one transaction.
1324
+ - **Options**:
1325
+
1326
+ <!-- BEGIN FORGELOOP GENERATED: cli:task-resume:options -->
1327
+
1328
+ - `--path <directory>`: target project directory (default: current directory)
1329
+ - `--task <id>`: task ID to operate on (when omitted, resolved from context or single active task)
1330
+ - `--claim <path>`: write claim to reacquire (defaults to all released claims) (repeatable)
1331
+ - `--json`: emit structured output as JSON
1332
+
1333
+ <!-- END FORGELOOP GENERATED: cli:task-resume:options -->
1334
+
1335
+ - **Example**:
1336
+
1337
+ ```bash
1338
+ forgeloop task-resume --task task-001 --claim src --claim tests --json
1339
+ ```
1340
+
1341
+ With no `--claim`, the command attempts to reacquire all claims recorded in the
1342
+ active recovery artifact. It returns `E_TASK_SCOPE_CONFLICT` without removing
1343
+ recovery state when another active task owns an overlapping path.
1344
+ The resume event counts as meaningful task activity. Never create, edit, or
1345
+ delete `recovery.json` manually to emulate this command.
1346
+
1347
+ ### `task-repair-legacy-recovery`
1348
+
1349
+ Migrates one recognized legacy recovery boundary event into the modern durable recovery representation.
1350
+
1351
+ - **Purpose**: Proves that a historical `OPERATOR_RECOVERY_RECORDED` without `recoveryId` marks the effective task boundary and materializes its modern representation as an append-only `LEGACY_RECOVERY_MIGRATION_RECORDED` event plus a transactional `recovery.json`. The original legacy event is never modified.
1352
+ - **Mutation**: Appends the migration event at the ledger tail and writes `recovery.json` in one transaction under project claims locking.
1353
+ - **Options**:
1354
+
1355
+ <!-- BEGIN FORGELOOP GENERATED: cli:task-repair-legacy-recovery:options -->
1356
+
1357
+ - `--path <directory>`: target project directory (default: current directory)
1358
+ - `--task <id>`: task ID to operate on (when omitted, resolved from context or single active task)
1359
+ - `--acknowledge-recovery`: fresh explicit acknowledgement of the legacy boundary migration (required)
1360
+ - `--json`: emit structured output as JSON
1361
+
1362
+ <!-- END FORGELOOP GENERATED: cli:task-repair-legacy-recovery:options -->
1363
+
1364
+ - **Example**:
1365
+
1366
+ ```bash
1367
+ forgeloop task-repair-legacy-recovery --task task-001 --acknowledge-recovery --json
1368
+ ```
1369
+
1370
+ Only the exact known legacy signature is eligible; ambiguous, tampered, or
1371
+ post-boundary-active ledgers fail closed with
1372
+ `E_LEGACY_RECOVERY_MIGRATION_INVALID` and ownership stays INCONSISTENT. A
1373
+ `STALE` task lease is settled only through CAS-safe stale release when the
1374
+ observed lock is unchanged; `LIVE` locks refuse with `E_TASK_LOCKED`, and
1375
+ `UNKNOWN`/`CORRUPT` locks fail closed with the lock preserved — never delete a
1376
+ lock file manually to unblock this command. The repair is idempotent: an
1377
+ already repaired task returns `{repaired: 0, alreadyRepaired: true}` only when
1378
+ the whole canonical recovery relationship validates; any mismatch fails closed.
1379
+ The repair itself never releases claims directly; ownership becomes validated
1380
+ recovery state and ordinary mutation remains blocked until `task-resume`.
@@ -29,6 +29,15 @@ Switching between execution environments (for example Codex $\rightarrow$ Claude
29
29
 
30
30
  When a new harness starts in a repository where an active task exists, it must discover the existing task, reconcile continuity when present, inspect the checkout, and proceed from the recorded state rather than overwriting the contract.
31
31
 
32
+ Before it touches task state, a harness can verify the public compatibility
33
+ handshake it will use for the handoff:
34
+
35
+ <!-- FORGELOOP EXAMPLE: cross-harness:handshake | exit=0 | json.lifecycle.phases.0=RECEIVED -->
36
+ ```bash
37
+ forgeloop protocol-info --json
38
+ ```
39
+ <!-- END FORGELOOP EXAMPLE -->
40
+
32
41
  Key continuity invariants:
33
42
 
34
43
  - **Continuity is optional**: A missing `continuity.json` file does not invalidate an otherwise resumable task.
@@ -45,6 +54,7 @@ Key continuity invariants:
45
54
  | --- | --- | --- | --- |
46
55
  | **Task Descriptor** | `.forgeloop/task-state/<taskKey>/task.json` | Task ID, write claims, and task registration | Descriptor authority |
47
56
  | **Lifecycle Checkpoint** | `.forgeloop/task-state/<taskKey>/work-state.json` | Current phase, cycle, active guides, preflight binding | Canonical lifecycle truth |
57
+ | **Recovery State** | `.forgeloop/task-state/<taskKey>/recovery.json` | Candidate current suspension bound to ledger history | One input to canonical validated claim ownership; never sufficient alone and never completion evidence |
48
58
  | **Operational Continuity** | `.forgeloop/task-state/<taskKey>/continuity.json` | Active focus, remaining items, known issues, inspect-first paths | Operational context only (non-evidence) |
49
59
  | **Implementation Truth** | Git checkout / filesystem | Actual source code and files | Ground truth for changes |
50
60
  | **Task Intent** | `.forgeloop/task-state/<taskKey>/contract.json` | Objectives, constraints, deliverables, verification requirements | Contract authority |
@@ -133,9 +143,11 @@ forgeloop status --json
133
143
 
134
144
  Or with explicit flag:
135
145
 
146
+ <!-- FORGELOOP EXAMPLE: cross-harness:status | fixture=task:auth-feature | exit=0 | json.taskId=auth-feature -->
136
147
  ```bash
137
148
  forgeloop status --task auth-feature --json
138
149
  ```
150
+ <!-- END FORGELOOP EXAMPLE -->
139
151
 
140
152
  Verify that the task exists and observe the current lifecycle phase (e.g. `EXECUTING` or `VERIFYING`). If multiple tasks exist and no selector is provided, ForgeLoop returns `E_TASK_AMBIGUOUS`.
141
153
 
@@ -171,6 +183,26 @@ forgeloop next --task auth-feature --json
171
183
 
172
184
  Follow the deterministic action returned by ForgeLoop (e.g. `CONTINUE_IMPLEMENTATION`, `ENTER_VERIFYING`, or `RECORD_CHECK`).
173
185
 
186
+ If `next` returns `RESUME_RECOVERED_TASK`, the handoff is still the same task,
187
+ but ordinary mutation is suspended. Inspect the recovery metadata and current
188
+ claim owners, then reacquire claims explicitly:
189
+
190
+ ```bash
191
+ forgeloop task-show --task auth-feature --json
192
+ forgeloop task-resume --task auth-feature --json
193
+ ```
194
+
195
+ Do not create, edit, or delete `recovery.json` manually. Every harness must use
196
+ the canonical validated descriptor + work-state + recovery + ledger projection;
197
+ a tombstone alone never releases claims. If another task owns an
198
+ overlapping path, `task-resume` fails with `E_TASK_SCOPE_CONFLICT` and leaves
199
+ the recovery state intact.
200
+
201
+ A project containing active task recovery state requires ForgeLoop 1.4.0 or
202
+ newer and `features.taskClaimRecovery.validatedClaimProjection=true` in
203
+ `protocol-info`. An unaware reader must fail closed rather than continue from
204
+ descriptor-only ownership.
205
+
174
206
  ---
175
207
 
176
208
  ## 6. Stale, Inconsistent, or Missing Handoffs
@@ -186,6 +218,8 @@ ForgeLoop handles edge cases deterministically:
186
218
  | **Multiple Active Tasks** | No `--task` or `FORGELOOP_TASK` supplied | `E_TASK_AMBIGUOUS` | List tasks with `task-list` and supply `--task` |
187
219
  | **Different Task ID** | Contract / state ID mismatch | `TASK_MISMATCH` | Do not merge contexts; complete or clear previous state |
188
220
  | **Malformed State** | Corrupted JSON or invalid hash chain | `INVALID` | Fails closed; inspect errors via `forgeloop doctor --json` |
221
+ | **Recovered Task** | `recovery.json` and its complete ledger history validate as one active recovery cycle | `RESUME_RECOVERED_TASK` | Inspect ownership, then use `task-resume`; recovery is not completion |
222
+ | **Recovery Inconsistency** | Artifact/history, descriptor claims, lock, or ledger evidence is missing, corrupt, or mismatched | `RESOLVE_RECOVERY_INCONSISTENCY` | Historical claims remain reserved; run `validate-protocol` and repair the named artifact instead of forcing recovery |
189
223
 
190
224
  ---
191
225
 
@@ -31,10 +31,24 @@ Reason-code truth -> exported protocol constants (src/core/error-codes.js,
31
31
  Guide registry truth -> canonical guide registry (src/config/guides.json)
32
32
  Package contents truth -> package.json + package tests (tests/package.test.js)
33
33
  Documentation routing -> DOCS_INDEX.md
34
+ Integration API truth -> src/integration.js (exports, envelope, limits, risk classes, resources)
35
+ MCP behavior truth -> integrations/mcp/src/* and integrations/mcp/package.json
36
+ MCP package boundary -> MCP package tests + scripts/mcp-package-smoke.mjs
34
37
  ```
35
38
 
36
39
  Operational documentation must explain canonical behavior, not redefine it.
37
40
 
41
+ Documentation-impact questions for integration/MCP changes:
42
+
43
+ - Did a server mode or capability gate change?
44
+ - Did an MCP transport change?
45
+ - Did an adapter error code change?
46
+ - Did an integration limit or resource list change?
47
+
48
+ Anti-drift invariant: every `documentation-manifest.json` entry marked
49
+ `packaged: true` is mechanically checked against the core npm tarball
50
+ contents (`tests/package.test.js`).
51
+
38
52
  Canonical phase and transition inventories must be derived from `WORK_PHASES`
39
53
  and `WORK_TRANSITIONS`; do not maintain independent hand-written transition
40
54
  enums when a generated or mechanically validated representation is available.
@@ -123,6 +137,21 @@ When writing documentation, use precise terms:
123
137
 
124
138
  Avoid ambiguous phrases like *"should generally"* or *"usually"* for behaviors that are strictly enforced by the validator.
125
139
 
140
+ ### Stable requirement IDs
141
+
142
+ Every protocol-level `MUST` or `MUST NOT` has a stable `FL-<AREA>-<NNN>`
143
+ anchor and an entry in [`protocol-requirements.json`](./protocol-requirements.json).
144
+ Each map entry names its normative source, implementation validator, and at
145
+ least one executable test. `npm run docs:check` rejects an unmapped normative
146
+ requirement, an unused mapping, or a missing implementation/test target.
147
+
148
+ ### Documentation impact classification
149
+
150
+ Classify each documentation-impacting change as one or more of: `NONE`,
151
+ `REFERENCE_ONLY`, `OPERATIONAL`, `NORMATIVE`, `SCHEMA_COMPATIBILITY`,
152
+ `MIGRATION`, or `SECURITY`. Changes that are normative, compatibility,
153
+ migration, or security-sensitive require `npm run docs:check` before merge.
154
+
126
155
  ### Multi-Task Layout Rules
127
156
 
128
157
  - Canonical task-scoped paths are defined in `src/core/task-paths.js` under `.forgeloop/task-state/<taskKey>/`.
@@ -196,3 +225,5 @@ For documentation-impacting changes, verify each item before merging:
196
225
  - [ ] Did package-shipped documentation change?
197
226
  - [ ] Were generated reference docs updated (`npm run docs:generate`)?
198
227
  - [ ] Did documentation conformance CI pass (`npm run docs:check`)?
228
+ - [ ] If normative language changed, are stable IDs and mappings current?
229
+ - [ ] Is the documentation impact classification recorded in the PR?
@@ -39,6 +39,15 @@ Core mental model:
39
39
 
40
40
  In your project repository:
41
41
 
42
+ Confirm the installed CLI exposes the compatible protocol before creating
43
+ state. This is read-only and safe to run in a fresh project directory.
44
+
45
+ <!-- FORGELOOP EXAMPLE: getting-started:compatibility | exit=0 | json.compatibility.schemaVersion=1 -->
46
+ ```bash
47
+ forgeloop protocol-info --json
48
+ ```
49
+ <!-- END FORGELOOP EXAMPLE -->
50
+
42
51
  ```bash
43
52
  # Initialize ForgeLoop kit and discovery shims
44
53
  npx @cassiomc1/forgeloop init
@@ -373,6 +382,7 @@ forgeloop task-migrate --json
373
382
  ## 8. Next Steps
374
383
 
375
384
  - Continue a task across different AI harnesses: [`docs/CROSS_HARNESS_CONTINUITY.md`](./CROSS_HARNESS_CONTINUITY.md)
385
+ - Optional: use ForgeLoop through MCP or the Integration API — [`docs/MCP.md`](./MCP.md) and [`docs/UNIVERSAL_INTEGRATION.md`](./UNIVERSAL_INTEGRATION.md). MCP is not required; CLI and MCP share the same canonical project/task state.
376
386
  - Complete command reference: [`docs/CLI_REFERENCE.md`](./CLI_REFERENCE.md)
377
387
  - Artifact and schema reference: [`docs/ARTIFACT_REFERENCE.md`](./ARTIFACT_REFERENCE.md)
378
388
  - Common symptoms and recovery: [`docs/TROUBLESHOOTING.md`](./TROUBLESHOOTING.md)
package/docs/MCP.md ADDED
@@ -0,0 +1,126 @@
1
+ # ForgeLoop MCP
2
+
3
+ The `@cassiomc1/forgeloop-mcp` package is a **local-first MCP adapter** over
4
+ the canonical ForgeLoop integration API (`@cassiomc1/forgeloop/integration`).
5
+ It is an **adapter, never a second implementation**: every tool call executes
6
+ a canonical ForgeLoop command and every ownership value comes from the
7
+ canonical claim resolver.
8
+
9
+ Two transports ship in one package:
10
+
11
+ - `forgeloop-mcp` — **stdio**, the default/recommended transport;
12
+ - `forgeloop-mcp-http` — **optional** strict-modern MCP 2026 HTTP,
13
+ loopback-only (remote access is unsupported).
14
+
15
+ ## Core principles
16
+
17
+ - **No duplicated protocol logic.** The server contains no lifecycle,
18
+ ownership, recovery, lock, or transaction code.
19
+ - **No direct protocol-state access.** The adapter never reads or writes
20
+ `.forgeloop` files; all mutation flows through ForgeLoop's own guards
21
+ (locks, transactions, revision checks, ledger append serialization).
22
+ - **`COMPLETE` is not enough.** Claim release is presented exactly as the
23
+ canonical resolver reports it; forged COMPLETE stays INCONSISTENT with
24
+ historical claims retained.
25
+ - **Recovery acknowledgement is not authorization.** `acknowledgeRecovery`
26
+ in tool input only satisfies ForgeLoop's caller acknowledgement after the
27
+ server was started with recovery capability.
28
+
29
+ ## Modes
30
+
31
+ | Mode | Read | Loop mutations | task-resume | External | Maintenance | Recovery | Legacy repair | Force |
32
+ | --- | ---: | ---: | ---: | ---: | ---: | ---: | ---: | ---: |
33
+ | `readonly` | yes | no | no | no | no | no | no | no |
34
+ | `safe` (default) | yes | yes | yes | no | no | no | no | no |
35
+ | `full` | yes | yes | yes | opt-in | opt-in | opt-in | opt-in | opt-in |
36
+
37
+ Capability flags (process-scoped, immutable after launch):
38
+ `--allow-external-execution`, `--allow-maintenance`, `--allow-recovery`,
39
+ `--allow-legacy-repair`, `--allow-force-recovery`.
40
+
41
+ ## Resources
42
+
43
+ - `forgeloop://protocol/info`
44
+ - `forgeloop://project/tasks`
45
+ - `forgeloop://task/{taskId}/status`
46
+ - `forgeloop://task/{taskId}/ownership` — canonical validated ownership
47
+ - `forgeloop://task/{taskId}/contract`
48
+ - `forgeloop://task/{taskId}/continuity`
49
+
50
+ Raw recovery artifacts, transaction journals, lock files, and unbounded event
51
+ ledgers are intentionally not exposed.
52
+
53
+ ## Optional stateless HTTP transport
54
+
55
+ `forgeloop-mcp-http` serves the same deterministic catalog over the **strict
56
+ modern** stateless MCP 2026 model (legacy-era traffic rejected):
57
+
58
+ ```bash
59
+ forgeloop-mcp-http --project /repo --mode safe # 127.0.0.1:3333
60
+ ```
61
+
62
+ - **Loopback only**: non-loopback binds fail closed with
63
+ `E_MCP_REMOTE_NOT_SUPPORTED`. Authenticated remote access is not designed
64
+ yet; Host validation is DNS-rebinding defense, not authentication.
65
+ - Strict modern: legacy-era handshakes are rejected, never silently served.
66
+ - Stateless: no session identity is issued, so transport metadata is never
67
+ ForgeLoop authority.
68
+ - Resource bounds: 4 MiB body cap, POST-only, header/request/keepalive
69
+ timeouts, in-flight ceiling (503 `E_MCP_HTTP_BUSY`).
70
+
71
+ ## Version matrix
72
+
73
+ | Component | Current contract |
74
+ | --- | --- |
75
+ | ForgeLoop core package | `1.5.x` repository generation |
76
+ | ForgeLoop protocol | `1` |
77
+ | Integration API | `1` |
78
+ | MCP package | `0.1.x` initial package |
79
+ | MCP protocol target | `2026-07-28` |
80
+
81
+ Repository implementation is separate from npm publication; nothing here
82
+ claims a published release.
83
+
84
+ ## Risk classes and gating examples
85
+
86
+ Invocations are classified at the adapter boundary:
87
+
88
+ ```text
89
+ READ_ONLY · LOOP_MUTATION · CLAIM_REACQUISITION · EXTERNAL_EXECUTION
90
+ MAINTENANCE · CLAIM_RELEASE_RECOVERY · LEGACY_MIGRATION · FORCE_DESTRUCTIVE
91
+ ```
92
+
93
+ Examples: `bundle` → MAINTENANCE (hidden in readonly/safe);
94
+ `task-resume` → CLAIM_REACQUISITION (available in safe — canonical claim
95
+ reacquisition); `task-recover` → CLAIM_RELEASE_RECOVERY (full +
96
+ `--allow-recovery`, plus the canonical acknowledgement); legacy repair →
97
+ separately gated/hidden; force unlock → separately gated. Tool input can
98
+ never elevate launch policy.
99
+
100
+ ## Bounds and timeouts
101
+
102
+ - Structured MCP input is byte-bounded (`E_MCP_INPUT_TOO_LARGE`).
103
+ - External execution cannot exceed the server launch timeout maximum:
104
+ omitted/null timeout receives the maximum; `0`, negative, non-integer, or
105
+ above-maximum values are refused.
106
+ - Tool, capabilities and resource output is bounded using the exact UTF-8
107
+ serialization actually transmitted. Oversized output fails closed with
108
+ `E_MCP_RESULT_TOO_LARGE` and is never silently truncated.
109
+
110
+ ## Adapter errors
111
+
112
+ Adapter-level errors are separate from the generated ForgeLoop core error
113
+ list in [TROUBLESHOOTING](./TROUBLESHOOTING.md):
114
+
115
+ | Code | Meaning |
116
+ | --- | --- |
117
+ | `E_MCP_INPUT_TOO_LARGE` | structured tool input exceeds the byte bound |
118
+ | `E_MCP_RESULT_TOO_LARGE` | serialized output exceeds the output bound |
119
+ | `E_MCP_HTTP_BUSY` | in-flight HTTP ceiling reached (503, Retry-After) |
120
+ | `E_MCP_REMOTE_NOT_SUPPORTED` | non-loopback bind attempted |
121
+ | `E_MCP_EXECUTION_TIMEOUT_INVALID` | timeout not a positive integer |
122
+ | `E_MCP_EXECUTION_TIMEOUT_EXCEEDS_LIMIT` | timeout above server maximum |
123
+ | `E_MCP_FORGELOOP_INTEGRATION_UNSUPPORTED` | installed core integration API mismatch |
124
+
125
+ Canonical ForgeLoop errors (e.g. `E_TASK_SCOPE_CONFLICT`,
126
+ `E_TASK_CLAIM_OWNERSHIP_INCONSISTENT`) are always preserved verbatim.