@cassiomc1/forgeloop 1.10.2 → 1.11.1
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/.cursor/rules/project-loop.mdc +2 -2
- package/.forgeloop/forgeloop.gitignore +1 -0
- package/.github/copilot-instructions.md +2 -2
- package/AGENTS.md +1 -0
- package/AGENT_COMPATIBILITY.md +7 -0
- package/CLAUDE.md +1 -0
- package/DOCS_INDEX.md +10 -0
- package/LOOP_SYSTEM_DESIGN.md +12 -0
- package/ORCHESTRATOR_INTEGRATION.md +9 -0
- package/PROTOCOL_INTEGRATION.md +17 -0
- package/README.md +12 -10
- package/THIRD_PARTY_NOTICES.md +11 -0
- package/THREAT_MODEL.md +21 -0
- package/benchmarks/repository-index/README.md +73 -0
- package/benchmarks/repository-index/queries.json +12 -0
- package/benchmarks/repository-index/run-hot-path.mjs +142 -0
- package/benchmarks/repository-index/run-persistent-transport.mjs +169 -0
- package/completions/_forgeloop +7 -1
- package/completions/forgeloop.bash +13 -1
- package/completions/forgeloop.fish +40 -1
- package/docs/AGENT_PROTOCOL_SUMMARY.md +8 -1
- package/docs/CLI_REFERENCE.md +114 -2
- package/docs/DOCUMENTATION_GUIDE.md +10 -5
- package/docs/GETTING_STARTED.md +17 -0
- package/docs/MCP.md +16 -1
- package/docs/PACKAGE_CONTENTS.md +7 -1
- package/docs/PERSISTENT_SEARCH_TRANSPORT.md +289 -0
- package/docs/RECIPES.md +30 -0
- package/docs/RELEASE_CHECKLIST.md +34 -0
- package/docs/REPOSITORY_INDEX.md +553 -0
- package/docs/TROUBLESHOOTING.md +147 -0
- package/docs/UNIVERSAL_INTEGRATION.md +30 -0
- package/docs/diagrams/README.md +10 -0
- package/package.json +11 -2
- package/scripts/update-tgrep-manifest.mjs +86 -0
- package/scripts/verify-tgrep-manifest.mjs +17 -0
- package/src/cli.js +35 -0
- package/src/commands/doctor.js +76 -1
- package/src/commands/index-rebuild.js +1 -0
- package/src/commands/index-setup.js +1 -0
- package/src/commands/index-start.js +1 -0
- package/src/commands/index-status.js +1 -0
- package/src/commands/index-stop.js +1 -0
- package/src/commands/init.js +39 -1
- package/src/commands/repository-index.js +111 -0
- package/src/commands/search.js +1 -0
- package/src/commands/update.js +32 -4
- package/src/core/cli-command-definitions.js +97 -3
- package/src/core/command-executors.js +35 -2
- package/src/core/command-input.js +23 -0
- package/src/core/error-codes.js +195 -0
- package/src/core/filesystem.js +10 -1
- package/src/core/integration-invocation-policy.js +27 -0
- package/src/core/integration-resources.js +16 -1
- package/src/core/protocol-info.js +23 -0
- package/src/integration.d.ts +90 -0
- package/src/integration.js +21 -0
- package/src/persistent-transport/client.js +293 -0
- package/src/persistent-transport/constants.js +24 -0
- package/src/persistent-transport/errors.js +38 -0
- package/src/persistent-transport/framing.js +61 -0
- package/src/persistent-transport/lifecycle.js +116 -0
- package/src/persistent-transport/ownership.js +184 -0
- package/src/persistent-transport/paths.js +31 -0
- package/src/persistent-transport/protocol.js +95 -0
- package/src/persistent-transport/server.js +256 -0
- package/src/persistent-transport/state.js +49 -0
- package/src/repository-index/args.js +59 -0
- package/src/repository-index/binary-manager.js +413 -0
- package/src/repository-index/constants.js +45 -0
- package/src/repository-index/errors.js +38 -0
- package/src/repository-index/lifecycle.js +17 -0
- package/src/repository-index/lock.js +113 -0
- package/src/repository-index/manifest.js +132 -0
- package/src/repository-index/metrics.js +30 -0
- package/src/repository-index/normalize-json.js +187 -0
- package/src/repository-index/paths.js +39 -0
- package/src/repository-index/platform.js +20 -0
- package/src/repository-index/process.js +140 -0
- package/src/repository-index/readiness.js +62 -0
- package/src/repository-index/search.js +262 -0
- package/src/repository-index/server.js +432 -0
- package/src/repository-index/status.js +397 -0
- package/src/repository-index/tgrep-manifest.json +38 -0
|
@@ -10,6 +10,7 @@ Do not treat ForgeLoop as vendor-specific, optional, or to follow only "in spiri
|
|
|
10
10
|
1. Read [`LOOP_ENGINEERING.md`](../../LOOP_ENGINEERING.md) and [`PROTOCOL_INTEGRATION.md`](../../PROTOCOL_INTEGRATION.md).
|
|
11
11
|
2. Confirm [`PROJECT_PROFILE.md`](../../PROJECT_PROFILE.md) from real sources; initialize if in `template` mode.
|
|
12
12
|
3. Use [`GUIDE_ROUTER.md`](../../GUIDE_ROUTER.md) to activate relevant guides and report IDs.
|
|
13
|
+
3. For repository-wide textual discovery, prefer `forgeloop search` and treat its results as discovery only; do not use them as verification evidence or scope authority.
|
|
13
14
|
4. Respect higher-level and local instructions and the latest request.
|
|
14
15
|
5. Make the minimum change, run specific checks, and run proportional regression checks.
|
|
15
16
|
6. Diagnose failures before fixing them; do not repeat attempts without new evidence.
|
|
@@ -41,5 +42,4 @@ before asking. Do not ask the user to choose among reversible local
|
|
|
41
42
|
product-positioning alternatives; the canonical checklist and boundary remain
|
|
42
43
|
in `LOOP_ENGINEERING.md`.
|
|
43
44
|
|
|
44
|
-
External workflow approval rules do not override ForgeLoop's autonomous-mode
|
|
45
|
-
precedence; consult `LOOP_ENGINEERING.md#external-workflow-compatibility`.
|
|
45
|
+
External workflow approval rules do not override ForgeLoop's autonomous-mode precedence; consult `LOOP_ENGINEERING.md#external-workflow-compatibility`.
|
|
@@ -11,6 +11,7 @@ Use these instructions across the repository while preserving local rules.
|
|
|
11
11
|
- If an official ForgeLoop structured integration is available in your host, prefer it for protocol operations; otherwise use the project-local ForgeLoop CLI. Never simulate ForgeLoop-managed lifecycle, claim, recovery, ledger, or completion state directly.
|
|
12
12
|
- Confirm [`PROJECT_PROFILE.md`](../PROJECT_PROFILE.md) from evidence; initialize if in `template` mode.
|
|
13
13
|
- Select context with [`GUIDE_ROUTER.md`](../GUIDE_ROUTER.md) and report activated guide IDs.
|
|
14
|
+
- For repository-wide textual discovery, prefer `forgeloop search` and treat its results as discovery only; do not use them as verification evidence or scope authority.
|
|
14
15
|
- Respect the latest request, scope, and higher-level instructions.
|
|
15
16
|
- Make the smallest coherent change; validate with specific and regression checks.
|
|
16
17
|
- Diagnose causes before fixing failures; do not make unverified attempts.
|
|
@@ -41,5 +42,4 @@ before asking. Do not ask the user to choose among reversible local
|
|
|
41
42
|
product-positioning alternatives; the canonical checklist and boundary remain
|
|
42
43
|
in `LOOP_ENGINEERING.md`.
|
|
43
44
|
|
|
44
|
-
External workflow approval rules do not override ForgeLoop's autonomous-mode
|
|
45
|
-
precedence; consult `LOOP_ENGINEERING.md#external-workflow-compatibility`.
|
|
45
|
+
External workflow approval rules do not override ForgeLoop's autonomous-mode precedence; consult `LOOP_ENGINEERING.md#external-workflow-compatibility`.
|
package/AGENTS.md
CHANGED
|
@@ -10,6 +10,7 @@ Do not treat ForgeLoop as vendor-specific, optional, or to follow only "in spiri
|
|
|
10
10
|
1. If an official ForgeLoop structured integration is available in your host, prefer it for protocol operations; otherwise use the project-local ForgeLoop CLI. Never simulate ForgeLoop-managed lifecycle, claim, recovery, ledger, or completion state directly.
|
|
11
11
|
1. Inspect [`PROJECT_PROFILE.md`](./PROJECT_PROFILE.md). Confirm facts from sources; initialize if in `template` mode.
|
|
12
12
|
1. Use [`GUIDE_ROUTER.md`](./GUIDE_ROUTER.md) to select relevant guides and report activated guide IDs.
|
|
13
|
+
1. For repository-wide textual discovery, prefer `forgeloop search` and treat its results as discovery only; do not use them as verification evidence or scope authority.
|
|
13
14
|
1. Make the smallest coherent change, run specific checks, and proportional regression checks.
|
|
14
15
|
1. Diagnose failures before fixing; do not repeat attempts without new evidence.
|
|
15
16
|
1. Do not install software, publish, delete, or migrate data without authority. Do not install a missing verification tool merely to satisfy a check. For missing Qwen-MM-Plugins, follow `LOOP_ENGINEERING.md`.
|
package/AGENT_COMPATIBILITY.md
CHANGED
|
@@ -24,3 +24,10 @@ retained as a deprecated compatibility stub:
|
|
|
24
24
|
immutable handoff.
|
|
25
25
|
- Preserve the distinction between `consumerId`, harness labels, and
|
|
26
26
|
authenticated identity; none grants authority or transfers claims.
|
|
27
|
+
- For repository-wide textual discovery, prefer `forgeloop search` and its
|
|
28
|
+
provider-neutral contract. Do not require a host-installed `grep`, `rg`, or
|
|
29
|
+
`tgrep`, and do not silently substitute one when the mandatory index is
|
|
30
|
+
unhealthy.
|
|
31
|
+
- Treat Repository Index results as local discovery context only. They are not
|
|
32
|
+
evidence, completion truth, task ownership, or authority to execute a
|
|
33
|
+
command.
|
package/CLAUDE.md
CHANGED
|
@@ -12,6 +12,7 @@ When working in this repository:
|
|
|
12
12
|
1. If an official ForgeLoop structured integration is available in your host, prefer it for protocol operations; otherwise use the project-local ForgeLoop CLI. Never simulate ForgeLoop-managed lifecycle, claim, recovery, ledger, or completion state directly.
|
|
13
13
|
1. Verify [`PROJECT_PROFILE.md`](./PROJECT_PROFILE.md) against real sources; initialize if in `template` mode.
|
|
14
14
|
1. Consult [`GUIDE_ROUTER.md`](./GUIDE_ROUTER.md), select relevant guides, and announce their IDs.
|
|
15
|
+
1. For repository-wide textual discovery, prefer `forgeloop search` and treat its results as discovery only; do not use them as verification evidence or scope authority.
|
|
15
16
|
1. Make small changes, run specific checks, and run proportional regression checks.
|
|
16
17
|
1. Investigate root causes before fixing failures; do not make unverified attempts.
|
|
17
18
|
1. Request authority for unrelated installations, publications, deletions, or migrations. Do not install a missing verification tool merely to satisfy a check. For missing Qwen-MM-Plugins, follow `LOOP_ENGINEERING.md`.
|
package/DOCS_INDEX.md
CHANGED
|
@@ -18,6 +18,8 @@ integration and guide context. Use this map before editing documentation.
|
|
|
18
18
|
| Cross-harness continuity | [`docs/CROSS_HARNESS_CONTINUITY.md`](./docs/CROSS_HARNESS_CONTINUITY.md) | Operational resume guidance, immutable handoffs, and multi-tool resumption |
|
|
19
19
|
| Agent bootstrap summary | [`docs/AGENT_PROTOCOL_SUMMARY.md`](./docs/AGENT_PROTOCOL_SUMMARY.md) | Generated concise navigation aid for protocol invariants and commands |
|
|
20
20
|
| CLI command reference | [`docs/CLI_REFERENCE.md`](./docs/CLI_REFERENCE.md) | Full syntax, options, and JSON examples for all commands |
|
|
21
|
+
| Repository Index and Search | [`docs/REPOSITORY_INDEX.md`](./docs/REPOSITORY_INDEX.md) | Mandatory managed engine, indexed search contract, lifecycle, resource, security, and benchmark behavior |
|
|
22
|
+
| Persistent CLI search transport | [`docs/PERSISTENT_SEARCH_TRANSPORT.md`](./docs/PERSISTENT_SEARCH_TRANSPORT.md) | CLI-only local IPC, host lifecycle, ownership, recovery, bounds, privacy, and direct API/MCP boundary |
|
|
21
23
|
| Artifact and schema reference | [`docs/ARTIFACT_REFERENCE.md`](./docs/ARTIFACT_REFERENCE.md) | Purpose, mutability, and trust classifications of `.forgeloop/` |
|
|
22
24
|
| Durable actions and trajectory evidence | [`docs/EXECUTION_TRACE.md`](./docs/EXECUTION_TRACE.md) and [`docs/RECIPES.md`](./docs/RECIPES.md) | Action provenance, reconciliation, metrics, and project-local evaluation |
|
|
23
25
|
| Troubleshooting and recovery | [`docs/TROUBLESHOOTING.md`](./docs/TROUBLESHOOTING.md) | Symptom-first recovery and stable error code reference |
|
|
@@ -77,6 +79,7 @@ is historical evidence and is not part of the published core package.
|
|
|
77
79
|
| **External runtime / orchestrator integrator** | [`ORCHESTRATOR_INTEGRATION.md`](./ORCHESTRATOR_INTEGRATION.md) |
|
|
78
80
|
| **Resuming another tool / session** | [`docs/CROSS_HARNESS_CONTINUITY.md`](./docs/CROSS_HARNESS_CONTINUITY.md) |
|
|
79
81
|
| **Looking up CLI commands** | [`docs/CLI_REFERENCE.md`](./docs/CLI_REFERENCE.md) |
|
|
82
|
+
| **Understanding persistent CLI search transport** | [`docs/PERSISTENT_SEARCH_TRANSPORT.md`](./docs/PERSISTENT_SEARCH_TRANSPORT.md) |
|
|
80
83
|
| **Inspecting `.forgeloop/` files** | [`docs/ARTIFACT_REFERENCE.md`](./docs/ARTIFACT_REFERENCE.md) |
|
|
81
84
|
| **Fixing a broken or stale state** | [`docs/TROUBLESHOOTING.md`](./docs/TROUBLESHOOTING.md) |
|
|
82
85
|
| **Looking for quick recipes** | [`docs/RECIPES.md`](./docs/RECIPES.md) |
|
|
@@ -99,6 +102,8 @@ is historical evidence and is not part of the published core package.
|
|
|
99
102
|
- **Inspect real execution PoC and audit evidence**: [`poc/README.md`](./poc/README.md)
|
|
100
103
|
- **Resume after switching tools**: [`docs/CROSS_HARNESS_CONTINUITY.md`](./docs/CROSS_HARNESS_CONTINUITY.md)
|
|
101
104
|
- **Check CLI options and syntax**: [`docs/CLI_REFERENCE.md`](./docs/CLI_REFERENCE.md)
|
|
105
|
+
- **Search the repository through ForgeLoop**: [`docs/REPOSITORY_INDEX.md`](./docs/REPOSITORY_INDEX.md)
|
|
106
|
+
- **Understand persistent CLI search transport**: [`docs/PERSISTENT_SEARCH_TRANSPORT.md`](./docs/PERSISTENT_SEARCH_TRANSPORT.md)
|
|
102
107
|
- **Understand what `.forgeloop/` stores**: [`docs/ARTIFACT_REFERENCE.md`](./docs/ARTIFACT_REFERENCE.md)
|
|
103
108
|
- **Fix a blocked, stale, or invalid state**: [`docs/TROUBLESHOOTING.md`](./docs/TROUBLESHOOTING.md)
|
|
104
109
|
- **Recover a stale task or reacquire released claims**: [`docs/RECIPES.md`](./docs/RECIPES.md#recipe-15--release-and-reacquire-claims-for-an-abandoned-task)
|
|
@@ -130,6 +135,11 @@ process into adapters or README sections; link to the canonical source.
|
|
|
130
135
|
7. Use [`docs/REVISION_PROVIDERS.md`](./docs/REVISION_PROVIDERS.md) and
|
|
131
136
|
[`docs/CODE_ATTESTATION.md`](./docs/CODE_ATTESTATION.md) when verification
|
|
132
137
|
scope, provider boundaries, signing, or revision-range coverage is involved.
|
|
138
|
+
8. Use [`docs/REPOSITORY_INDEX.md`](./docs/REPOSITORY_INDEX.md) for repository
|
|
139
|
+
discovery and [`docs/PERSISTENT_SEARCH_TRANSPORT.md`](./docs/PERSISTENT_SEARCH_TRANSPORT.md)
|
|
140
|
+
for the CLI-only local transport; search output remains operational context
|
|
141
|
+
and never replaces guides, contracts, verification evidence, or completion
|
|
142
|
+
validation.
|
|
133
143
|
|
|
134
144
|
## Verification and release
|
|
135
145
|
|
package/LOOP_SYSTEM_DESIGN.md
CHANGED
|
@@ -173,6 +173,18 @@ API-backed operations remain disabled until their documented credentials or
|
|
|
173
173
|
service endpoints are configured. The kit links to the upstream project but
|
|
174
174
|
does not bundle its source, MCP server, model, or dependencies.
|
|
175
175
|
|
|
176
|
+
### Repository Index
|
|
177
|
+
|
|
178
|
+
The Repository Index is a mandatory, provider-neutral discovery boundary for
|
|
179
|
+
Git repositories. The CLI, Integration API, and MCP adapter share one search
|
|
180
|
+
service backed initially by a ForgeLoop-managed, pinned Microsoft `tgrep`
|
|
181
|
+
1.0.3 executable. The executable is verified before use; the project index
|
|
182
|
+
under `.forgeloop/repository-index/tgrep/` and `engine-state.json` are derived
|
|
183
|
+
cache/state outside the task ledger. Index health can block operational
|
|
184
|
+
readiness, but index contents never become evidence, completion authority,
|
|
185
|
+
task ownership, or historical truth. A host uses `forgeloop search` rather
|
|
186
|
+
than assuming `grep`, `rg`, or `tgrep` is present on PATH.
|
|
187
|
+
|
|
176
188
|
### `GUIDE_ROUTER.md`
|
|
177
189
|
|
|
178
190
|
Canonical map between request or project signals and applicable guides. Each route records:
|
|
@@ -34,6 +34,15 @@ Any non-terminal state → BLOCKED when a genuine blocker is evidenced
|
|
|
34
34
|
- `COMPLETE`
|
|
35
35
|
- `BLOCKED`
|
|
36
36
|
|
|
37
|
+
## Repository Index readiness
|
|
38
|
+
|
|
39
|
+
For Git repositories, the mandatory Repository Index is an operational
|
|
40
|
+
readiness dependency, not a second workflow state machine. An orchestrator
|
|
41
|
+
should surface `index-status` health and route repair through the canonical
|
|
42
|
+
`index-setup`, `index-start`, `index-stop`, and `index-rebuild` commands. An
|
|
43
|
+
index failure must not rewrite historical completion, task claims, receipts,
|
|
44
|
+
or verification evidence; discovery remains non-authoritative cache/state.
|
|
45
|
+
|
|
37
46
|
## Canonical transition table
|
|
38
47
|
|
|
39
48
|
| From | Condition | To |
|
package/PROTOCOL_INTEGRATION.md
CHANGED
|
@@ -191,6 +191,23 @@ from Protocol v1, schema v1, and Integration API v1:
|
|
|
191
191
|
| `canonicalHandoffs` | v2 | Immutable handoff snapshots with ledger-backed exactly-once operational acceptance |
|
|
192
192
|
| `advisoryContextProviders` | v1 | Lazy, opt-in, provider-neutral Integration API injection only |
|
|
193
193
|
|
|
194
|
+
`repositoryIndex` v1 is a mandatory provider-neutral discovery capability for
|
|
195
|
+
Git repositories. ForgeLoop currently implements it with a managed, pinned
|
|
196
|
+
Microsoft `tgrep` 1.0.3 release. `protocol-info --json` and the stable
|
|
197
|
+
Integration API advertise the same `repository/search` operation and
|
|
198
|
+
`repository/index-status` project resource used by the CLI and MCP adapter.
|
|
199
|
+
The index is derived local cache/state: it is never lifecycle evidence,
|
|
200
|
+
completion authority, task ownership, or historical source of truth. Hosts
|
|
201
|
+
must not replace an unhealthy index with a silent `rg`, `grep`, or PATH-based
|
|
202
|
+
fallback, and must not treat search results as proof of verification.
|
|
203
|
+
Normal structured search and status projections are path-safe: match paths are
|
|
204
|
+
repository-relative and machine-local repository, index, state, and binary
|
|
205
|
+
paths are omitted across the CLI, Integration API, and MCP adapter. The first
|
|
206
|
+
query performs strong readiness validation; warm queries use a per-repository
|
|
207
|
+
process-local readiness cache with one bounded recovery retry after a
|
|
208
|
+
server/index failure. Native exit code `1` remains a successful zero-match
|
|
209
|
+
result.
|
|
210
|
+
|
|
194
211
|
`canonicalHandoffs` v2 advertises `supported: true`, immutable snapshots,
|
|
195
212
|
`lifecycleAuthority: false`, `evidenceAuthority: false`,
|
|
196
213
|
`exactlyOnceAcceptance: true`, `acceptanceLedgerBacked: true`, and the
|
package/README.md
CHANGED
|
@@ -13,10 +13,10 @@
|
|
|
13
13
|
[](https://github.com/cassiomc1/forgeloop/actions/workflows/release-notes.yml)
|
|
14
14
|
|
|
15
15
|
ForgeLoop is a portable, vendor-neutral protocol for AI-assisted development
|
|
16
|
-
and developer workflows. It turns
|
|
17
|
-
|
|
18
|
-
continuity, and validator-backed completion.
|
|
19
|
-
|
|
16
|
+
and developer workflows. It turns outcomes into contracts, deterministic routing,
|
|
17
|
+
resumable state, evidence-backed verification, recovery, cross-harness
|
|
18
|
+
continuity, managed repository-wide discovery, and validator-backed completion.
|
|
19
|
+
It is a protocol CLI, not an agent or LLM runtime, framework, or graph orchestrator.
|
|
20
20
|
|
|
21
21
|
The operational sources are indexed in [`DOCS_INDEX.md`](./DOCS_INDEX.md).
|
|
22
22
|
[`LOOP_ENGINEERING.md`](./LOOP_ENGINEERING.md) is the canonical process;
|
|
@@ -94,6 +94,7 @@ Then, inside your project repository:
|
|
|
94
94
|
```bash
|
|
95
95
|
forgeloop init
|
|
96
96
|
forgeloop doctor
|
|
97
|
+
forgeloop search "example"
|
|
97
98
|
```
|
|
98
99
|
|
|
99
100
|
If you prefer not to install globally, use `npx`:
|
|
@@ -427,7 +428,7 @@ The canonical source is the typed Archify workflow
|
|
|
427
428
|
[`docs/diagrams/forgeloop-engineering-flow.workflow.json`](./docs/diagrams/forgeloop-engineering-flow.workflow.json).
|
|
428
429
|
The committed animated interactive explorer is
|
|
429
430
|
[`docs/assets/diagrams/forgeloop-engineering-flow.html`](./docs/assets/diagrams/forgeloop-engineering-flow.html),
|
|
430
|
-
which
|
|
431
|
+
which traces it. The
|
|
431
432
|
animated, self-contained SVG fallback is
|
|
432
433
|
[`docs/assets/diagrams/forgeloop-engineering-flow.svg`](./docs/assets/diagrams/forgeloop-engineering-flow.svg),
|
|
433
434
|
and the deterministic hash receipt is
|
|
@@ -435,20 +436,21 @@ and the deterministic hash receipt is
|
|
|
435
436
|
The governance source is [`docs/diagrams/manifest.json`](./docs/diagrams/manifest.json),
|
|
436
437
|
and the source-bound visual approval is kept in
|
|
437
438
|
[`docs/diagrams/reviews/forgeloop-engineering-flow.review.json`](./docs/diagrams/reviews/forgeloop-engineering-flow.review.json).
|
|
438
|
-
The broader architecture and
|
|
439
|
-
[`LOOP_SYSTEM_DESIGN.md`](./LOOP_SYSTEM_DESIGN.md)
|
|
439
|
+
The broader architecture and the CLI-only search boundary are in
|
|
440
|
+
[`LOOP_SYSTEM_DESIGN.md`](./LOOP_SYSTEM_DESIGN.md) and
|
|
441
|
+
[`PERSISTENT_SEARCH_TRANSPORT.md`](./docs/PERSISTENT_SEARCH_TRANSPORT.md).
|
|
440
442
|
|
|
441
443
|
[Open the animated ForgeLoop evidence-first engineering flow](./docs/assets/diagrams/forgeloop-engineering-flow.html)
|
|
442
444
|
|
|
443
445
|

|
|
444
446
|
|
|
445
|
-
|
|
446
|
-
diagrams. The [Verification Trust Flow source](./docs/diagrams/forgeloop-verification-trust-flow.workflow.json),
|
|
447
|
+
Two focused, source-bound workflow
|
|
448
|
+
diagrams complement it. The [Verification Trust Flow source](./docs/diagrams/forgeloop-verification-trust-flow.workflow.json),
|
|
447
449
|
[animated explorer](./docs/assets/diagrams/forgeloop-verification-trust-flow.html),
|
|
448
450
|
[SVG fallback](./docs/assets/diagrams/forgeloop-verification-trust-flow.svg),
|
|
449
451
|
[receipt](./docs/assets/diagrams/forgeloop-verification-trust-flow.receipt.json),
|
|
450
452
|
and [visual review](./docs/diagrams/reviews/forgeloop-verification-trust-flow.review.json)
|
|
451
|
-
show
|
|
453
|
+
show fail-closed verification. The [Code Attestation Chain
|
|
452
454
|
source](./docs/diagrams/forgeloop-code-attestation-flow.workflow.json),
|
|
453
455
|
[animated explorer](./docs/assets/diagrams/forgeloop-code-attestation-flow.html),
|
|
454
456
|
[SVG fallback](./docs/assets/diagrams/forgeloop-code-attestation-flow.svg),
|
package/THIRD_PARTY_NOTICES.md
CHANGED
|
@@ -213,6 +213,17 @@ or make its prescriptive examples universal.
|
|
|
213
213
|
|
|
214
214
|
## Runtime dependencies with upstream notices
|
|
215
215
|
|
|
216
|
+
### Microsoft tgrep
|
|
217
|
+
|
|
218
|
+
- Project: [microsoft/tgrep](https://github.com/microsoft/tgrep).
|
|
219
|
+
- License declared by the upstream project: MIT.
|
|
220
|
+
- Use in this collection: the initially pinned native engine behind the
|
|
221
|
+
ForgeLoop Repository Index and normalized Repository Search contract.
|
|
222
|
+
- Boundary: ForgeLoop verifies the exact release asset checksum and version
|
|
223
|
+
before managed installation. The executable is provisioned outside the npm
|
|
224
|
+
package; future redistribution must continue to include the MIT notice and
|
|
225
|
+
verify any changed upstream asset.
|
|
226
|
+
|
|
216
227
|
### Model Context Protocol SDK (MCP package only)
|
|
217
228
|
|
|
218
229
|
- Packages: `@modelcontextprotocol/server` and `@modelcontextprotocol/client`
|
package/THREAT_MODEL.md
CHANGED
|
@@ -29,6 +29,27 @@ and an arbitrary external system in one transaction. Idempotency, durable
|
|
|
29
29
|
intent, evidence, and reconciliation reduce duplicate-effect risk but do not
|
|
30
30
|
provide a universal exactly-once guarantee.
|
|
31
31
|
|
|
32
|
+
## Repository Index boundary
|
|
33
|
+
|
|
34
|
+
The Repository Index is a local derived-cache and discovery boundary. Its
|
|
35
|
+
native files are opaque inputs to the managed engine, never protocol evidence
|
|
36
|
+
or completion authority. ForgeLoop owns the engine version, asset identity,
|
|
37
|
+
index location, process lifecycle, and normalized result boundary.
|
|
38
|
+
|
|
39
|
+
| Threat | Mitigation | Residual limitation | Test evidence |
|
|
40
|
+
| --- | --- | --- | --- |
|
|
41
|
+
| Supply-chain asset replacement | Version, archive SHA-256, and executable SHA-256 are pinned; the executable digest is checked before version execution and atomic installation; extracted version is verified | A separately privileged host can replace files after validation | `tests/repository-index-manifest.test.js`, `tests/repository-index-binary-manager.test.js` |
|
|
42
|
+
| Archive path traversal | Archives are inspected for absolute, parent-traversing, and symbolic-link entries and extracted only into a temporary directory | Filesystem privileges can still alter the temporary directory outside the process boundary | `tests/repository-index-binary-manager.test.js` |
|
|
43
|
+
| Malicious repository path or pattern shell injection | Native execution uses direct argument arrays with `shell: false`; request values are bounded and never interpolated into shell text | A hostile native engine remains an external process within the configured resource limits | `tests/repository-index-search.test.js`, `tests/repository-index-platform.test.js` |
|
|
44
|
+
| Stale index | The tgrep watcher, strong first-use checks, process-local per-repository readiness cache, bounded recovery retry, status checks, doctor, explicit rebuild, and live/differential tests keep current working-tree visibility observable | A crash or native-engine defect can require an explicit rebuild | `tests/repository-index-hot-path.test.js`, `tests/repository-index-live.test.js`, `tests/repository-index-differential.test.js` |
|
|
45
|
+
| Incorrect server ownership or PID reuse | Stop/start validates repository root, index path, server metadata, binary, liveness, and command line; it never kills by name or PID alone | A privileged process can rewrite all local metadata consistently | `tests/repository-index-server.test.js` |
|
|
46
|
+
| Persistent host endpoint substitution or PID reuse | The CLI transport stores user-scoped state, verifies the exact host entrypoint and persistent-server marker, binds a scope identity to the handshake nonce, and performs bounded one-attempt recovery | A same-user or separately privileged process can deliberately deny the endpoint or rewrite every local artifact | `tests/persistent-transport.test.js`, `tests/persistent-transport-native.test.js` |
|
|
47
|
+
| Persistent transport frame smuggling or resource exhaustion | Length-prefixed frames have explicit request/response byte limits; protocol version, IDs, methods, parameters, timeouts, and response IDs are validated before search dispatch | The local OS/user boundary still controls process scheduling and filesystem availability | `tests/persistent-transport.test.js` |
|
|
48
|
+
| Persistent host privacy or fallback drift | State excludes repositories, history, matches, and evidence; the host calls canonical `searchRepository()` directly and `rg` is benchmark-only; public projections omit machine-local transport paths | A privileged same-user process can observe local IPC traffic | `docs/REPOSITORY_INDEX.md`, `docs/PERSISTENT_SEARCH_TRANSPORT.md`, `tests/persistent-transport.test.js` |
|
|
49
|
+
| Sensitive match-content leakage | Search stays local; matches are not sent to telemetry or globally logged; metrics contain counts and timings rather than content | A caller can intentionally export its own local result | `tests/repository-index-search.test.js`, `tests/repository-index-integration.test.js` |
|
|
50
|
+
| Machine-local path leakage | Normal CLI, Integration API, and MCP search/status projections omit absolute repository, index, state, and binary paths; matches are normalized relative paths | Explicit diagnostic commands may reveal local paths when requested | `tests/repository-index-privacy.test.js`, `tests/repository-index-hot-path.test.js`, `tests/repository-index-integration.test.js` |
|
|
51
|
+
| Attacker-controlled or corrupt index files | Native index files remain opaque derived cache; corruption fails closed and is repaired by discard/rebuild, never interpreted as trusted truth | Native engine semantics and filesystem corruption remain outside ForgeLoop's parser | `tests/repository-index-migration.test.js`, `tests/repository-index-server.test.js` |
|
|
52
|
+
|
|
32
53
|
### Hardened durable-action threats (T-DURABLE-01 … T-DURABLE-13)
|
|
33
54
|
|
|
34
55
|
| ID | Threat | Mitigation | Test evidence |
|
|
@@ -0,0 +1,73 @@
|
|
|
1
|
+
# Repository Index benchmarks
|
|
2
|
+
|
|
3
|
+
This directory contains reproducible query inputs for the ForgeLoop Repository
|
|
4
|
+
Index. It is an observational benchmark suite, not a release gate based on a
|
|
5
|
+
universal speed target.
|
|
6
|
+
|
|
7
|
+
Measure at least:
|
|
8
|
+
|
|
9
|
+
- first-use setup and search;
|
|
10
|
+
- warm server-backed selective query;
|
|
11
|
+
- warm indexed selective query;
|
|
12
|
+
- query after a file mutation;
|
|
13
|
+
- high-match query;
|
|
14
|
+
- low-match/no-match query.
|
|
15
|
+
|
|
16
|
+
The hot-path runner also performs 100 repeated low-match queries by default
|
|
17
|
+
for raw `tgrep`, the ForgeLoop search service, and `rg`. It reports total,
|
|
18
|
+
mean, median, and p95 milliseconds for each series. Set
|
|
19
|
+
`FORGELOOP_BENCHMARK_ITERATIONS` only when a different bounded sample size is
|
|
20
|
+
needed.
|
|
21
|
+
|
|
22
|
+
For every result, record the repository identifier and commit, platform,
|
|
23
|
+
architecture, Node.js version, pinned tgrep version, query id, mode, duration,
|
|
24
|
+
and observed match count. A result should have this shape:
|
|
25
|
+
|
|
26
|
+
```json
|
|
27
|
+
{
|
|
28
|
+
"schemaVersion": 1,
|
|
29
|
+
"repository": "local-fixture",
|
|
30
|
+
"commit": "working-tree",
|
|
31
|
+
"platform": "darwin",
|
|
32
|
+
"arch": "arm64",
|
|
33
|
+
"nodeVersion": "v26.8.1",
|
|
34
|
+
"tgrepVersion": "1.0.3",
|
|
35
|
+
"query": "selective-literal",
|
|
36
|
+
"mode": "warm",
|
|
37
|
+
"durationMs": 12,
|
|
38
|
+
"matches": 1
|
|
39
|
+
}
|
|
40
|
+
```
|
|
41
|
+
|
|
42
|
+
An optional `rg` comparison is a test oracle only. It is never a runtime
|
|
43
|
+
fallback and must not change the ForgeLoop search contract. Publish measured
|
|
44
|
+
results with the fixture, command, and platform context; do not claim that
|
|
45
|
+
indexed search is always faster or that search metrics imply token, cost, or
|
|
46
|
+
agent-quality improvements.
|
|
47
|
+
|
|
48
|
+
Run the hot-path benchmark against the exact native binary used by CI or the
|
|
49
|
+
host:
|
|
50
|
+
|
|
51
|
+
```bash
|
|
52
|
+
FORGELOOP_TGREP_BINARY=/absolute/path/to/tgrep \
|
|
53
|
+
node benchmarks/repository-index/run-hot-path.mjs
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
The runner reports first-use, warm, post-mutation, and repeated low-match
|
|
57
|
+
timings. It is observational and does not replace correctness, lifecycle, or
|
|
58
|
+
cross-platform CI checks. `rg` is benchmark-only; it is never a ForgeLoop
|
|
59
|
+
runtime fallback.
|
|
60
|
+
|
|
61
|
+
The persistent-host benchmark compares the direct API, the user-scoped
|
|
62
|
+
persistent transport, a fresh CLI process, raw tgrep, and optional `rg` over
|
|
63
|
+
the same low-match workload. It records cold and warm timings, p95 values, and
|
|
64
|
+
the separate Node process-startup component:
|
|
65
|
+
|
|
66
|
+
```bash
|
|
67
|
+
FORGELOOP_TGREP_BINARY=/absolute/path/to/tgrep \
|
|
68
|
+
node benchmarks/repository-index/run-persistent-transport.mjs
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
The benchmark starts an isolated host under a temporary user home and removes
|
|
72
|
+
it after the run. A missing `rg` is reported as unavailable rather than being
|
|
73
|
+
treated as a ForgeLoop failure.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
{
|
|
2
|
+
"schemaVersion": 1,
|
|
3
|
+
"queries": [
|
|
4
|
+
{ "id": "selective-literal", "pattern": "repositoryFingerprint", "fixedStrings": true },
|
|
5
|
+
{ "id": "regex", "pattern": "E_[A-Z_]+", "fixedStrings": false },
|
|
6
|
+
{ "id": "case-insensitive", "pattern": "executionreceipt", "ignoreCase": true },
|
|
7
|
+
{ "id": "word", "pattern": "receipt", "wordRegexp": true },
|
|
8
|
+
{ "id": "javascript", "pattern": "export", "types": ["js"] },
|
|
9
|
+
{ "id": "high-match", "pattern": "const", "fixedStrings": true },
|
|
10
|
+
{ "id": "no-match", "pattern": "FORGELOOP_BENCHMARK_ABSENT", "fixedStrings": true }
|
|
11
|
+
]
|
|
12
|
+
}
|
|
@@ -0,0 +1,142 @@
|
|
|
1
|
+
import { appendFile, cp, mkdtemp, readFile, rm } from "node:fs/promises";
|
|
2
|
+
import { execFile } from "node:child_process";
|
|
3
|
+
import { promisify } from "node:util";
|
|
4
|
+
import os from "node:os";
|
|
5
|
+
import path from "node:path";
|
|
6
|
+
import { fileURLToPath } from "node:url";
|
|
7
|
+
|
|
8
|
+
import { searchRepository } from "../../src/repository-index/search.js";
|
|
9
|
+
import { stopRepositoryIndexServer } from "../../src/repository-index/server.js";
|
|
10
|
+
import { getCanonicalRepositorySearchArgs, appendSearchFilters } from "../../src/repository-index/args.js";
|
|
11
|
+
import { getTgrepIndexPath } from "../../src/repository-index/paths.js";
|
|
12
|
+
import { createTgrepBinaryHandle, runTgrep } from "../../src/repository-index/process.js";
|
|
13
|
+
|
|
14
|
+
const execFileAsync = promisify(execFile);
|
|
15
|
+
const packageRoot = path.resolve(path.dirname(fileURLToPath(import.meta.url)), "../..");
|
|
16
|
+
const fixtureRoot = path.join(packageRoot, "tests", "fixtures", "repository-index", "sample-repo");
|
|
17
|
+
const binary = process.env.FORGELOOP_TGREP_BINARY;
|
|
18
|
+
|
|
19
|
+
if (!binary || !path.isAbsolute(binary)) {
|
|
20
|
+
throw new Error("Set FORGELOOP_TGREP_BINARY to the absolute path of the pinned native tgrep binary");
|
|
21
|
+
}
|
|
22
|
+
const binaryHandle = createTgrepBinaryHandle(binary);
|
|
23
|
+
|
|
24
|
+
const fixture = await mkdtemp(path.join(os.tmpdir(), "forgeloop-repository-index-benchmark-"));
|
|
25
|
+
const queries = JSON.parse(await readFile(path.join(packageRoot, "benchmarks/repository-index/queries.json"), "utf8")).queries;
|
|
26
|
+
const options = { packageRoot, binaryPath: binary, startupTimeoutMs: 30_000, commandTimeoutMs: 60_000 };
|
|
27
|
+
const results = [];
|
|
28
|
+
const repeatedIterations = Number.parseInt(process.env.FORGELOOP_BENCHMARK_ITERATIONS ?? "100", 10);
|
|
29
|
+
if (!Number.isSafeInteger(repeatedIterations) || repeatedIterations < 1 || repeatedIterations > 1_000) {
|
|
30
|
+
throw new Error("FORGELOOP_BENCHMARK_ITERATIONS must be an integer from 1 to 1000");
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
const measure = async (query, mode, { record = true } = {}) => {
|
|
34
|
+
const startedAt = performance.now();
|
|
35
|
+
const result = await searchRepository(fixture, { ...options, ...query });
|
|
36
|
+
const durationMs = performance.now() - startedAt;
|
|
37
|
+
if (record) {
|
|
38
|
+
results.push({
|
|
39
|
+
query: query.id,
|
|
40
|
+
mode,
|
|
41
|
+
durationMs: Math.round(durationMs),
|
|
42
|
+
matches: result.matches.length,
|
|
43
|
+
nativeDurationMs: result.metrics.nativeDurationMs,
|
|
44
|
+
exitCode: result.metrics.exitCode,
|
|
45
|
+
});
|
|
46
|
+
}
|
|
47
|
+
return durationMs;
|
|
48
|
+
};
|
|
49
|
+
|
|
50
|
+
function percentile(values, fraction) {
|
|
51
|
+
const sorted = [...values].sort((left, right) => left - right);
|
|
52
|
+
const index = Math.min(sorted.length - 1, Math.ceil(sorted.length * fraction) - 1);
|
|
53
|
+
return sorted[index];
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
function summarize(durations) {
|
|
57
|
+
return {
|
|
58
|
+
iterations: durations.length,
|
|
59
|
+
totalMs: Math.round(durations.reduce((sum, value) => sum + value, 0)),
|
|
60
|
+
meanMs: Math.round(durations.reduce((sum, value) => sum + value, 0) / durations.length),
|
|
61
|
+
medianMs: Math.round(percentile(durations, 0.5)),
|
|
62
|
+
p95Ms: Math.round(percentile(durations, 0.95)),
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async function measureRawTgrep(query) {
|
|
67
|
+
const args = [...getCanonicalRepositorySearchArgs({
|
|
68
|
+
indexPath: getTgrepIndexPath(fixture),
|
|
69
|
+
pattern: query.pattern,
|
|
70
|
+
options: query,
|
|
71
|
+
})];
|
|
72
|
+
appendSearchFilters(args, query);
|
|
73
|
+
args.push(fixture);
|
|
74
|
+
const startedAt = performance.now();
|
|
75
|
+
const result = await runTgrep({
|
|
76
|
+
binary: binaryHandle,
|
|
77
|
+
repoRoot: fixture,
|
|
78
|
+
args,
|
|
79
|
+
timeoutMs: options.commandTimeoutMs,
|
|
80
|
+
maxOutputBytes: 4 * 1024 * 1024,
|
|
81
|
+
});
|
|
82
|
+
if (![0, 1].includes(result.exitCode)) {
|
|
83
|
+
throw new Error(`raw tgrep benchmark query failed with exit code ${result.exitCode}: ${result.stderr}`);
|
|
84
|
+
}
|
|
85
|
+
return performance.now() - startedAt;
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
async function measureRipgrep(query) {
|
|
89
|
+
const startedAt = performance.now();
|
|
90
|
+
let result;
|
|
91
|
+
try {
|
|
92
|
+
result = await execFileAsync("rg", [
|
|
93
|
+
"--fixed-strings",
|
|
94
|
+
"--color", "never",
|
|
95
|
+
"--glob", "!.forgeloop/repository-index/**",
|
|
96
|
+
query.pattern,
|
|
97
|
+
fixture,
|
|
98
|
+
], { maxBuffer: 4 * 1024 * 1024 });
|
|
99
|
+
} catch (error) {
|
|
100
|
+
if (error.code !== 1) throw error;
|
|
101
|
+
}
|
|
102
|
+
void result;
|
|
103
|
+
return performance.now() - startedAt;
|
|
104
|
+
}
|
|
105
|
+
|
|
106
|
+
async function measureRepeated(label, callback) {
|
|
107
|
+
const durations = [];
|
|
108
|
+
for (let index = 0; index < repeatedIterations; index += 1) durations.push(await callback());
|
|
109
|
+
return { label, ...summarize(durations) };
|
|
110
|
+
}
|
|
111
|
+
|
|
112
|
+
try {
|
|
113
|
+
await cp(fixtureRoot, fixture, { recursive: true });
|
|
114
|
+
await execFileAsync("git", ["init", "--quiet", fixture]);
|
|
115
|
+
await execFileAsync("git", ["-C", fixture, "add", "."]);
|
|
116
|
+
await measure({ id: "first-use", pattern: "alphaNeedle", fixedStrings: true }, "first-use");
|
|
117
|
+
for (const query of queries) await measure(query, "warm");
|
|
118
|
+
await appendFile(path.join(fixture, "src", "alpha.js"), "\nexport const benchmarkMutationNeedle = true;\n");
|
|
119
|
+
await measure({ id: "post-mutation", pattern: "benchmarkMutationNeedle", fixedStrings: true }, "post-mutation");
|
|
120
|
+
const lowMatchQuery = { id: "repeated-low-match", pattern: "FORGELOOP_BENCHMARK_ABSENT", fixedStrings: true };
|
|
121
|
+
const repeated = {
|
|
122
|
+
query: lowMatchQuery,
|
|
123
|
+
iterations: repeatedIterations,
|
|
124
|
+
rawTgrep: await measureRepeated("raw-tgrep", () => measureRawTgrep(lowMatchQuery)),
|
|
125
|
+
forgeloopSearch: await measureRepeated("forgeloop-search", () => measure(lowMatchQuery, "warm-repeated", { record: false })),
|
|
126
|
+
ripgrep: await measureRepeated("ripgrep", () => measureRipgrep(lowMatchQuery)),
|
|
127
|
+
};
|
|
128
|
+
console.log(JSON.stringify({
|
|
129
|
+
schemaVersion: 1,
|
|
130
|
+
repository: "repository-index-sample-fixture",
|
|
131
|
+
commit: "working-tree",
|
|
132
|
+
platform: process.platform,
|
|
133
|
+
arch: process.arch,
|
|
134
|
+
nodeVersion: process.version,
|
|
135
|
+
tgrepVersion: "1.0.3",
|
|
136
|
+
measurements: results,
|
|
137
|
+
repeatedLowMatch: repeated,
|
|
138
|
+
}, null, 2));
|
|
139
|
+
} finally {
|
|
140
|
+
await stopRepositoryIndexServer(fixture, options).catch(() => {});
|
|
141
|
+
await rm(fixture, { recursive: true, force: true });
|
|
142
|
+
}
|