agent-joule/dev-docs/ARCHITECTURE.md
David Charlot 7a59f0ffc5 agent-mei: rename agent_joule panel/theme/workflows to agent-mei, MEI fleet wiring and honest-energy receipts
Brings over the uncommitted agent-mei work (Mac checkout, Sep 22) plus box
follow-ups (Oct 17: agent_mei action namespace in keymap/zed.rs, MEI_BRIDGE.md,
RENAME_AGENT_MEI.md, unmetered receipts report 0 uJ). joule_core, JOULE_CODE_*
and JouleDB names unchanged. *.bak.* scratch files excluded.
2026-10-27 12:59:12 -04:00

98 lines
8.8 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# Architecture & Integration
> Generated 2026-06-14. How `agent_mei_panel` wires together internally, to the Joule host, and to its 16 external deps. file:line against audited HEAD.
## Component map
```
crates/zed/src/main.rs:770 agent_mei_panel::init(cx)
crates/zed/src/zed.rs:704 AgentMeiPanel::load(...) (initialize_panels)
│
init(cx) [agent_mei_panel.rs:84] AgentMeiPanel (struct :177) owns:
├─ control::init_control_server(cx) l0_cache: L0Cache (joule-code-cascade)
├─ observe_new<Workspace> → ToggleFocus l3_model: Option<Arc<L3Model>> (jouleclaw-runtime/loader)
└─ observe_new<Editor> → LocalFimDelegate cascade_gate: CascadeGate (cascade_gate.rs / joule-code-jcp)
hooks: HookRegistry (hooks.rs)
methodology: MethodologyTracker (methodology.rs)
session_mgr: SessionManager (sessions.rs)
energy_counter: EnergyCounter (jouleclaw-energy)
```
## 1. Cascade data flow
Entry **`submit_prompt`** (`agent_mei_panel.rs:1600`). Each tier guarded by `authorize_tier(tier, intent)`; first to close wins.
```
submit_prompt :1600
├─ resolve_at_mentions → pending_context :1608
├─ ensure_ledger_loaded / ensure_l2_loaded :1618 (rehydrate L0/L2 across sessions)
├─ L0 authorize_tier(L0) → l0_cache.try_resolve :1628 hit → note_close(L0,0) → RETURN :1646
├─ L1 attempt_l1_deterministic :1834 parse_substitution; buffer.set_text → record_tier_edit(L1)
└─ attempt_l2_then_l3 :1673
├─ L2 embed (GGUF as encoder) → cosine ≥ threshold → replay prior edit → record_tier_edit(L2)
├─ L3 attempt_l3_then_l4 :1883
│ energy_before; document_symbols (LSP) on fg :1927
│ background: load_l3_model → codebase_index.search → format_symbol_context →
│ harness_prompt → l3_generate :1958
│ FILE blocks → apply_multi_file_edit; else parse_edit;
│ empty → send_to_l4; else review? stage_pending_edit : buffer.set_text + record_l3_edit
└─ L4 send_to_l4 :2462
authorize_tier(L4) DENY → "L4 denied by grant" RETURN ← no-phone-home seam
thread.send(ContentBlock) over ACP :2491
(close captured later via thread subscription — see §risks)
```
**Edit review** (`stage_pending_edit:2178` → `accept_pending_edit:2231` → `apply_staged_now:2204`): staging records a `PendingEdit`; buffers untouched until Accept (`set_text` + `record_l3_edit`). Reject just drops it. Control protocol exposes the same (`accept`/`reject`).
**Energy** (`bracket_energy:2443`): `after − before` from `energy_uj_now`; `Estimator` → `measured_uj = None` (honest). Counter from `host_energy_counter:3850` — Apple IOReport (aarch64), NVML/RAPL (linux), else `None`.
**Sealing** (`record_tier_edit:2542`, the shared close path): bracket energy → `build_coding_op` → `append_to_ledger:686` (hash-chained COSE_Sign1) → `note_methodology_op` → `l0_cache.insert` → `record_review` → `note_close` → `index_for_l2` → `attest_applied_edit:3039` (sandbox). Also folds the op into the active session via `record_active_op:728`.
**L4 close is asymmetric:** sealed not in `send_to_l4` but in the **thread subscription** (`:1123-1136`) — on `AcpThreadEvent`, `captured_op:3137` walks the thread's diffs into a `CodingOp`, then `append_to_ledger` + `l0_cache.insert`. (See risk #1.)
## 2. Joule integration seams
- **Init:** `main.rs:770` `agent_mei_panel::init(cx)` (the `zed.rs:5337` init is in `mod test` only — no production double-init).
- **Panel load:** `zed.rs:704` `AgentMeiPanel::load` in `initialize_panels`, added via `add_panel_when_ready` alongside the other dock panels.
- **Joule APIs touched:** `workspace::Workspace` (panel host, actions, `impl Panel`); `project::Project` (`agent_server_store`, `document_symbols` LSP, `open_buffer`, worktrees); `agent_servers`/`acp_thread`/`agent-client-protocol` (ACP connection + L4 send + diff subscription); `editor::Editor` (prompt + FIM provider); `language::Buffer`/`multi_buffer` (edit application); `lsp` (symbol context); macOS `sandbox` (Seatbelt for the agent's `run` tool).
## 3. External dependency boundary (16 git deps, pinned `4132daa`)
| Dep | Used by | Purpose | Status |
|---|---|---|---|
| `joule-code-cargo` | panel, methodology | `CodingOp`, `Edit`, `Anchor`, `SignedOp` | Core |
| `joule-code-cascade` | panel, cascade_gate | `L0Cache`, `Request`, `Stage` | Core |
| `joule-code-receipt` | panel | `Energy`, `Provenance`, `Receipt` | Core |
| `joule-code-schema` | panel, gate, methodology, sessions | `Tier`, `ContentId`, `ALG_ED25519` | Core |
| `joule-code-jcp` | cascade_gate | `JcpGate` — tier auth + sealed receipts | Core (gate) |
| `jcp-bridge` | tool_gate | `LocalToolGate` — per-tool-call auth | Core (gate) |
| `joule-code-identity` | sessions, panel | `Identity`, `ActorKind` — did:key principals | Used |
| `joule-code-spec` | methodology | `cite`, `coverage`, `Clause`, `SignedSpec` | Used |
| `joule-code-review` | methodology, panel | two-stage review ledger | Used |
| `joule-code-release` | methodology | `certify`, `ReleaseCertificate` — ship door | Used (via `ship_summary`) |
| `joule-code-joulecontract` | methodology | `frame_check` — out-of-frame edit gate | Used |
| `joulecontract-schema` | methodology | `Contract`, `Rigor` | **Thin** (inert frame contract only) |
| `jouleclaw-energy` | panel | `EnergyCounter` + IOReport/NVML/RAPL | Core |
| `jouleclaw-runtime` | panel | `generate`, `embed`, `cosine_similarity` | Core (L3) |
| `jouleclaw-loader-gguf` | panel | GGUF reader/tokenizer + safetensors Mellum | Core (L3) |
| `joule-code-conformance` (dev) | tests | `verify_pack` over C1–C4 vectors | Test-only |
**Flags:** `joule-code-release`'s discharge gate is hardcoded `false` (`ship_summary:2985`) — ship door un-passable; `jcp-bridge`/`ToolGate` only active under `JOULE_CODE_AGENT_TOOLS`; Mellum surface only via `JOULE_CODE_L3_ENGINE=mellum`.
## 4. Subsystem wiring
- **`authorize_tier`** `:2611` is the single junction composing **hooks** (PreTool, deny-wins, `:2623`) + **cascade_gate** (JCP grant + lifetime ceiling, `:2629`). Every tier passes through it.
- **`record_tier_edit`** `:2542` is the close junction fanning into methodology, sessions, L0 cache, L2 index, reviews, sandbox attestation.
- **`control_dispatch`** `:2261` is the headless seam — the control server (`control.rs:185`) and MCP bridge (`run_mcp_bridge`) drive the same panel methods a human action would.
- **methodology** ← `note_methodology_op:943` signs+cites+frame-checks each op, exports to `.joule/*.json`. **sessions** ← `ensure_session_mgr` builds the manager with `cascade_gate.grant()`; worktrees back background sessions (`active_session_worktree` → agent cwd). **tool_gate** ← passed as the `authorize_tool` closure into `local_agent::run_agent_loop`.
## 5. Integration risks / danglers
1. **L4 close depends on the thread subscription firing** (`:1123-1136`). If the subscription is dropped on reconnect (`:1141`) or no diff event fires, the L4 turn produces no ledger op and no L0 entry — a silently lost close (unlike synchronous L0–L3). *(See FINDINGS P1.)*
2. **Release/ship door structurally un-passable** — `contract_discharged = false` hardcoded (`:2985`); the full release certificate can't be reached (only merge-via-`verified` can).
3. **Session grant ↔ live gate is descriptor-only** — `SessionGrant` is sealed into the open receipt but not bound to the live `CascadeGate` (`sessions.rs:25-27`); sessions with different declared grants are gated identically.
4. **Heavy env-var gating — accountability is opt-in.** Out of the box (no `JOULE_CODE_GRANT`/`_LIFETIME_UJ`/`_AGENT_TOOLS`/`JOULECLAW_L3_MODEL`/`_REVIEW_EDITS`/`_CONTROL_SOCK`) the cascade runs **ungated and L4-reachable** — no-phone-home is a configuration, not a default.
5. **`session_mgr` silently absent outside a git repo** — `ensure_session_mgr` returns early; `record_active_op` no-ops; closed ops hit the ledger but never fold into a session chain, with no surfaced warning.
6. **`joulecontract-schema` near-vestigial** — only builds an inert frame contract.
7. **RAPL counter fragile by design** — selected only if a read succeeds; normal Linux user → `None` → 0-J receipts that look honest but carry no measurement and no "unavailable" signal.
8. **FIM provider attaches per-editor at `observe_new` time** (set-once); editors opened before a model is configured stay no-op for the session.