@cassiomc1/forgeloop 2.0.0-rc.2 → 2.0.0-rc.5

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.
package/DOCS_INDEX.md CHANGED
@@ -36,6 +36,7 @@ integration and guide context. Use this map before editing documentation.
36
36
  | Platform adapters | [`docs/PLATFORM_ADAPTERS.md`](./docs/PLATFORM_ADAPTERS.md) | Generic CI boundary and platform mapping guidance |
37
37
  | Universal integration API | [`docs/UNIVERSAL_INTEGRATION.md`](./docs/UNIVERSAL_INTEGRATION.md) | Programmatic integration subpath, envelope semantics, and consumer map |
38
38
  | Audit UX read model | [`docs/AUDIT_UX.md`](./docs/AUDIT_UX.md) | Bounded read-only task timeline, health, verification, ownership, and completion projection |
39
+ | ForgeLoopAudit workspace | [`docs/FORGELOOP_AUDIT.md`](./docs/FORGELOOP_AUDIT.md) | In-repository Audit setup, project inspection, view walkthrough, screenshots and verification links |
39
40
  | Advisory context providers | [`docs/ADVISORY_CONTEXT.md`](./docs/ADVISORY_CONTEXT.md) | Optional external host context, non-evidence trust boundary, allowlist normalization, and safety rules |
40
41
  | Provider extension architecture | [`docs/PROVIDER_ARCHITECTURE.md`](./docs/PROVIDER_ARCHITECTURE.md) | Provider-neutral architecture, invocation lifecycle, strict snapshots, cancellation, trust, and authority boundaries |
41
42
  | Provider extension reference | [`docs/PROVIDERS.md`](./docs/PROVIDERS.md) | Capability discovery, provider kinds, common contract, limits, errors, and maintainer checklist |
package/README.md CHANGED
@@ -11,9 +11,9 @@
11
11
  [![Package smoke](https://github.com/cassiomc1/forgeloop/actions/workflows/package-smoke.yml/badge.svg?branch=main)](https://github.com/cassiomc1/forgeloop/actions/workflows/package-smoke.yml)
12
12
  [![Release notes](https://github.com/cassiomc1/forgeloop/actions/workflows/release-notes.yml/badge.svg?branch=main)](https://github.com/cassiomc1/forgeloop/actions/workflows/release-notes.yml)
13
13
 
14
- ForgeLoop is the deterministic governor for AI-assisted engineering. Jev provides bounded System One semantic input; the host coding model provides System Two implementation. ForgeLoop owns lifecycle, claims, gates, evidence, completion, recovery, and publication truth.
14
+ ForgeLoop governs AI-assisted engineering. Jev supplies bounded semantic input; the host model implements changes. ForgeLoop owns lifecycle, claims, gates, evidence, completion, recovery, and publication truth.
15
15
 
16
- Operational sources: [`DOCS_INDEX.md`](./DOCS_INDEX.md); [`LOOP_ENGINEERING.md`](./LOOP_ENGINEERING.md) is canonical; [`PROTOCOL_INTEGRATION.md`](./PROTOCOL_INTEGRATION.md) defines discovery; [`PROJECT_PROFILE.md`](./PROJECT_PROFILE.md) stores project facts; [`GUIDE_ROUTER.md`](./GUIDE_ROUTER.md) selects guides.
16
+ Sources: [`DOCS_INDEX.md`](./DOCS_INDEX.md); canonical [`LOOP_ENGINEERING.md`](./LOOP_ENGINEERING.md); discovery [`PROTOCOL_INTEGRATION.md`](./PROTOCOL_INTEGRATION.md); project facts [`PROJECT_PROFILE.md`](./PROJECT_PROFILE.md); guides [`GUIDE_ROUTER.md`](./GUIDE_ROUTER.md).
17
17
 
18
18
  ## Where should I start?
19
19
 
@@ -33,7 +33,6 @@ Operational sources: [`DOCS_INDEX.md`](./DOCS_INDEX.md); [`LOOP_ENGINEERING.md`]
33
33
  - **Troubleshooting & error codes** → [`docs/TROUBLESHOOTING.md`](./docs/TROUBLESHOOTING.md)
34
34
  - **Code attestation & revision coverage** → [`docs/CODE_ATTESTATION.md`](./docs/CODE_ATTESTATION.md)
35
35
  - **System architecture & safety** → [`LOOP_SYSTEM_DESIGN.md`](./LOOP_SYSTEM_DESIGN.md) & [`THREAT_MODEL.md`](./THREAT_MODEL.md)
36
- - **Documentation index & ownership** → [`DOCS_INDEX.md`](./DOCS_INDEX.md)
37
36
 
38
37
  ## Real execution proof
39
38
 
@@ -78,7 +77,8 @@ not establish project identity. Public project evidence remains v1.
78
77
  ## Distribution and license
79
78
 
80
79
  GitHub hosts source and issues. The core npm package is publicly available;
81
- the MCP companion retains restricted distribution.
80
+ the MCP companion retains restricted distribution. RC5 targets `next`; `latest`
81
+ remains at RC4. Stable SQLite acceptance is incomplete.
82
82
  ForgeLoop-owned source and documentation are licensed under the
83
83
  [PolyForm Noncommercial License 1.0.0](./LICENSE). Commercial use is not granted
84
84
  by this license. Third-party notices are documented in
@@ -88,7 +88,7 @@ by this license. Third-party notices are documented in
88
88
 
89
89
  ### Installation
90
90
 
91
- After your organization grants access, configure npm authentication and install:
91
+ Install the public core package from npm; no organization access is required:
92
92
 
93
93
  ```bash
94
94
  npm install --global @cassiomc1/forgeloop
@@ -203,7 +203,7 @@ forgeloop protocol-info --json
203
203
  | Validate completion | Yes | Provides real work and evidence |
204
204
  | Schedule agents or LLM inference | No | External to the protocol |
205
205
 
206
- From the privately delivered package, initialize a target project with:
206
+ Initialize a target project using the public npm package:
207
207
 
208
208
  ```bash
209
209
  npx @cassiomc1/forgeloop init
@@ -246,7 +246,7 @@ host-reported or `UNKNOWN`; values are never estimated.
246
246
  capability, but advisory recall remains Integration API only. The stock CLI
247
247
  does not auto-recall providers and does not expose a `context-recall` command.
248
248
 
249
- Validate the source checkout offline before any private release:
249
+ Validate the source checkout before preparing a release:
250
250
 
251
251
  ```bash
252
252
  node src/cli.js init
@@ -495,6 +495,12 @@ CI audits each supplied task receipt explicitly. With no receipt it reports
495
495
  completion or attestation. For contributor checks and safe payload retention,
496
496
  see [CONTRIBUTING.md](./CONTRIBUTING.md#focused-verification-and-maintenance).
497
497
 
498
+ ## ForgeLoopAudit workspace
499
+
500
+ The read-only auditor lives in `ForgeLoopAudit/`. See
501
+ [setup, usage and screenshots](./docs/FORGELOOP_AUDIT.md) for the demo,
502
+ project inspection and audit views.
503
+
498
504
  ## Protocol compatibility
499
505
 
500
506
  The npm package version is independent of protocol version. The current
@@ -615,17 +621,15 @@ docs/diagrams/ typed Archify diagram source and inventory
615
621
  docs/assets/diagrams/ committed HTML, SVG, and generation receipt
616
622
  scripts/ checks, renderer, release identity, CI notes
617
623
  tests/ Node and Python regression coverage
618
- .forgeloop/ project-scoped ForgeLoop configuration
624
+ .forgeloop/ project-scoped configuration
619
625
  .forgeloop/task-state/ isolated live task protocol state
620
626
  DOCS_INDEX.md documentation map and ownership boundaries
621
627
  ```
622
628
 
623
- Project-scoped configuration remains under `.forgeloop/`.
624
629
  Task-scoped mutable protocol state is stored under
625
630
  `.forgeloop/task-state/<taskKey>/`.
626
631
 
627
- For document ownership, guide routing, capability degradation, and integration
628
- details, start at [`DOCS_INDEX.md`](./DOCS_INDEX.md).
632
+ For documentation ownership and integrations, see [`DOCS_INDEX.md`](./DOCS_INDEX.md).
629
633
 
630
634
  ## Programmatic integration and MCP
631
635
 
@@ -635,8 +639,7 @@ ForgeLoop exposes a stable programmatic surface:
635
639
  import { executeForgeLoopCommand } from "@cassiomc1/forgeloop/integration";
636
640
  ```
637
641
 
638
- The local MCP server (`@cassiomc1/forgeloop-mcp`, stdio) is an adapter over
639
- this exact API — it never reimplements ForgeLoop. See
642
+ The local MCP server (`@cassiomc1/forgeloop-mcp`, stdio) wraps this API. See
640
643
  [`docs/UNIVERSAL_INTEGRATION.md`](./docs/UNIVERSAL_INTEGRATION.md) and
641
644
  [`docs/MCP.md`](./docs/MCP.md). MCP is optional; the CLI and instruction-only
642
645
  hosts remain fully supported without it.
@@ -7,7 +7,7 @@
7
7
  ForgeLoop is a portable protocol and support CLI for verifiable engineering workflows. It records and validates task state, contracts, routing, checks, evidence, continuity, and optional code attestations. It does not become an agent scheduler, delegation service, source-control authority, or secret manager.
8
8
 
9
9
  Protocol version: 1
10
- Package version: 2.0.0-rc.2
10
+ Package version: 2.0.0-rc.5
11
11
 
12
12
  ## Canonical loop
13
13
 
@@ -48,6 +48,21 @@ behavioral certification for every external harness. Existing native
48
48
  `AGENTS.md`, `CLAUDE.md`, Cursor, and Copilot integration surfaces remain
49
49
  intact.
50
50
 
51
+ ## ForgeLoopAudit workspace guidance
52
+
53
+ The generated Skill documents the Audit component at `ForgeLoopAudit/` inside
54
+ the ForgeLoop repository, replacing the standalone repository and
55
+ `../ForgeLoopAudit`. It directs agents to
56
+ resolve the target project root before running commands: core ForgeLoop uses
57
+ the parent root; Audit uses its own directory, instructions, package scripts,
58
+ and configured protocol runtime. ForgeLoop is the source and contribution home
59
+ for both components; their package commands, task state, and receipt provenance
60
+ remain scoped to the target root. Audit remains a read-only auditor.
61
+
62
+ Maintain this guidance in `scripts/generate-forgeloop-skill.mjs`, then run
63
+ `npm run skill:generate` and `npm run skill:check`. An independently installed
64
+ Skill must be updated separately; generation only writes the repository copy.
65
+
51
66
  ## Safety Boundaries
52
67
 
53
68
  Agents must discover tasks first, follow `next`, respect claims, require
@@ -0,0 +1,71 @@
1
+ # ForgeLoopAudit — setup and usage
2
+
3
+ ForgeLoopAudit is the local, read-only web interface for inspecting ForgeLoop
4
+ projects. It lives in [`ForgeLoopAudit/`](https://github.com/cassiomc1/forgeloop/blob/main/ForgeLoopAudit/README.md) in this
5
+ repository; the standalone repository is retired. Use it to inspect task
6
+ progress, findings, verification evidence, quality, history and reports.
7
+ ForgeLoop remains the authority for lifecycle and completion decisions.
8
+
9
+ ## Start ForgeLoopAudit
10
+
11
+ Use Node.js **24.19.0 or newer**. From a checkout of this repository:
12
+
13
+ ```bash
14
+ cd ForgeLoopAudit
15
+ npm ci
16
+ npm run build:web
17
+ node dist/server/cli.mjs --demo
18
+ ```
19
+
20
+ The CLI starts a loopback-only server and opens the browser. If it does not
21
+ open automatically, use the one-time bootstrap URL printed in the terminal.
22
+ Keep the terminal running while using Audit; press `Ctrl+C` to stop the server.
23
+
24
+ To inspect your own ForgeLoop project, replace the demo command with an
25
+ explicit project path, still from `ForgeLoopAudit/`:
26
+
27
+ ```bash
28
+ node dist/server/cli.mjs --project /absolute/path/to/your-project
29
+ ```
30
+
31
+ To inspect the parent ForgeLoop checkout itself, use `--project ..`.
32
+ Audit reads native SQLite task state through ForgeLoop's public Integration API.
33
+ Legacy JSON projects open as `ARTIFACT_ONLY`; Audit does not migrate them or
34
+ change task state. Project selection stays with the local server rather than
35
+ uploading a repository through the browser.
36
+
37
+ ## Explore the audit views
38
+
39
+ 1. Open **Audit Summary** for integrity, completion readiness, quality and trust
40
+ verdicts. Use **Run audit** to refresh the read-only assessment.
41
+ 2. Use **Projects** and **Task Board** to navigate registered projects and tasks
42
+ by canonical lifecycle phase; open **Tasks** for a task's detailed audit.
43
+ 3. Review **Findings** and **Evidence** to trace warnings and verification gaps.
44
+ Coverage, attestation and completion are separate signals.
45
+ 4. Inspect **Quality**, **Policy & Trust**, **Audit History** and **Diagnostics**
46
+ for supporting context. Use **Reports** for exports to managed local storage.
47
+ **Repository Search** is discovery only, not verification evidence.
48
+
49
+ ## Screenshots
50
+
51
+ These are existing captures of the ForgeShop demo, taken from the production
52
+ renderer at 1440 × 900 in the dark theme, aligned to Audit `0.3.0-rc.3` and
53
+ ForgeLoop `2.0.0-rc.4`. The demo deliberately includes invalid, stale, blocked and
54
+ unverified scenarios. Its warnings and coverage values do not establish
55
+ completion or production readiness for your project.
56
+
57
+ | Audit Summary | Task Board |
58
+ | --- | --- |
59
+ | ![ForgeShop demo audit summary with separate integrity, readiness, quality and trust verdicts](https://raw.githubusercontent.com/cassiomc1/forgeloop/main/ForgeLoopAudit/screen/audit-summary.png) | ![Read-only task board grouping demo tasks by canonical lifecycle phase](https://raw.githubusercontent.com/cassiomc1/forgeloop/main/ForgeLoopAudit/screen/kanban.png) |
60
+ | Findings | Evidence |
61
+ | ![ForgeShop demo findings with provenance and severity](https://raw.githubusercontent.com/cassiomc1/forgeloop/main/ForgeLoopAudit/screen/findings.png) | ![ForgeShop demo task evidence and verification scope](https://raw.githubusercontent.com/cassiomc1/forgeloop/main/ForgeLoopAudit/screen/evidence.png) |
62
+
63
+ See the [complete screenshot gallery](https://github.com/cassiomc1/forgeloop/blob/main/ForgeLoopAudit/README.md#screenshots)
64
+ and [capture provenance](https://github.com/cassiomc1/forgeloop/blob/main/ForgeLoopAudit/screen/README.md) for the full set.
65
+ For development, verification and command-root details, see the
66
+ [Audit README](https://github.com/cassiomc1/forgeloop/blob/main/ForgeLoopAudit/README.md),
67
+ [Audit documentation index](https://github.com/cassiomc1/forgeloop/blob/main/ForgeLoopAudit/docs/README.md) and
68
+ [workspace guidance](./AGENT_SKILL.md#forgeloopaudit-workspace-guidance).
69
+ Audit has its own dependencies, build and verification commands. Its application
70
+ source and screenshots are excluded from the core npm package; npm users can
71
+ [read this section and its images on GitHub](https://github.com/cassiomc1/forgeloop#forgeloopaudit-workspace).
@@ -117,6 +117,12 @@ and numeric limits.
117
117
 
118
118
  ### Global installation
119
119
 
120
+ The public core package is available without organization access. As of
121
+ 2026-10-14, both npm `latest` and `next` resolve to `2.0.0-rc.4`. The default
122
+ installation below uses `latest`; use `@cassiomc1/forgeloop@2.0.0-rc.4` when an
123
+ exact reproducible version is required. RC4 remains a release candidate;
124
+ its passing new-user checks do not establish stable SQLite acceptance.
125
+
120
126
  Install the ForgeLoop CLI globally to make the `forgeloop` command available
121
127
  in your terminal:
122
128
 
@@ -235,7 +235,7 @@ are added; `providerExtensions.publicRegistryApi` and
235
235
  - Protocol v1, schema v1, Integration API v1 unchanged.
236
236
  - `advisoryContextProviders` v1 and `providerExtensions` v1 unchanged; the
237
237
  `ADVISORY_CONTEXT` provider kind covers this adapter.
238
- - Requires Node.js 20+.
238
+ - The ForgeLoop host requires Node.js 24.19.0+ with built-in `node:sqlite`.
239
239
  - Upstream behaviors referenced: `opensrc path <source> [--cwd]`,
240
240
  `OPENSRC_HOME` override, fetch-on-miss, `opensrc 0.7.3` observed at
241
241
  authoring time (never hardcoded into behavior).
@@ -1,6 +1,6 @@
1
1
  # Core npm package contents
2
2
 
3
- This page describes the intentional boundary of the privately delivered
3
+ This page describes the intentional boundary of the publicly distributed
4
4
  `@cassiomc1/forgeloop` package. The [`files`](../package.json) list in
5
5
  `package.json` is the packaging source of truth, while the repository's
6
6
  `tests/package.test.js` and clean-room `scripts/package_smoke.mjs` checks keep
@@ -13,7 +13,9 @@ The package exposes the `forgeloop` executable from `src/cli.js` and the
13
13
  `@cassiomc1/forgeloop/integration` subpath from `src/integration.js`, with its
14
14
  declaration file. The package has the approved exact `@typesafe-ai/sdk`
15
15
  dependency for the pinned semantic decision plane and `smol-toml` for bounded
16
- Cargo manifest parsing. It requires Node.js 20 or newer.
16
+ Cargo manifest parsing. It requires Node.js 24.19.0 or newer with built-in
17
+ `node:sqlite`, matching `package.json#engines`. The MCP companion retains
18
+ restricted distribution.
17
19
 
18
20
  ## Included files
19
21
 
@@ -90,6 +92,9 @@ The published tarball includes the following consumer-facing groups:
90
92
 
91
93
  The tarball intentionally omits repository-only material:
92
94
 
95
+ - the `ForgeLoopAudit/` application source, dependencies, builds, and application
96
+ package; maintaining it inside the repository does not add it to the core
97
+ package's `files` allowlist;
93
98
  - tests, conformance fixtures, coverage output, and secret-scanning helpers;
94
99
  Harness-specific Agent Skill installation directories and Skill caches are
95
100
  not packaged.
@@ -4,11 +4,61 @@ This is the current release checklist for the PolyForm Noncommercial-licensed
4
4
  `@cassiomc1/forgeloop` package. It is a preparation and verification checklist;
5
5
  it does not authorize publication.
6
6
 
7
- ## Bounded 2.0.0-rc.2 publication
7
+ ## Current published candidate — 2026-10-14
8
+
9
+ The public core package is `2.0.0-rc.4`; npm `latest` and `next` both resolve
10
+ to RC4. It requires Node.js 24.19.0+ with built-in `node:sqlite`. The MCP
11
+ companion retains restricted distribution. RC4 is a release candidate;
12
+ `soleSQLiteWriter` remains false and stable/production acceptance is open.
13
+
14
+ Observed release evidence:
15
+
16
+ - [RC4 verification and publication](https://github.com/cassiomc1/forgeloop/actions/runs/38629809865)
17
+ passed full release checks and published with provenance.
18
+ - [Exact-version npm audit](https://github.com/cassiomc1/forgeloop/actions/runs/38638055749)
19
+ passed 15/15 checks on each native Windows, macOS and Linux host.
20
+ - [Default latest-install audit](https://github.com/cassiomc1/forgeloop/actions/runs/38669237414)
21
+ installed the unversioned public package with a fresh npm cache,
22
+ configuration, install prefix and project; all 15 checks passed on each OS.
23
+ This run uses the test-harness branch `codex/latest-user-audit-20261014`;
24
+ its `latest` input is not present in the main-branch workflow.
25
+
26
+ The published npm `gitHead` and immutable `v2.0.0-rc.4` tag identify
27
+ `d05c17cddda6a09581d526732b08eb0789b9734f`. The RC3 tag is retained; its
28
+ publication was cancelled before npm publication. RC4 carries those runtime
29
+ fixes plus the Windows archive-repair test correction.
30
+
31
+ Verify the live registry before relying on a channel:
32
+
33
+ ```bash
34
+ npm view @cassiomc1/forgeloop version dist-tags --json
35
+ npm view @cassiomc1/forgeloop@2.0.0-rc.4 gitHead dist.integrity --json
36
+ ```
37
+
38
+ ## Runtime corrections after RC2
39
+
40
+ RC3 and later candidates with runtime changes require fresh full repository
41
+ verification and the clean-user audit on Windows, macOS and Linux. The bounded
42
+ source-equivalence shortcut below applies only to RC1 and RC2. Stable SQLite
43
+ acceptance remains separate; a passing new-user audit does not change
44
+ `soleSQLiteWriter` or authorize a stable 2.x release. Candidates publish to
45
+ `next` under the existing release-channel policy.
46
+
47
+ The `Published npm new-user SQLite audit` workflow supports `candidate` and
48
+ `npm` package sources. Before a PR, test the packed candidate in an isolated
49
+ installation, home directory and project on each platform. After publication,
50
+ dispatch the same workflow with source `npm` and the exact new version. Require
51
+ every check to pass, including live Jev health, concurrent task creation,
52
+ SQLite integrity and a completed-task backup restored to a fresh directory.
53
+ Supply `FORGELOOP_NPM_AUDIT_JEV_KEY` through GitHub Actions secrets and remove
54
+ temporary credentials after the runs finish.
55
+
56
+ ## Historical bounded 2.0.0-rc.2 publication
8
57
 
9
58
  The owner approved short verification and an RC fallback on 2026-10-13. This
10
- candidate is published only to npm `next`; stable `latest` remains blocked while
11
- `soleSQLiteWriter` is false. A release candidate does not assert completed
59
+ candidate is published to npm `next`. The owner subsequently authorized moving
60
+ `latest` to the existing RC2 through a guarded tag-only promotion. Publishing a
61
+ new stable version remains blocked while `soleSQLiteWriter` is false. A release candidate does not assert completed
12
62
  migration acceptance or production readiness.
13
63
 
14
64
  After npm rejected restricted RC1 publication with `E402`, the owner authorized
@@ -50,9 +100,35 @@ confirmation `PUBLISH PUBLIC PACKAGE`, then verify npm version, Git commit,
50
100
  tarball integrity, and provenance. Stable publishing retains full checks and
51
101
  additionally refuses incomplete sole-writer acceptance.
52
102
 
103
+ ## Owner-authorized default-channel promotion
104
+
105
+ Publishing a new release candidate still uses `next`. Moving an already
106
+ published candidate to `latest` is a separate owner-authorized tag operation;
107
+ it neither republishes the package nor changes the immutable Git tag.
108
+ The owner authorized RC4 after its cross-platform npm audits passed.
109
+
110
+ For that explicitly authorized RC4 operation, authenticated npm CLI access
111
+ was used:
112
+
113
+ ```bash
114
+ npm dist-tag add @cassiomc1/forgeloop@2.0.0-rc.4 latest
115
+ npm view @cassiomc1/forgeloop dist-tags --json
116
+ ```
117
+
118
+ npm may require separate browser/security-key verification for login and
119
+ for the tag mutation. Credentials belong in npm's authentication flow,
120
+ never documentation, Git or task artifacts. Confirm the registry result
121
+ before reporting success. The version remains an RC; promotion does not
122
+ establish stable SQLite acceptance or production readiness.
123
+
124
+ The older `promote-latest` operation in `npm-publish.yml` and
125
+ `scripts/promote-rc-latest.mjs` is hard-bound to RC2. It is historical tooling,
126
+ not an RC4 promotion path; do not dispatch `POINT LATEST TO RC2` for the
127
+ current release. Its exact-identity and tag guards are retained.
128
+
53
129
  ## ForgeLoop 2.0.0 candidate scope
54
130
 
55
- The 2.0.0 candidate is not release-ready while SQLite migration plan §10 has
131
+ A stable 2.0.0 release is not ready while SQLite migration plan §10 has
56
132
  open consumer-closure, current-source performance, or platform evidence. Keep
57
133
  all acceptance boxes open until their evidence is bound to the exact candidate
58
134
  revision.
@@ -1,5 +1,38 @@
1
1
  # ForgeLoop Troubleshooting Guide
2
2
 
3
+ ## Windows Repository Index ZIP extraction
4
+
5
+ When `init` reports `E_REPOSITORY_INDEX_ENGINE_EXTRACTION_FAILED` and
6
+ `tar: This does not look like a tar archive`, a GNU tar from Git may be
7
+ shadowing Windows' ZIP-capable system tool. RC3 selects
8
+ `%SystemRoot%\System32\tar.exe` explicitly for inspection and extraction.
9
+ Archive and binary checksum verification and unsafe-entry rejection still
10
+ apply. A missing system tool remains an explicit extraction failure; ForgeLoop
11
+ does not install replacement software automatically.
12
+
13
+ ## Restoring completed tasks into another directory
14
+
15
+ Use an attachment-inclusive `storage-backup` and `storage-restore` with
16
+ externally stopped writers. Historical execution records keep their original
17
+ project root and exact bytes. RC3 accepts them only when they exactly match
18
+ the snapshot bound to the destination's restore journal and storage marker.
19
+ Keep `.forgeloop/storage-restores/<operation-id>/snapshot` and its journal;
20
+ they are retained provenance evidence, not a disposable search cache.
21
+ An altered record, missing snapshot or broken binding remains rejected.
22
+
23
+ Storage restore does not install project code or instruction adapters. Restore
24
+ the project files separately, then run `forgeloop init` in the destination to
25
+ install the kit before expecting the complete `doctor` report to be healthy.
26
+
27
+ ## Concurrent task creation
28
+
29
+ Task creation revalidates write claims atomically. If the catalog changes while
30
+ a command is prepared, RC4 refreshes authoritative claim inputs with bounded
31
+ backoff and jitter without replaying the command or its external effects.
32
+ Overlapping claims still fail. Sustained contention can exhaust the retry
33
+ bound and return a catalog-change refusal; inspect fresh task discovery
34
+ before deciding whether to retry.
35
+
3
36
  This guide provides symptom-first recovery procedures for common ForgeLoop protocol, state, and verification issues.
4
37
 
5
38
  ---
@@ -100,7 +133,8 @@ tests or change assertions. Local coverage remains an explicit command because
100
133
  instrumentation adds measurable overhead; the PR unit lane wraps that same
101
134
  `test:ci` process across four deterministic shards, then aggregates coverage
102
135
  without running the suite again. Docs-only changes run the quick suite only on
103
- the first Node 24 shard; the other shards and the Node 20 lane do not install.
136
+ the first Node 24 shard; the other shards do not install. The minimum Node
137
+ 24.19.0 compatibility lane runs only for applicable source or runtime changes.
104
138
 
105
139
  #### Native Windows guidance
106
140
 
@@ -1405,6 +1405,17 @@
1405
1405
  "self-hosted-node-validation"
1406
1406
  ],
1407
1407
  "packaged": true
1408
+ },
1409
+ {
1410
+ "path": "docs/FORGELOOP_AUDIT.md",
1411
+ "id": "forgeloop-audit",
1412
+ "class": "operational",
1413
+ "audience": [
1414
+ "user",
1415
+ "maintainer"
1416
+ ],
1417
+ "canonicalFor": [],
1418
+ "packaged": true
1408
1419
  }
1409
1420
  ]
1410
1421
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@cassiomc1/forgeloop",
3
- "version": "2.0.0-rc.2",
3
+ "version": "2.0.0-rc.5",
4
4
  "description": "Evidence-first engineering protocol for AI coding environments and developer workflows",
5
5
  "repository": {
6
6
  "type": "git",
@@ -82,6 +82,7 @@
82
82
  "docs/MCP.md",
83
83
  "docs/UNIVERSAL_INTEGRATION.md",
84
84
  "docs/AUDIT_UX.md",
85
+ "docs/FORGELOOP_AUDIT.md",
85
86
  "docs/DIAGNOSTIC_MODEL.md",
86
87
  "docs/EXECUTION_TRACE.md",
87
88
  "docs/CODE_ATTESTATION.md",
@@ -193,7 +194,7 @@
193
194
  },
194
195
  "dependencies": {
195
196
  "@typesafe-ai/sdk": "0.6.0",
196
- "smol-toml": "1.8.0"
197
+ "smol-toml": "1.9.0"
197
198
  },
198
199
  "exports": {
199
200
  ".": "./src/cli.js",
@@ -42,6 +42,12 @@ Runtime registries and validators outrank canonical protocol docs; canonical doc
42
42
 
43
43
  In ForgeLoop's own source repository, use \`node src/cli.js\`. In a consumer repository, use that repository's configured local invocation. Never silently install or upgrade ForgeLoop, and never use a stale global CLI when repository-local code exists. If compatibility is uncertain, inspect \`protocol-info --json\`.
44
44
 
45
+ ## ForgeLoopAudit Workspace
46
+
47
+ ForgeLoopAudit is maintained at \`ForgeLoopAudit/\` inside the ForgeLoop repository, replacing the standalone repository and sibling path \`../ForgeLoopAudit\`. ForgeLoop is its source and contribution home. This source layout is not a prerequisite for consumer projects or npm installations. Audit remains a read-only auditor with its own package, documentation, build, and verification commands; it is not included in the core ForgeLoop npm package.
48
+
49
+ Resolve the target root before running commands. For core work, stay at the ForgeLoop root and use \`node src/cli.js\`. For Audit work, enter \`ForgeLoopAudit/\`, read its own project instructions and \`README.md\`, and resolve its configured ForgeLoop runtime there. Audit development uses \`npm run dev:web\`; its full verification uses \`npm run verify:full\`, both from the Audit root. Do not apply the parent's CLI path blindly in the child directory or merge their task state. A directory move does not rebind receipts, workspace bindings, or stored project paths; inspect affected artifacts through supported tooling and never rewrite protocol state to repair a moved root.
50
+
45
51
  ## Operating Workflow
46
52
 
47
53
  1. Discover existing work with \`node src/cli.js task-list --json\`.
@@ -27,6 +27,12 @@ Runtime registries and validators outrank canonical protocol docs; canonical doc
27
27
 
28
28
  In ForgeLoop's own source repository, use `node src/cli.js`. In a consumer repository, use that repository's configured local invocation. Never silently install or upgrade ForgeLoop, and never use a stale global CLI when repository-local code exists. If compatibility is uncertain, inspect `protocol-info --json`.
29
29
 
30
+ ## ForgeLoopAudit Workspace
31
+
32
+ ForgeLoopAudit is maintained at `ForgeLoopAudit/` inside the ForgeLoop repository, replacing the standalone repository and sibling path `../ForgeLoopAudit`. ForgeLoop is its source and contribution home. This source layout is not a prerequisite for consumer projects or npm installations. Audit remains a read-only auditor with its own package, documentation, build, and verification commands; it is not included in the core ForgeLoop npm package.
33
+
34
+ Resolve the target root before running commands. For core work, stay at the ForgeLoop root and use `node src/cli.js`. For Audit work, enter `ForgeLoopAudit/`, read its own project instructions and `README.md`, and resolve its configured ForgeLoop runtime there. Audit development uses `npm run dev:web`; its full verification uses `npm run verify:full`, both from the Audit root. Do not apply the parent's CLI path blindly in the child directory or merge their task state. A directory move does not rebind receipts, workspace bindings, or stored project paths; inspect affected artifacts through supported tooling and never rewrite protocol state to repair a moved root.
35
+
30
36
  ## Operating Workflow
31
37
 
32
38
  1. Discover existing work with `node src/cli.js task-list --json`.
@@ -124,6 +124,19 @@ function commandProvenanceError(message = "Observed command evidence requires a
124
124
  return artifactError("E_COMMAND_PROVENANCE_UNATTESTED", message, [ARTIFACT_PATHS.receipt]);
125
125
  }
126
126
 
127
+ async function validateExecutionProjectRoot({ target, taskId, check, execution, protocolProjectRoot, allowForeignCwd, executionArtifacts }) {
128
+ if (allowForeignCwd || path.resolve(protocolProjectRoot) === path.resolve(target)) return;
129
+ const { isRetainedRestoreExecution } = await import("../storage/restore-execution-provenance.js");
130
+ const restored = !executionArtifacts && await isRetainedRestoreExecution(target, execution, { taskId });
131
+ if (!restored) {
132
+ throw artifactError(
133
+ "E_EXECUTION_REF_INVALID",
134
+ "Execution artifact protocol project root does not match the current target",
135
+ [taskId ? taskExecutionPath(taskId, check.executionRef) : ARTIFACT_PATHS.executionDirectory],
136
+ );
137
+ }
138
+ }
139
+
127
140
  /**
128
141
  * Revalidate the ForgeLoop-owned execution artifact behind an observed command
129
142
  * check. This is intentionally asynchronous so completion and audit can verify
@@ -194,13 +207,7 @@ export async function validateCheckExecutionProvenance(check, {
194
207
  [taskId ? taskExecutionPath(taskId, check.executionRef) : ARTIFACT_PATHS.executionDirectory],
195
208
  );
196
209
  }
197
- if (!allowForeignCwd && path.resolve(protocolProjectRoot) !== path.resolve(target)) {
198
- throw artifactError(
199
- "E_EXECUTION_REF_INVALID",
200
- "Execution artifact protocol project root does not match the current target",
201
- [taskId ? taskExecutionPath(taskId, check.executionRef) : ARTIFACT_PATHS.executionDirectory],
202
- );
203
- }
210
+ await validateExecutionProjectRoot({ target, taskId, check, execution, protocolProjectRoot, allowForeignCwd, executionArtifacts });
204
211
  if (check.status === "passed" && (execution.status !== "passed" || execution.exitCode !== 0)) {
205
212
  throw artifactError(
206
213
  "E_EXECUTION_REF_INVALID",
@@ -181,8 +181,14 @@ export function validateArchiveEntry(entry) {
181
181
  return normalized;
182
182
  }
183
183
 
184
+ export function resolveArchiveTool(archive, { platform = process.platform, env = process.env } = {}) {
185
+ // Windows ships a ZIP-capable bsdtar. Git's GNU tar can shadow it on PATH.
186
+ if (platform === "win32") return path.win32.join(env.SystemRoot || "C:\\Windows", "System32", "tar.exe");
187
+ return archive === "zip" ? "unzip" : "tar";
188
+ }
189
+
184
190
  async function listArchiveEntries(archivePath, { archive = "tar.gz", execFileImpl = nodeExecFile } = {}) {
185
- const listCommand = archive === "zip" && process.platform !== "win32" ? "unzip" : "tar";
191
+ const listCommand = resolveArchiveTool(archive);
186
192
  const archiveName = path.basename(archivePath);
187
193
  const listArgs = listCommand === "unzip" ? ["-Z1", archiveName] : ["-tf", archiveName];
188
194
  try {
@@ -195,7 +201,7 @@ async function listArchiveEntries(archivePath, { archive = "tar.gz", execFileImp
195
201
 
196
202
  async function extractArchive(archivePath, extractionDirectory, { archive = "tar.gz", execFileImpl = nodeExecFile } = {}) {
197
203
  await listArchiveEntries(archivePath, { archive, execFileImpl });
198
- const extractCommand = archive === "zip" && process.platform !== "win32" ? "unzip" : "tar";
204
+ const extractCommand = resolveArchiveTool(archive);
199
205
  const destination = path.relative(path.dirname(archivePath), extractionDirectory).replaceAll("\\", "/") || ".";
200
206
  const extractArgs = extractCommand === "unzip"
201
207
  ? ["-q", path.basename(archivePath), "-d", destination]
@@ -26,7 +26,7 @@ function artifactSchema(row) {
26
26
  }
27
27
 
28
28
  /** Validate imported payloads with their existing protocol schemas and rules. */
29
- export async function validateMigrationDatabase(db, { target, packageRoot }) {
29
+ export async function validateMigrationDatabase(db, { target, packageRoot, archivedExecutions = false }) {
30
30
  const schemas = new Map();
31
31
  const validate = async (text, name, label, taskId = null) => {
32
32
  if (!name) throw Object.assign(new Error(`Unmapped operational artifact: ${label}`), { code: "E_STORAGE_MIGRATION_UNMAPPED_SOURCE" });
@@ -55,13 +55,13 @@ export async function validateMigrationDatabase(db, { target, packageRoot }) {
55
55
  validator?.(value);
56
56
  }
57
57
  }
58
- return withOperationalStore({ db, target }, async () => {
58
+ return withOperationalStore({ db, target, readOnly: true }, async () => {
59
59
  const tasks = [];
60
60
  for (const row of db.prepare("SELECT task_id FROM tasks ORDER BY task_id").iterate()) {
61
61
  const state = await readWorkState(target, { taskId: row.task_id, packageRoot });
62
62
  for (const check of state?.checks ?? []) {
63
63
  if (check.provenance === "FORGELOOP_EXECUTED") {
64
- await validateCheckExecutionProvenance(check, { target, packageRoot, taskId: row.task_id });
64
+ await validateCheckExecutionProvenance(check, { target, packageRoot, taskId: row.task_id, allowForeignCwd: archivedExecutions });
65
65
  }
66
66
  }
67
67
  const ledger = await validateEventLedger(target, packageRoot, { taskId: row.task_id });
@@ -77,7 +77,7 @@ export async function resumeProjectReplacementPreparation(target, { operationId,
77
77
  if (intent.phase === "PREPARING") {
78
78
  await ensurePreparedRestoreSnapshot(target, current.root, intent, { journalFilename: "replacement-intent.json" });
79
79
  await withVerifiedProjectStorageBackup(await assertSafePath(current.root, "snapshot"), async retained => {
80
- await validateMigrationDatabase(retained.db, { target: retained.root, packageRoot });
80
+ await validateMigrationDatabase(retained.db, { target: retained.root, packageRoot, archivedExecutions: true });
81
81
  await withVerifiedProjectStorageBackup(intent.source, source => {
82
82
  if (canonicalFingerprint(source.manifest) !== intent.sourceFingerprint
83
83
  || canonicalFingerprint(logicalSnapshot(source.db)) !== canonicalFingerprint(logicalSnapshot(retained.db))) throw invalid("Replacement recovery snapshot differs from recorded source");
@@ -99,7 +99,7 @@ export async function resumeProjectReplacementPreparation(target, { operationId,
99
99
  export async function replaceActiveProjectStorage(source, target, { writersQuiesced = false, packageRoot = getPackageRoot() } = {}) {
100
100
  if (writersQuiesced !== true) throw Object.assign(new Error("Exclude writers and close their database connections before active replacement"), { code: "E_STORAGE_MIGRATION_QUIESCENCE_REQUIRED" });
101
101
  return withVerifiedProjectStorageBackup(source, async ({ db, root, manifest }) => {
102
- await validateMigrationDatabase(db, { target: root, packageRoot });
102
+ await validateMigrationDatabase(db, { target: root, packageRoot, archivedExecutions: true });
103
103
  const relative = path.relative(path.resolve(target), root).split(path.sep).join("/");
104
104
  if (REPLACEMENT_ACTIVE_ROOTS.some(name => relative === name || relative.startsWith(`${name}/`))) throw invalid("Replacement source cannot be archived outgoing operational state");
105
105
  return withStorageMaintenance(target, async () => {
@@ -119,7 +119,7 @@ export async function replaceActiveProjectStorage(source, target, { writersQuies
119
119
  const snapshot = await assertSafePath(operationRoot, "snapshot");
120
120
  await backupProjectStorage(db, root, snapshot);
121
121
  await withVerifiedProjectStorageBackup(snapshot, async retained => {
122
- await validateMigrationDatabase(retained.db, { target: retained.root, packageRoot });
122
+ await validateMigrationDatabase(retained.db, { target: retained.root, packageRoot, archivedExecutions: true });
123
123
  await withVerifiedProjectStorageBackup(root, current => {
124
124
  if (canonicalFingerprint(current.manifest) !== intent.sourceFingerprint
125
125
  || canonicalFingerprint(logicalSnapshot(current.db)) !== canonicalFingerprint(logicalSnapshot(retained.db))) throw invalid("Incoming source changed during replacement preparation");
@@ -92,7 +92,7 @@ export async function restoreProjectStorageToFreshProject(source, target, { writ
92
92
  if (canonicalFingerprint(current.manifest) !== canonicalFingerprint(sourceManifest)
93
93
  || canonicalFingerprint(logicalSnapshot(current.db)) !== canonicalFingerprint(logicalSnapshot(snapshot))) throw invalid("Source backup changed during restore preparation");
94
94
  });
95
- await validateMigrationDatabase(snapshot, { target: retained, packageRoot });
95
+ await validateMigrationDatabase(snapshot, { target: retained, packageRoot, archivedExecutions: true });
96
96
  const binding = { operationId, databaseSchemaVersion: readStorageMeta(snapshot).schema_version,
97
97
  sourceInventoryFingerprint: canonicalFingerprint({ database: manifest.databaseSha256, inventory: manifest.inventorySha256, references: manifest.references }) };
98
98
  Object.assign(journal, { phase: "READY", binding, snapshotFingerprint: canonicalFingerprint(manifest) });
@@ -111,7 +111,7 @@ async function publishReadyRestore(target, operationRoot, journal, { packageRoot
111
111
  const persist = () => writeFileAtomic(journalPath, `${JSON.stringify(journal, null, 2)}\n`);
112
112
  const { operationId, binding } = journal;
113
113
  return withVerifiedProjectStorageBackup(retained, async ({ db: snapshot, manifest }) => {
114
- await validateMigrationDatabase(snapshot, { target: retained, packageRoot });
114
+ await validateMigrationDatabase(snapshot, { target: retained, packageRoot, archivedExecutions: true });
115
115
  await assertOwnedStorageMaintenance(target);
116
116
  await verifyProjectRestoreJournal(target, operationId, { expectedOwnerId: await assertOwnedStorageMaintenance(target) });
117
117
  await assertRestorePublicationLayout(target, snapshot);
@@ -186,7 +186,7 @@ async function validatePreparedRestore(target, root, journal, packageRoot) {
186
186
  await withVerifiedProjectStorageBackup(journal.source, async source => {
187
187
  if (canonicalFingerprint(source.manifest) !== journal.sourceFingerprint
188
188
  || canonicalFingerprint(logicalSnapshot(source.db)) !== canonicalFingerprint(logicalSnapshot(snapshot))) throw invalid("Prepared restore differs from its recorded source backup");
189
- await validateMigrationDatabase(snapshot, { target: snapshotPath, packageRoot });
189
+ await validateMigrationDatabase(snapshot, { target: snapshotPath, packageRoot, archivedExecutions: true });
190
190
  });
191
191
  await assertOwnedStorageMaintenance(target);
192
192
  Object.assign(journal, { phase: "READY", snapshotFingerprint: canonicalFingerprint(manifest), binding: {
@@ -0,0 +1,26 @@
1
+ import { canonicalFingerprint } from "../core/artifacts.js";
2
+ import { readStorageVersionMarker } from "./storage-marker.js";
3
+ import { verifyProjectRestoreJournal } from "./restore-journal.js";
4
+ import { withVerifiedProjectStorageBackup } from "./project-backup.js";
5
+ import { operationalContext } from "./operational-context.js";
6
+
7
+ /** Historical roots remain valid only for exact records in the bound snapshot. */
8
+ export async function isRetainedRestoreExecution(target, execution, { taskId } = {}) {
9
+ // Replacement journals also audit incoming archives. Those read-only audits
10
+ // must not select their snapshot as the caller's live operational project.
11
+ return operationalContext.run(null, () => verifyRetainedExecution(target, execution, taskId));
12
+ }
13
+
14
+ async function verifyRetainedExecution(target, execution, taskId) {
15
+ const marker = await readStorageVersionMarker(target);
16
+ if (!marker?.operationId) return false;
17
+ let retained;
18
+ try { retained = await verifyProjectRestoreJournal(target, marker.operationId); }
19
+ catch (error) { if (error.code === "ENOENT") return false; throw error; }
20
+ if (!retained.snapshotVerified || !["PUBLISHING", "ACTIVE"].includes(retained.journal.phase)) return false;
21
+ return withVerifiedProjectStorageBackup(retained.snapshot, ({ db }) => {
22
+ const row = db.prepare("SELECT payload_json FROM executions WHERE task_id = ? AND execution_id = ?")
23
+ .get(taskId ?? execution.taskId, execution.executionId);
24
+ return Boolean(row && canonicalFingerprint(JSON.parse(row.payload_json)) === canonicalFingerprint(execution));
25
+ });
26
+ }
@@ -22,7 +22,7 @@ const belongs = (name, root) => name === root || name.startsWith(`${root}/`);
22
22
  async function incomingSnapshot(target, operationId, packageRoot) {
23
23
  const root = await assertSafePath(target, `.forgeloop/storage-restores/${operationId}/snapshot`);
24
24
  return withVerifiedProjectStorageBackup(root, async ({ db, manifest }) => {
25
- await validateMigrationDatabase(db, { target: root, packageRoot });
25
+ await validateMigrationDatabase(db, { target: root, packageRoot, archivedExecutions: true });
26
26
  return { root, fingerprint: canonicalFingerprint(manifest) };
27
27
  });
28
28
  }
@@ -80,7 +80,7 @@ export async function prepareActiveStorageReplacement(target, operationId, { wri
80
80
  const backup = await assertSafePath(root, "backup");
81
81
  await backupProjectStorage(db, target, backup);
82
82
  await withVerifiedProjectStorageBackup(backup, async retained => {
83
- await validateMigrationDatabase(retained.db, { target: retained.root, packageRoot });
83
+ await validateMigrationDatabase(retained.db, { target: retained.root, packageRoot, archivedExecutions: true });
84
84
  const logicalFingerprint = canonicalFingerprint(logicalSnapshot(retained.db));
85
85
  if (logicalFingerprint !== canonicalFingerprint(logicalSnapshot(db))) throw invalid("Outgoing state changed while its retained backup was prepared");
86
86
  Object.assign(manifest, { backupFingerprint: canonicalFingerprint(retained.manifest), logicalFingerprint });
@@ -1,7 +1,8 @@
1
1
  import { publishAttachmentFile, verifyAttachmentFile } from "./attachment-files.js";
2
2
  import { insertVerifiedAttachmentReference, validateAttachmentReference } from "./attachment-references.js";
3
3
  import path from "node:path";
4
- import { randomUUID, createHash } from "node:crypto";
4
+ import { randomUUID, createHash, randomInt } from "node:crypto";
5
+ import { setTimeout as delay } from "node:timers/promises";
5
6
  import { operationalContext, getOperationalStore } from "./operational-context.js";
6
7
  import { canonicalFingerprint } from "../core/artifacts.js";
7
8
  import { taskStorageKey } from "../core/task-identity.js";
@@ -547,7 +548,10 @@ async function commitPreparedStore(store, packageRoot) {
547
548
  for (let refresh = 0; ; refresh += 1) {
548
549
  try { store.commit(); return; }
549
550
  catch (error) {
550
- if (error.code !== "E_STORAGE_CATALOG_CHANGED" || refresh >= 3) throw error;
551
+ if (error.code !== "E_STORAGE_CATALOG_CHANGED" || refresh >= 16) throw error;
552
+ // Spread simultaneous creators before refreshing their authoritative
553
+ // claim inputs. The prepared command and external effects are not replayed.
554
+ await delay(Math.min(5 * (2 ** refresh), 100) + randomInt(0, 20));
551
555
  store.reads.delete("task-catalog");
552
556
  const { discoverTasks } = await import("../core/task-discovery.js");
553
557
  const { assertNoScopeConflicts } = await import("../core/task-scope.js");