agent-joule/dev-docs/ARCHITECTURE.md
David Charlot 88a41bedd3 Rebrand Zed -> Joule across UI, docs, embedded assets, and legal
The fork shipped renamed binary/bundle but the product name "Zed" was still
shown everywhere in the UI. Rename the display name to "Joule" (tagline
"Energy-metered agentic coding") while preserving every load-bearing reference.

Display renames (105 .rs files): release_channel display_name, welcome +
onboarding + ai_onboarding, macOS app menu (About/Hide/Quit), window titles,
About panel, settings UI, notifications, update/collab messages, system specs.
Bundle display name (crates/zed/Cargo.toml) -> "Joule" so CFBundleName / the
menu-bar name and the .app are "Joule" (identifier science.transaction.AgentJoule
and the agent-joule binary are unchanged).

Docs (223 .md): product prose Zed -> Joule, fence-aware (code blocks untouched).
Embedded assets (12 JSONC): // comments only.
Legal (4 files): product -> Joule, entity "Zed Industries, Inc." -> "Transaction
Science, Inc.", domain zed.dev -> openie.dev. NOTE: these are structurally Zed's
inherited terms and need a real legal review, not just a rename.

Deliberately PRESERVED (renaming would break the app): font aliases ("Zed Plex
Sans"/".ZedMono" in text_system + assets), HTTP header names (X-Zed-Predict-*),
zed:: action ids, app/bundle identifiers (dev.zed.Zed*, Zed-Editor-* product ids),
zed.dev URLs, ZED_* env vars, crate/package names, "Zed Industries" in non-legal
attribution, and the {zed,Zed} config-dir glob.

Signed + notarized + stapled as Joule.app, installed to /Applications, verified
(Gatekeeper: Notarized Developer ID, FH93XC2VRR). Caveat: test-code string
literals were renamed too, so some `cargo test` assertions may need updating.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
2026-07-03 02:23:35 -04:00

8.8 KiB
Raw Permalink Blame History

Architecture & Integration

Generated 2026-06-14. How agent_joule_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_joule_panel::init(cx)
   crates/zed/src/zed.rs:704   AgentJoulePanel::load(...)  (initialize_panels)
            │
   init(cx) [agent_joule_panel.rs:84]                AgentJoulePanel (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_joule_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_joule_panel::init(cx) (the zed.rs:5337 init is in mod test only — no production double-init).
  • Panel load: zed.rs:704 AgentJoulePanel::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.