@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.
Files changed (84) hide show
  1. package/.cursor/rules/project-loop.mdc +2 -2
  2. package/.forgeloop/forgeloop.gitignore +1 -0
  3. package/.github/copilot-instructions.md +2 -2
  4. package/AGENTS.md +1 -0
  5. package/AGENT_COMPATIBILITY.md +7 -0
  6. package/CLAUDE.md +1 -0
  7. package/DOCS_INDEX.md +10 -0
  8. package/LOOP_SYSTEM_DESIGN.md +12 -0
  9. package/ORCHESTRATOR_INTEGRATION.md +9 -0
  10. package/PROTOCOL_INTEGRATION.md +17 -0
  11. package/README.md +12 -10
  12. package/THIRD_PARTY_NOTICES.md +11 -0
  13. package/THREAT_MODEL.md +21 -0
  14. package/benchmarks/repository-index/README.md +73 -0
  15. package/benchmarks/repository-index/queries.json +12 -0
  16. package/benchmarks/repository-index/run-hot-path.mjs +142 -0
  17. package/benchmarks/repository-index/run-persistent-transport.mjs +169 -0
  18. package/completions/_forgeloop +7 -1
  19. package/completions/forgeloop.bash +13 -1
  20. package/completions/forgeloop.fish +40 -1
  21. package/docs/AGENT_PROTOCOL_SUMMARY.md +8 -1
  22. package/docs/CLI_REFERENCE.md +114 -2
  23. package/docs/DOCUMENTATION_GUIDE.md +10 -5
  24. package/docs/GETTING_STARTED.md +17 -0
  25. package/docs/MCP.md +16 -1
  26. package/docs/PACKAGE_CONTENTS.md +7 -1
  27. package/docs/PERSISTENT_SEARCH_TRANSPORT.md +289 -0
  28. package/docs/RECIPES.md +30 -0
  29. package/docs/RELEASE_CHECKLIST.md +34 -0
  30. package/docs/REPOSITORY_INDEX.md +553 -0
  31. package/docs/TROUBLESHOOTING.md +147 -0
  32. package/docs/UNIVERSAL_INTEGRATION.md +30 -0
  33. package/docs/diagrams/README.md +10 -0
  34. package/package.json +11 -2
  35. package/scripts/update-tgrep-manifest.mjs +86 -0
  36. package/scripts/verify-tgrep-manifest.mjs +17 -0
  37. package/src/cli.js +35 -0
  38. package/src/commands/doctor.js +76 -1
  39. package/src/commands/index-rebuild.js +1 -0
  40. package/src/commands/index-setup.js +1 -0
  41. package/src/commands/index-start.js +1 -0
  42. package/src/commands/index-status.js +1 -0
  43. package/src/commands/index-stop.js +1 -0
  44. package/src/commands/init.js +39 -1
  45. package/src/commands/repository-index.js +111 -0
  46. package/src/commands/search.js +1 -0
  47. package/src/commands/update.js +32 -4
  48. package/src/core/cli-command-definitions.js +97 -3
  49. package/src/core/command-executors.js +35 -2
  50. package/src/core/command-input.js +23 -0
  51. package/src/core/error-codes.js +195 -0
  52. package/src/core/filesystem.js +10 -1
  53. package/src/core/integration-invocation-policy.js +27 -0
  54. package/src/core/integration-resources.js +16 -1
  55. package/src/core/protocol-info.js +23 -0
  56. package/src/integration.d.ts +90 -0
  57. package/src/integration.js +21 -0
  58. package/src/persistent-transport/client.js +293 -0
  59. package/src/persistent-transport/constants.js +24 -0
  60. package/src/persistent-transport/errors.js +38 -0
  61. package/src/persistent-transport/framing.js +61 -0
  62. package/src/persistent-transport/lifecycle.js +116 -0
  63. package/src/persistent-transport/ownership.js +184 -0
  64. package/src/persistent-transport/paths.js +31 -0
  65. package/src/persistent-transport/protocol.js +95 -0
  66. package/src/persistent-transport/server.js +256 -0
  67. package/src/persistent-transport/state.js +49 -0
  68. package/src/repository-index/args.js +59 -0
  69. package/src/repository-index/binary-manager.js +413 -0
  70. package/src/repository-index/constants.js +45 -0
  71. package/src/repository-index/errors.js +38 -0
  72. package/src/repository-index/lifecycle.js +17 -0
  73. package/src/repository-index/lock.js +113 -0
  74. package/src/repository-index/manifest.js +132 -0
  75. package/src/repository-index/metrics.js +30 -0
  76. package/src/repository-index/normalize-json.js +187 -0
  77. package/src/repository-index/paths.js +39 -0
  78. package/src/repository-index/platform.js +20 -0
  79. package/src/repository-index/process.js +140 -0
  80. package/src/repository-index/readiness.js +62 -0
  81. package/src/repository-index/search.js +262 -0
  82. package/src/repository-index/server.js +432 -0
  83. package/src/repository-index/status.js +397 -0
  84. 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`.
@@ -1,3 +1,4 @@
1
1
  # Local resumable task state is untrusted, target-specific data.
2
2
  work-state.json
3
3
  executions/
4
+ repository-index/
@@ -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`.
@@ -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
 
@@ -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 |
@@ -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
  [![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)
14
14
 
15
15
  ForgeLoop is a portable, vendor-neutral protocol for AI-assisted development
16
- and developer workflows. It turns an outcome into a contract, deterministic
17
- routing, resumable state, evidence-backed verification, recovery, cross-harness
18
- continuity, and validator-backed completion. It is a protocol/support CLI, not
19
- an agent or LLM runtime, not an agent framework, and not a graph orchestrator.
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 opens in the dark presentation stage and traces the workflow. The
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 boundaries are in
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
  ![ForgeLoop evidence-first engineering flow (animated SVG fallback)](./docs/assets/diagrams/forgeloop-engineering-flow.svg)
444
446
 
445
- The architecture flow is complemented by two focused, source-bound workflow
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 why narrow verification is fail-closed. The [Code Attestation Chain
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),
@@ -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
+ }