diff --git a/AGENTS.md b/AGENTS.md
index 23b0a800..c5f52909 100644
--- a/AGENTS.md
+++ b/AGENTS.md
@@ -9,6 +9,14 @@ and pluggable.
This is the canonical operating manual for any AI agent working in this repo. `CLAUDE.md`
imports it. Read §0 before editing anything.
+### Internal subagent delegation
+
+When parallel delegation is appropriate for ChatGPT/Codex tasks, use exactly four bounded
+subagents inside the current task and chat. Keep delegation at one level: workers return bounded
+results and the parent performs the sole integration. Subagents must never be sent to Orca, Orca
+orchestration, or separate user-visible threads. Before finalizing, verify that all four workers
+returned and that no forbidden routing or descendant delegation occurred.
+
---
## 0. Read this first — two architectures live in one package
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 5a9d5f45..934a999b 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -184,12 +184,26 @@ All notable changes to Engraphis are documented here. Format loosely follows
- Folder imports report truncation explicitly: a folder with more matching files than the
ceiling now warns and returns `truncated`/`matched_total`/`unreadable` fields instead of
silently importing an alphabetically-first slice that looks complete.
+- The `engraphis_prime_agent` integration now ships a fleet wrapper that boots multiple
+ sub-agents (researcher / coder / reviewer / writer) with one shared memory workspace,
+ with fleet-wide configuration via `ENGRAPHIS_REPO` and per-agent override via the
+ `repo=` argument; the `engraphis-prime-agent install` subcommand configures a target
+ prime-agent configuration file and `python -m engraphis_prime_agent install`
+ works directly from the installed wheel.
### Fixed
- The Every node dashboard view no longer crashes on open: a declaration-order bug in the
renderer threw during construction before anything painted. The scene canvas also keeps its
accessible role/label now instead of being hidden from assistive technology.
+- Prompt-only recall now honours an opt-in `ENGRAPHIS_RECALL_ARM_CANDIDATE_K` env var (and
+ the matching `RecallEngine(arm_candidate_k_cap=...)` constructor argument) that clamps both
+ the first-page widening (`candidate_k + min(250, candidate_k*3)`) and the second-page
+ ceiling, so operators can trade untrusted-scope widening for latency on the new k=50
+ default without code changes. The accompanying benchmark test,
+ `test_recall_arm_candidate_k_cap.py`, uses a 300-fact trusted corpus because both requested
+ arm depths clamp to the same 49 rows on a smaller corpus and the timing assertion was
+ unreliable. Default behaviour is unchanged.
- Import previews now page the source manifest exactly like execution, so vaults whose manifest
outgrew one list page (10k identities) no longer show manifest-only files as silently absent
from the preview plan; beyond-boundary rows are reported as `missing` instead of dropped.
diff --git a/README.md b/README.md
index 31c94a2e..80421c2a 100644
--- a/README.md
+++ b/README.md
@@ -396,6 +396,51 @@ including `engraphis_check_update`, is in the [MCP tool reference](https://githu
For installation, configuration, lifecycle commands, and the local trust boundary, see the
[Pi extension guide](https://github.com/Coding-Dev-Tools/engraphis/blob/main/integrations/pi/README.md).
+### Command Code SessionStart hook
+
+`integrations/commandcode/` ships a SessionStart hook that warms up a new
+session with bounded, recalled context from the local Engraphis gateway. Fails
+open on timeout and is installed via `python scripts/install_cc_hook.py`.
+
+### prime-agent fleet
+
+`integrations/prime_agent/` ships a first-party Python package for
+[PrimeIntellect prime-agent](https://github.com/PrimeIntellect-ai/prime-agent)
+that exposes the same nine Smart MCP tools, with a `PrimeAgentFleet` of eight
+named sub-agents (`researcher`, `planner`, `coder`, `reviewer`, `tester`,
+`documenter`, `monitor`, `integrator`) sharing one `engraphis-mcp` stdio
+subprocess. Install via `pip install ./integrations/prime_agent` and register
+with `python scripts/install_prime_agent.py`. See the
+[prime-agent integration guide](https://github.com/Coding-Dev-Tools/engraphis/blob/main/integrations/prime_agent/README.md).
+
+**What the integration is.** A `PrimeAgentFleet` is a thin Python layer
+around the same `engraphis-mcp` Smart gateway every other host uses. At
+runtime the fleet holds one shared `EngraphisMcpClient`, which owns one
+`engraphis-mcp` subprocess over JSON-RPC stdio. Each of the eight named
+sub-agents gets its own Engraphis session (started lazily on first tool use)
+and its own default `repo` scope, so per-role memory is isolated while the
+local gateway stays single-process. The eight sub-agent names
+(`researcher`, `planner`, `coder`, `reviewer`, `tester`, `documenter`,
+`monitor`, `integrator`) are the fixed default; pass `agent_names=[...]` to
+`PrimeAgentFleet(...)` for a custom set. Concurrent tool calls serialize at
+the JSON-RPC frame layer through an `asyncio.Lock`, so framework-level
+parallelism (eight sub-agents reasoning at once) is preserved while the
+underlying MCP transport remains one ordered stream. The only integration
+surface is `EngraphisPrimeAgent.register()` in
+`integrations/prime_agent/src/engraphis_prime_agent/agent.py` -- that is the
+single adapter point to override if prime-agent's tool-registration API
+differs from the assumed `target.register_tool(name, fn, schema=...)`
+contract.
+
+The design -- eight named sub-agents, one shared stdio subprocess,
+per-agent session bootstrap, and `ENGRAPHIS_*`-only environment forwarding
+to the gateway -- is recorded in `~/.commandcode/plans/prime-agent-integration.md`
+on the host where the integration was developed. When that host plan is not
+available (other contributor machines, CI), the same design is summarized in
+the PR description that introduced the integration and in the
+[prime-agent integration guide](https://github.com/Coding-Dev-Tools/engraphis/blob/main/integrations/prime_agent/README.md)
+("Architecture" and "Concurrency model" sections).
+
## Quickstart: repository graph
```bash
@@ -721,8 +766,8 @@ file. It never searches the working directory for `.env`, and explicit process v
| `ENGRAPHIS_ALLOW_AUTOMATIC_CRITICAL_RETENTION` | `false` | Opt in only when an LLM supervisor may automatically assign the long-lived `critical` class; explicit user-selected critical retention is unaffected |
| `ENGRAPHIS_WHISPER_MODEL` | Not set | Enables local faster-whisper audio/video transcription |
| `ENGRAPHIS_POSTGRES_DSN` | Not set | CLI-only PostgreSQL source; used for the connection and never stored |
-| `ENGRAPHIS_POSTGRES_CONNECT_TIMEOUT` | `10` | PostgreSQL introspection connection timeout in seconds (bounded to 1–120) |
-| `ENGRAPHIS_POSTGRES_STATEMENT_TIMEOUT_MS` | `30000` | Per-introspection PostgreSQL statement timeout in milliseconds (bounded to 1–300000) |
+| `ENGRAPHIS_POSTGRES_CONNECT_TIMEOUT` | `10` | PostgreSQL introspection connection timeout in seconds (bounded to 1--120) |
+| `ENGRAPHIS_POSTGRES_STATEMENT_TIMEOUT_MS` | `30000` | Per-introspection PostgreSQL statement timeout in milliseconds (bounded to 1--300000) |
| `ENGRAPHIS_GRAPH_TOKEN` | Not set | Bearer token for `engraphis-graph-server`; required off-loopback |
| `ENGRAPHIS_GRAPH_HOST` / `ENGRAPHIS_GRAPH_PORT` | `127.0.0.1` / `8720` | Read-only graph/recall server bind address |
| `ENGRAPHIS_LLM_PROVIDER` | `openai` | `openai \| anthropic \| google \| openrouter \| custom` |
@@ -743,6 +788,10 @@ file. It never searches the working directory for `.env`, and explicit process v
| `ENGRAPHIS_CLOUD_ACCESS_TOKEN` | Not set | Optional short-lived access token for ephemeral jobs |
| `ENGRAPHIS_MANAGED_COMPUTE_CONSENT` | *(auto)* | Operator override only; default follows whether a cloud session is configured (connected = allowed, local-only = never). `0` opts a connected installation out; `1` permits local snapshot preparation but does not create a cloud credential or authorize an upload |
+The optional cross-encoder reranker is model- and hardware-dependent. Treat its quality and
+latency as deployment-specific until a versioned model identity, exact configuration, and
+reproducible evaluation artifact are available for the comparison being reported.
+
See `.env.example` for the full variable inventory. Supply those values through the process
environment or the trusted config file above; copying it to an arbitrary `./.env` does not make
Engraphis load it.
diff --git a/docs/architecture/engraphis-v2-architecture.png b/docs/architecture/engraphis-v2-architecture.png
new file mode 100644
index 00000000..afda4af2
Binary files /dev/null and b/docs/architecture/engraphis-v2-architecture.png differ
diff --git a/docs/architecture/engraphis-v2-architecture.svg b/docs/architecture/engraphis-v2-architecture.svg
new file mode 100644
index 00000000..c6f452fb
--- /dev/null
+++ b/docs/architecture/engraphis-v2-architecture.svg
@@ -0,0 +1,225 @@
+
\ No newline at end of file
diff --git a/docs/architecture/generate_engraphis_architecture.py b/docs/architecture/generate_engraphis_architecture.py
new file mode 100644
index 00000000..7bc327b7
--- /dev/null
+++ b/docs/architecture/generate_engraphis_architecture.py
@@ -0,0 +1,247 @@
+from __future__ import annotations
+
+import html
+from pathlib import Path
+
+
+WIDTH = 1600
+HEIGHT = 1240
+OUT = Path(__file__).with_name("engraphis-v2-architecture.svg")
+
+
+lines: list[str] = []
+late_labels: list[str] = []
+
+
+def add(value: str) -> None:
+ lines.append(value)
+
+
+def esc(value: str) -> str:
+ return html.escape(value, quote=True)
+
+
+def text(x: float, y: float, value: str, *, size: float = 14, fill: str = "#0f172a",
+ weight: str = "400", anchor: str = "start", letter: str = "0") -> None:
+ add(
+ f''
+ f'{esc(value)}'
+ )
+
+
+def rect(x: float, y: float, w: float, h: float, *, fill: str = "#ffffff",
+ stroke: str = "#cbd5e1", width: float = 1, radius: float = 12,
+ dash: str = "") -> None:
+ dash_attr = f' stroke-dasharray="{dash}"' if dash else ""
+ add(
+ f''
+ )
+
+
+def region(x: float, y: float, w: float, h: float, title: str, fill: str) -> None:
+ rect(x, y, w, h, fill=fill, stroke="#cbd5e1", width=1.2, radius=18, dash="8 6")
+ text(x + 20, y + 27, title, size=12, fill="#475569", weight="700", letter="1.2")
+
+
+def node(x: float, y: float, w: float, h: float, title: str, subtitle: str,
+ accent: str, *, fill: str = "#ffffff", title_size: float = 15,
+ subtitle_size: float = 11.5) -> None:
+ rect(x, y, w, h, fill=fill, stroke="#cbd5e1", width=1.2, radius=12)
+ rect(x, y, 7, h, fill=accent, stroke=accent, width=0, radius=4)
+ text(x + 20, y + 30, title, size=title_size, weight="700")
+ text(x + 20, y + 53, subtitle, size=subtitle_size, fill="#475569")
+
+
+def storage_node(x: float, y: float, w: float, h: float, title: str,
+ bullets: list[str], accent: str) -> None:
+ rect(x, y, w, h, fill="#ffffff", stroke="#cbd5e1", width=1.2, radius=12)
+ rect(x, y, 7, h, fill=accent, stroke=accent, width=0, radius=4)
+ text(x + 20, y + 29, title, size=14.5, weight="700")
+ for index, bullet in enumerate(bullets):
+ yy = y + 53 + index * 20
+ add(f'')
+ text(x + 34, yy, bullet, size=11.5, fill="#475569")
+
+
+def path(points: list[tuple[float, float]], color: str, marker: str, *, dash: str = "",
+ width: float = 2, opacity: float = 1.0) -> None:
+ data = "M " + " L ".join(f"{x},{y}" for x, y in points)
+ dash_attr = f' stroke-dasharray="{dash}"' if dash else ""
+ add(
+ f''
+ )
+
+
+def label(x: float, y: float, value: str, *, color: str = "#475569", anchor: str = "middle") -> None:
+ # Render labels after nodes so a short label never disappears beneath a box.
+ late_labels.append(
+ f'{esc(value)}'
+ )
+
+
+add(f'")
+
+OUT.write_text("\n".join(lines), encoding="utf-8")
+print(f"Wrote {OUT}")
diff --git a/engraphis/classic_assets/dashboard.js b/engraphis/classic_assets/dashboard.js
index a7b599ca..06b4130b 100644
--- a/engraphis/classic_assets/dashboard.js
+++ b/engraphis/classic_assets/dashboard.js
@@ -1236,7 +1236,7 @@ function loadGraphEngine(loadAll=false){
GRAPH_ENGINE_LOADING=new Promise((resolve,reject)=>{
const script=document.createElement('script');
const bust=GRAPH_ENGINE_RETRY>0?'&r='+GRAPH_ENGINE_RETRY:'';
- script.src='/v2-assets/engraphis-graph.js?v=20260815-merge-ready-1'+bust;
+ script.src='/v2-assets/engraphis-graph.js?v=20260828-galaxy-default-gravity-1'+bust;
/* A 200 that never registers the global is a corrupt/truncated asset, not a success —
resolving there would hand graphRenderEngine() an undefined EngraphisGraph. Failed
attempts drop the script node and clear the memo so the next call retries with a
diff --git a/engraphis/classic_assets/index.html b/engraphis/classic_assets/index.html
index d9770f20..a044c781 100644
--- a/engraphis/classic_assets/index.html
+++ b/engraphis/classic_assets/index.html
@@ -349,6 +349,6 @@
graph view. dashboard.js fetches both on demand from graphRender(); see loadForceGraph()
and loadGraphEngine(). scripts/externalize_dashboard_assets.py enforces both halves:
they stay out of this file, and the lazy references still have to resolve. -->
-
+