diff --git a/.planning/ROADMAP.md b/.planning/ROADMAP.md index 6183f9bd..9e8f5045 100644 --- a/.planning/ROADMAP.md +++ b/.planning/ROADMAP.md @@ -345,7 +345,7 @@ Plans: **Depends on:** Independent of Phases 1–12 for its UI and content work. Its security model must not contradict Phase 10 (Key-Material Hardening) — coordinate rather than widen. AIUI's own source lives in a **separate repository** (`git.tx1138.com/lfg2025/AIUI`, branch `development`, cloned at `~/Projects/AIUI`), so this phase spans two repos and needs push access to both. -**Plans:** 9/15 plans executed +**Plans:** 10/15 plans executed Plans: @@ -369,7 +369,7 @@ Plans: **Wave 4** - [x] 13-09-PLAN.md — AIUI delivery: enforced build, pinned commit, live-asset verify, iframe sandbox mechanism (AIUI-04/05) -- [ ] 13-10-PLAN.md — D-04 chain: Ollama tool-calling + D-08 node-side history (AIUI-01) +- [x] 13-10-PLAN.md — D-04 chain: Ollama tool-calling + D-08 node-side history (AIUI-01) - [ ] 13-11-PLAN.md — SongGrid lit from the real library + the m4a/aac/opus/wma share-mime fix (AIUI-03) **Wave 5** diff --git a/.planning/STATE.md b/.planning/STATE.md index 7279e89c..3c425f1c 100644 --- a/.planning/STATE.md +++ b/.planning/STATE.md @@ -5,15 +5,15 @@ milestone_name: milestone current_phase: 13 current_phase_name: aiui-functional-conversational-node-control-and-content-surf status: executing -stopped_at: 13-08 COMPLETE 2026-08-05, next wave 4 (13-10, then 13-11 -- check 13-11 for stale /home/archipelago/Projects/AIUI absolute paths per D-19 before executing it) -last_updated: "2026-08-05T19:17:57.167Z" +stopped_at: 13-10 COMPLETE 2026-08-05, next wave 4 remainder is 13-11 -- check 13-11 for stale /home/archipelago/Projects/AIUI absolute paths per D-19 before executing it +last_updated: "2026-08-05T22:06:22.580Z" last_activity: 2026-08-05 -last_activity_desc: 13-08 complete (D-11 confirm gate + trusted-chrome dialog, checkpoint approved on-device) +last_activity_desc: 13-10 complete (D-04 Ollama tool-calling backend + D-08 node-side chat history) progress: total_phases: 13 completed_phases: 2 total_plans: 60 - completed_plans: 47 + completed_plans: 48 percent: 15 --- @@ -29,11 +29,11 @@ See: .planning/PROJECT.md (updated 2026-07-29) ## Current Position Phase: 13 (aiui-functional-conversational-node-control-and-content-surf) — EXECUTING -Plan: 9 of 15 complete (13-01, 13-02, 13-03, 13-04, 13-05, 13-06, 13-07, 13-08, 13-09) — next: wave 4 (13-10, 13-11) +Plan: 10 of 15 complete (13-01, 13-02, 13-03, 13-04, 13-05, 13-06, 13-07, 13-08, 13-09, 13-10) — next: wave 4 remainder (13-11) Status: Ready to execute -Last activity: 2026-08-05 — 13-08 complete (D-11 confirm gate + trusted-chrome dialog, checkpoint approved on-device) +Last activity: 2026-08-05 — 13-10 complete (D-04 Ollama tool-calling backend + D-08 node-side chat history) -Progress: [████████░░] 78% +Progress: [████████░░] 80% ## Performance Metrics @@ -66,6 +66,7 @@ Progress: [████████░░] 78% | Phase 01 P01 | n/a-continuation | 2 tasks | 1 files | | Phase 13 P07 | ~3h45m | 2 tasks | 5 files | | Phase 13 P08 | ~7h45m (elapsed, w/ session restart) | 3 tasks | 10 files | +| Phase 13 P10 | ~2h45m | 2 tasks | 10 files | ## Accumulated Context @@ -135,6 +136,9 @@ Decisions are logged in PROJECT.md (10 locked ADRs in the `` block + - [Phase ?]: [Phase 13, 13-08]: System prompt rewritten to make the model call destructive tools directly rather than text-asking for confirmation — the original wording ('every write requires a human confirmation') read as 'collect consent in text first', which skipped the tool call and dropped the user's typed 'confirmed' into a void on the next stateless turn - [Phase ?]: [Phase 13, 13-08]: CONFIRM_TIMEOUT raised 120s->300s and the full HTTP timeout chain (rpcClient, assistant.chat 420s, AIUI bridge 180s->430s) raised past it, so transport can no longer time out a human reading the confirm dialog before the confirm gate itself does - [Phase ?]: [Phase 13, 13-08]: Declined actions remembered per-turn in ToolExecCtx, keyed by the same action_key the approval nonce binds; execute_tool refuses a re-ask for that exact action before the gate reopens, closing a retry loop where a declined action kept re-prompting (T-13-50 mechanized) +- [Phase ?]: [Phase 13, 13-10]: OllamaBackend leads the D-04 chain via POST /api/chat (never assist.rs's /api/generate); model_supports_tools() probes /api/show and process-caches the answer, turning AI-SPEC's qwen2.5-coder [ASSUMED] tool-capability note into a runtime fact; FallbackChain falls through to Claude on a transport error mid-turn, not just at initial selection +- [Phase ?]: [Phase 13, 13-10]: history.rs persists the full ChatMessage transcript per CallerScope-derived HistoryKey under data_dir, atomic (temp+rename) and 0600, with wallet/files-category tool-call arguments redacted before disk (verified against raw bytes, not just the struct); chat() persists but does not yet feed prior turns back into live model context (deliberately scoped out, needs Claude tool_use/tool_result id-pairing test budget as a follow-up) +- [Phase ?]: [Phase 13, 13-10]: detect_ollama() and its two containing modules bumped to pub(crate) (api/rpc/mod.rs, api/rpc/mesh/mod.rs, api/rpc/mesh/assistant.rs) so assistant::backends could reuse the existing Ollama probe rather than writing a second one; run_loop (loop_.rs) now returns (answer, full_history) instead of just the answer, both Rule-3 deviations structurally required by the plan's own stated intent ### Pending Todos @@ -212,8 +216,8 @@ The 5x lifecycle gate was NOT run. ## Session Continuity -Last session: 2026-08-05T19:17:34.921Z -Stopped at: 13-08 COMPLETE 2026-08-05, next wave 4 (13-10, then 13-11 -- check 13-11 for stale /home/archipelago/Projects/AIUI absolute paths per D-19 before executing it) +Last session: 2026-08-05T22:06:22.548Z +Stopped at: 13-10 COMPLETE 2026-08-05, next wave 4 remainder is 13-11 -- check 13-11 for stale /home/archipelago/Projects/AIUI absolute paths per D-19 before executing it `assistant::` tests incl. `approval_nonce_binds_to_exact_action` individually; dispatcher.rs untouched; fc09d7a2's tools.rs/grants.rs/backends/mod.rs diffs confirmed rustfmt-only, no behavior change). Task 2 (ToolConfirmModal.vue, contextBroker.ts, Chat.vue mount, diff --git a/.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/13-10-SUMMARY.md b/.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/13-10-SUMMARY.md new file mode 100644 index 00000000..8db13c4d --- /dev/null +++ b/.planning/phases/13-aiui-functional-conversational-node-control-and-content-surf/13-10-SUMMARY.md @@ -0,0 +1,190 @@ +--- +phase: 13-aiui-functional-conversational-node-control-and-content-surf +plan: 10 +subsystem: ai +tags: [rust, ollama, tool-calling, chat-history, persistence, d-04, d-08] + +requires: + - phase: 13-aiui-functional-conversational-node-control-and-content-surf + provides: "13-01's Backend trait/BackendTurn seam, 13-05's curated tool registry + grants, 13-08's confirm gate (ConfirmGate, ToolExecCtx) that every destructive tool call passes through regardless of backend" +provides: + - "assistant/backends/ollama.rs: OllamaBackend — POST /api/chat tool-calling adapter with synthesized call ids, pass-through (not re-parsed) arguments, an explicit generation cap, and model_supports_tools() turning AI-SPEC's qwen2.5-coder [ASSUMED] note into a runtime, process-cached fact" + - "assistant/backends/mod.rs: select_backend is now async and D-04-ordered (Ollama first via detect_ollama() reuse, Claude fallback); a new FallbackChain falls through to Claude on an Ollama transport error mid-turn" + - "assistant/history.rs: History/HistoryKey — D-08 node-side chat persistence under data_dir, keyed by CallerScope, atomic (temp+rename) and 0600, with truncation + incremental compaction and wallet/files-category argument redaction" + - "assistant.history / assistant.clear-history RPCs, routed through 13-01's existing assistant. dispatcher arm" + - "run_loop (loop_.rs) now returns (answer, full_history) so chat() can persist the whole completed turn, not just the final answer" +affects: [13-11, 13-12, 13-13, 13-14, 13-15] + +tech-stack: + added: [] + patterns: + - "Async backend selection with per-call transport fallback: FallbackChain wraps a primary Backend and falls through to a secondary on a send() error, so a leg that answers most of a loop and then drops mid-turn still completes the turn" + - "Process-lifetime capability cache keyed by (base_url, model): model_supports_tools probes once per (server, model) pair and never re-probes, failing CLOSED (not-tool-capable) on any ambiguity" + - "data_dir-scoped JSON persistence, atomic via temp-sibling + rename (music/index.rs::save_atomic's precedent, not streaming/session.rs's in-place write) plus 0600 (grants.rs's precedent)" + - "Caller-resolved category maps instead of a shared registry reference: history.rs takes a HashMap the caller builds from tools::registry(), keeping history.rs decoupled from ToolRegistry's private internals and independently unit-testable" + +key-files: + created: + - core/archipelago/src/assistant/backends/ollama.rs + - core/archipelago/src/assistant/history.rs + modified: + - core/archipelago/src/assistant/backends/mod.rs + - core/archipelago/src/assistant/mod.rs + - core/archipelago/src/assistant/loop_.rs + - core/archipelago/src/assistant/tools.rs + - core/archipelago/src/api/rpc/assistant_chat.rs + - core/archipelago/src/api/rpc/mod.rs + - core/archipelago/src/api/rpc/mesh/mod.rs + - core/archipelago/src/api/rpc/mesh/assistant.rs + +key-decisions: + - "detect_ollama() (api/rpc/mesh/assistant.rs) bumped to pub(crate), with its containing mesh and assistant submodules bumped to pub(crate) mod (api/rpc/mod.rs, api/rpc/mesh/mod.rs) — the minimal visibility change needed to reuse the existing probe from assistant::backends rather than writing a second one, matching this file's own pre-existing pub(crate) mod bitcoin_relay;/pub(crate) mod lnd; convention" + - "run_loop's return type changed from Result to Result<(String, Vec)> — structurally necessary so chat() can persist the tool-call/tool-result messages the loop built internally, not only the final answer text. Touches loop_.rs and one pre-existing test call site in tools.rs, both outside this plan's declared files_modified — documented as Rule 3 (structurally necessary), mirroring 13-05's own precedent for the same class of change" + - "history.rs takes a HashMap built by the caller (mod.rs::chat()) rather than a &ToolRegistry reference — ToolRegistry's internal tools field is private to the tools module, so a sibling module (history) cannot construct a synthetic registry for its own redaction tests. The HashMap seam keeps history.rs fully unit-testable (including the wallet/files redaction test) without adding a test-only constructor to tools.rs" + - "chat() persists the completed turn to history.rs but does NOT (yet) feed a caller's prior persisted turns back into a NEW turn's live model context — scoped out deliberately: none of Task 2's bullets require it, and reconstructing prior ToolCall/ToolResult pairs from the persisted, id-less record in a way that stays correct against Claude's strict tool_use/tool_result id-pairing needs its own test budget. Documented inline in mod.rs::chat()'s doc comment as a named follow-up" + - "OllamaBackend's model selection uses a fixed OLLAMA_DEFAULT_MODEL constant (mirroring assist.rs::DEFAULT_MODEL) rather than reading mesh.assistant-configure's live-configured model — that config lives inside MeshState, reachable only when the mesh service is running, and AIUI's chat path must work with mesh entirely absent. Matches the existing ClaudeBackend's own hardcoded-constant-model pattern from 13-01/13-08" + - "The generation-length cap is set explicitly on every OllamaBackend request but is not verified against a live Ollama server this session (no Ollama running on this dev box — noted in the executor's own gotchas) — the wire-format contract is exercised end-to-end against a local hyper-based HTTP stub instead, and model_supports_tools/select_backend's fall-through path is proven the same way. Live on-device verification (a real question answered by a real local model, and the fall-through when Ollama is stopped) is deferred; see Next Phase Readiness" + +requirements-completed: [AIUI-01] + +coverage: + - id: D1 + description: "OllamaBackend implements the Backend trait against POST /api/chat (never /api/generate), with a messages+tools request, non-streaming requests, an explicit generation cap on every request, synthesized non-empty unique-within-turn tool-call ids, and pass-through (not re-parsed) tool-call arguments" + requirement: AIUI-01 + verification: + - kind: unit + ref: "cargo test --package archipelago assistant::backends::ollama::tests:: (10 tests: ollama_uses_chat_endpoint_not_generate, tool_calls_get_synthesized_ids, arguments_object_is_not_string_parsed, text_only_response_maps_to_backend_turn_text, request_is_non_streaming_with_explicit_generation_cap, request_carries_messages_and_tools_arrays, model_supports_tools_reads_capabilities_from_api_show, non_tool_capable_model_falls_through_to_claude, model_supports_tools_caches_for_process_lifetime, unreachable_ollama_returns_transport_error_not_panic)" + status: pass + human_judgment: false + - id: D2 + description: "select_backend is async and D-04-ordered — Ollama first (via the reused detect_ollama() probe) when reachable and tool-capable, Claude otherwise; a transport error mid-turn on the Ollama leg falls through to Claude for that same call rather than failing the turn" + requirement: AIUI-01 + verification: + - kind: unit + ref: "cargo test --package archipelago assistant::backends::tests:: (3 tests: ollama_is_selectable_truth_table, ollama_transport_error_falls_through_to_next_backend, healthy_primary_never_reaches_secondary)" + status: pass + human_judgment: false + - id: D3 + description: "History persists the completed ChatMessage transcript under data_dir per caller (HistoryKey from CallerScope), survives a simulated daemon restart, truncates oversized tool results with a visible marker, compacts older turns into an incrementally-extended running summary once the verbatim window is exceeded, and never persists a wallet/files-category tool's argument value (verified against both the deserialized struct and the raw file bytes)" + requirement: AIUI-01 + verification: + - kind: unit + ref: "cargo test --package archipelago assistant::history::tests:: (8 tests: append_persists_and_survives_reload, operator_and_mesh_transcripts_are_separate, long_tool_result_is_truncated_with_marker, compaction_folds_older_turns_into_incremental_summary, clear_removes_only_this_callers_transcript, wallet_tool_arguments_never_reach_the_transcript, project_has_no_path_to_pending_confirmation_state, history_file_is_created_0600)" + status: pass + human_judgment: false + - id: D4 + description: "assistant.history / assistant.clear-history are reachable through the existing assistant.* dispatcher arm (dispatcher.rs untouched), each scoped to the calling session's own HistoryKey" + requirement: AIUI-01 + verification: + - kind: unit + ref: "git diff --exit-code -- archipelago/src/api/rpc/dispatcher.rs (exit 0); cargo test --package archipelago assistant:: full suite compiles and passes with the new match arms wired in handle_assistant" + status: pass + human_judgment: false + - id: D5 + description: "A real question, answered locally by a real Ollama server when reachable and tool-capable, and the observable fall-through-to-Claude behavior when Ollama is stopped, on a real node" + verification: [] + human_judgment: true + rationale: "No Ollama server is running on this dev box (confirmed refused at 127.0.0.1:11434, per the executor's own pre-briefed gotcha) — the wire-format contract, capability probe, and fall-through decision are all proven against a local HTTP stub instead, which is a real but not live-Ollama verification. The plan's own top-level block names this exact live check; it could not be run this session and needs a human/on-device pass on a box with Ollama installed before this can be marked fully proven end-to-end." + +duration: ~2h45m (dominated by concurrent-load cargo compiles on a shared 4-core box also running another agent's release build — each full test compile took 16-22 minutes) +completed: 2026-08-05 +status: complete +--- + +# Phase 13 Plan 10: D-04's Local-First Ollama Leg + D-08 Node-Side History Summary + +**Ollama tool-calling backend (`POST /api/chat`, synthesized call ids, pass-through arguments, capability-probed fall-through) leading the D-04 chain, plus a per-caller, atomic, compacted chat-history transcript under `data_dir` (D-08) that `assistant.history`/`assistant.clear-history` serve back to the calling session.** + +## Performance + +- **Duration:** ~2h45m wall-clock (`a245e5d2` 15:18 → `3da9928c` 18:03, 2026-08-05), most of it spent inside three full `cargo test --package archipelago assistant::` compiles (16-22 min each) on a 4-core box that was concurrently running another agent's `cargo build --release` for an unrelated in-flight release cut +- **Tasks:** 2/2 (both `type="auto" tdd="true"`, no checkpoints — plan is fully autonomous) +- **Files modified:** 10 (2 created: `backends/ollama.rs`, `history.rs`; 8 modified) + +## Accomplishments + +- `OllamaBackend` (`assistant/backends/ollama.rs`) implements the `Backend` trait against Ollama's tool-calling chat endpoint — a different endpoint, request shape, and response shape from `mesh/listener/assist.rs::call_ollama`'s single-shot prompt endpoint, which is left untouched. Ollama's per-call tool-call ids (absent on the wire) are synthesized (`synthesize_call_id`); its already-parsed `function.arguments` object is passed straight through with zero `from_str` calls anywhere in the file (grep-verified). +- `model_supports_tools` queries Ollama's own `/api/show` and caches the answer for the process lifetime, keyed by `(base_url, model)` — this is what turns AI-SPEC's `[ASSUMED]` note about `qwen2.5-coder`'s tool capability into a runtime, checked fact rather than an inherited guess. +- `select_backend` (`backends/mod.rs`) is now `async` and D-04-ordered: it reuses the existing `detect_ollama()` probe (bumped to `pub(crate)`, along with its two containing modules, matching this file's own pre-existing `pub(crate) mod bitcoin_relay;`/`pub(crate) mod lnd;` convention) rather than writing a second probe. A new `FallbackChain` wraps the Ollama leg so a transport error reached mid-turn (not just at initial selection) falls through to Claude for that same call instead of failing the whole turn. +- `History`/`HistoryKey` (`assistant/history.rs`) persist the `ChatMessage` transcript under `data_dir/assistant/history/.json`, atomically (temp-sibling + rename, matching `music/index.rs::save_atomic`'s precedent — a crash mid-write leaves the previous transcript intact) and 0600 (matching `grants.rs`'s convention). `HistoryKey` is derived from `CallerScope`, so an operator's AIUI transcript and a mesh peer's transcript are structurally distinct files. +- Oversized tool results are truncated with a visible marker (`MAX_TOOL_RESULT_CHARS`, a NEW assistant-scoped constant — never `assist.rs`'s LoRa-airtime-tuned `MAX_REPLY_CHARS`); once the verbatim window exceeds `KEEP_VERBATIM_TURNS`, older turns fold into a running summary extended incrementally (proven by asserting the earlier summary text survives verbatim as a substring after further growth, not merely that a summary exists). +- Wallet/files-category tool-call arguments are never persisted, verified against both the deserialized struct field (`None`) and the raw on-disk JSON bytes (the sensitive values themselves never appear in the file) — categories are resolved by the caller (`mod.rs::chat()`) from the same `tools::registry()` `execute_tool` uses, so `history.rs` never carries a second, driftable category list. +- `assistant.history`/`assistant.clear-history` route through 13-01's existing single `assistant.*` dispatcher arm; `dispatcher.rs` is untouched (`git diff --exit-code` confirmed). +- `run_loop` (`loop_.rs`) now returns `(answer, full_history)` instead of just the answer string, so `chat()` can persist the tool-call/tool-result messages the loop built internally, not only the user's question and the final answer. +- Full `assistant::` suite: **50/50 passing** (42 after Task 1 alone — 29 pre-existing + 13 new — then 50 after Task 2's 8 new history tests land), including `confirm::tests::restart_drops_pending_not_executes` (S-09 not weakened). + +## Task Commits + +Each task was committed atomically, with a Task-1-only intermediate state deliberately reconstructed (temporarily reverting `loop_.rs`'s signature change, `mod.rs`'s history wiring, and `assistant_chat.rs`'s new RPC arms) so Task 1's commit is genuinely self-contained and independently compilable/testable, matching the plan's own 2-task boundary rather than 13-05's precedent of a combined commit: + +1. **Task 1: Ollama tool-calling, first in the D-04 chain** — `821d8700` (feat). 42/42 `assistant::` tests pass in this commit's own tree state (verified directly, not inferred). +2. **Task 2: Node-side history, scoped by caller, compacted rather than truncated** — `3da9928c` (feat). 50/50 `assistant::` tests pass. + +**Plan metadata:** this commit (`docs(13-10): complete Ollama backend + node-side history plan`) + +## Files Created/Modified + +- `core/archipelago/src/assistant/backends/ollama.rs` (new) — `OllamaBackend`, `synthesize_call_id`, `model_supports_tools`, `OLLAMA_BASE_URL`, `OLLAMA_CHAT_URL`, `OLLAMA_DEFAULT_MODEL`, `OLLAMA_NUM_PREDICT`, `OLLAMA_HTTP_TIMEOUT`, `message_to_wire` +- `core/archipelago/src/assistant/backends/mod.rs` — `select_backend` now async, D-04-ordered; new `BackendId`, `FallbackChain`, `ollama_is_selectable` +- `core/archipelago/src/assistant/history.rs` (new) — `History`, `HistoryKey`, `PersistedMessage`/`PersistedRole`/`PersistedToolCall`/`PersistedToolResult`, `MAX_TOOL_RESULT_CHARS`, `KEEP_VERBATIM_TURNS`, `History::{load,save,append,compact,clear,recent}` +- `core/archipelago/src/assistant/mod.rs` — `chat()` rewired to await the async `select_backend`, persist the completed turn via `history.rs`; `pub mod history;` added +- `core/archipelago/src/assistant/loop_.rs` — `run_loop` returns `(String, Vec)`; the final answer is now also pushed onto history as a trailing `Assistant` message before returning +- `core/archipelago/src/assistant/tools.rs` — one pre-existing test call site updated to destructure `run_loop`'s new tuple return (mechanical compile fix, Rule 3) +- `core/archipelago/src/api/rpc/assistant_chat.rs` — `handle_assistant_history`, `handle_assistant_clear_history`, two new match arms in `handle_assistant` +- `core/archipelago/src/api/rpc/mod.rs` — `mod mesh;` → `pub(crate) mod mesh;` +- `core/archipelago/src/api/rpc/mesh/mod.rs` — `mod assistant;` → `pub(crate) mod assistant;` +- `core/archipelago/src/api/rpc/mesh/assistant.rs` — `detect_ollama()` → `pub(crate) async fn detect_ollama()` + +## Decisions Made + +See `key-decisions` in frontmatter — the `detect_ollama()` visibility bump, `run_loop`'s return-type change (Rule 3, mirroring 13-05's precedent), `history.rs`'s `HashMap` seam instead of a `&ToolRegistry` reference, the deliberate scoping decision to persist-but-not-yet-feed-back history into live model context, `OllamaBackend`'s fixed default-model constant (matching `ClaudeBackend`'s own pattern), and the deferred live-Ollama on-device verification. + +## Deviations from Plan + +### Auto-fixed Issues + +**1. [Rule 3 - Blocking] `run_loop` needed to return its built history, not just the final answer** +- **Found during:** Task 2, while wiring `chat()` to persist the completed turn +- **Issue:** `history.rs`'s persistence behavior explicitly requires the tool-call/tool-result messages a loop iteration produces (per AI-SPEC's "every turn of the loop... gets serialized to the per-node chat history file"), but `run_loop` (`loop_.rs`, not in this plan's `files_modified`) only ever returned the final answer string — `chat()` had no way to see what the loop built internally. +- **Fix:** `run_loop`'s return type became `Result<(String, Vec)>`; the loop now also pushes a final `Assistant` message carrying the answer before returning, so the returned vector IS the complete persisted-worthy transcript for the turn. +- **Files modified:** `core/archipelago/src/assistant/loop_.rs`, plus one pre-existing test call site in `core/archipelago/src/assistant/tools.rs` that needed a mechanical destructure-tuple fix to keep compiling. +- **Verification:** Full `assistant::` suite green (50/50) both immediately after this change and in the final state. +- **Committed in:** `3da9928c` (Task 2 commit) + +**2. [Rule 3 - Blocking] `detect_ollama()` was unreachable from `assistant::backends` without a visibility change** +- **Found during:** Task 1, implementing `select_backend`'s D-04 chain +- **Issue:** The plan's own read_first explicitly names `detect_ollama()` (`api/rpc/mesh/assistant.rs`) as the probe to reuse, but it — and its two containing modules (`mesh` inside `api::rpc`, `assistant` inside `mesh`) — were all module-private, invisible outside their own subtree. `crate::assistant::backends` is a completely separate module tree and could not reach it at all without a visibility change. +- **Fix:** Bumped `detect_ollama()` to `pub(crate)`, and its two containing `mod` declarations to `pub(crate) mod` — matching this same file's own pre-existing convention (`pub(crate) mod bitcoin_relay;`, `pub(crate) mod lnd;`) for exactly this kind of cross-module reuse. No behavior change to any existing caller. +- **Files modified:** `core/archipelago/src/api/rpc/mod.rs`, `core/archipelago/src/api/rpc/mesh/mod.rs`, `core/archipelago/src/api/rpc/mesh/assistant.rs`. +- **Verification:** `cargo check --package archipelago` clean; full `assistant::` suite green. +- **Committed in:** `821d8700` (Task 1 commit) + +--- + +**Total deviations:** 2 auto-fixed (both Rule 3 — blocking issues discovered while implementing the plan's own explicitly-stated intent, not scope creep). No architectural changes; both fixes were the minimal change needed to make the plan's own read_first guidance and Task 2's own behavior bullets achievable at all. +**Impact on plan:** Neither deviation touches a file the plan's threat model assigns a mitigation to beyond what was already planned; both are additive visibility/signature changes with no security-relevant behavior change to any existing caller. + +## Issues Encountered + +- **Shared-box compute contention.** This 4-core dev box was concurrently running another agent's `cargo build --release -p archipelago` (an in-flight release cut in the sibling `archy` worktree) for the entire session. Each full `cargo test --package archipelago assistant::` compile took 16-22 minutes as a result (vs. the executor gotchas' expectation of a much faster targeted-filter run) — no test failures resulted, only elongated wall-clock time. Verified via `ps aux` mid-session that this was genuine resource contention, not a bug in this plan's own code. +- **No live Ollama server available.** Per the executor's own pre-briefed gotcha (127.0.0.1:11434 refuses connections on this box), the plan's top-level `` item "with Ollama running and tool-capable, a read question is answered locally; with Ollama stopped, the same question falls through to Claude and the fall-through is logged" could not be exercised against a real Ollama process this session. All of `OllamaBackend`'s wire-format behavior, `model_supports_tools`' capability probing, and the D-04 fall-through decision logic ARE proven — against a local `hyper`-based HTTP stub (no mock-HTTP crate exists in this workspace) that faithfully reproduces Ollama's documented `/api/chat` and `/api/show` shapes — but a live-server, on-device pass is still needed before this can be called fully end-to-end proven. See coverage item D5. + +## User Setup Required + +None — no external service configuration required by this plan. (A user who wants to exercise the Ollama leg on their own node needs Ollama installed and running locally with a tool-capable model pulled; that is a pre-existing product requirement, not new setup this plan introduces.) + +## Next Phase Readiness + +- D-04's chain is now Ollama-first, Claude-fallback, with a real (not tracer-stub) local-model leg — 13-13's Routstr leg can insert as a third `Backend` without changing the trait or `select_backend`'s shape, exactly as the architecture promised. +- D-08's persistence is fully wired at the storage layer (append/load/compact/redact/truncate) and exposed via `assistant.history`/`assistant.clear-history`, but `chat()` deliberately does not yet feed a caller's prior turns back into a NEW turn's live model context — a scoped, documented follow-up (see `mod.rs::chat()`'s doc comment). Whoever picks this up next should budget real test coverage for Claude's strict `tool_use`/`tool_result` id-pairing requirement before reconstructing persisted turns into live `ChatMessage`s, since `history.rs`'s persisted `PersistedToolCall`/`PersistedToolResult` records deliberately don't carry the original call ids. +- **Live-Ollama on-device verification is the one open item from this plan's own `` block** — needs a box with Ollama installed and a tool-capable model pulled (13-08-SUMMARY.md's "Known Model-Quality Observations" section, and the flagged `[ASSUMED]` status of `qwen2.5-coder`'s tool-capability tag, are both still worth re-checking against a real model at that time). +- 13-11 (SongGrid + share-mime fix) still carries the documented D-19 stale-`/home/archipelago/Projects/AIUI` absolute-path risk noted in 13-08-SUMMARY.md — check it before executing, unrelated to this plan's own scope. + +--- +*Phase: 13-aiui-functional-conversational-node-control-and-content-surf* +*Completed: 2026-08-05* + +## Self-Check: PASSED + +All referenced files found (`backends/ollama.rs`, `history.rs`, this SUMMARY). Both task commit hashes +(`821d8700`, `3da9928c`) verified present in `git log --oneline --all`.