docs(13): capture phase context

This commit is contained in:
archipelago
2026-08-03 08:57:42 -04:00
parent 4a5588c59a
commit 48c7f5d02f
2 changed files with 520 additions and 0 deletions
@@ -0,0 +1,319 @@
# Phase 13: AIUI — Conversational Node Control & Content Surfaces - Context
**Gathered:** 2026-08-03
**Status:** Ready for planning
<domain>
## Phase Boundary
Make the embedded AIUI functional in three directions: (1) **human-language node control**
a typed request in AIUI chat reaches a real node action and returns a real result;
(2) **conversational settings** — system settings reachable by conversation, scoped to what
the user granted; (3) **content surfaces made real** — peer files, music, IndeeHub movies and
owned/paid content rendered live in the design AIUI already has. All of it inside a
**user-granted capability sandbox** that keeps keys, secrets and identity material away from
both the browser and the model.
**Not in scope:** cross-node content distribution with payments (the "archipelago content
source"); wallet spends, seed/key operations, federation trust changes and factory reset as
chat-reachable actions; Nostr integration polish; reviving the dead `ContentPanel.vue`
architecture.
</domain>
<decisions>
## Implementation Decisions
### Where the agent loop lives
- **D-01:** The agent loop (model call → tool call → result → model) runs **node-side in
Rust**. The `archipelago` binary owns the loop, the tool registry, and the model key. AIUI
becomes a thin chat client. Rationale: the key never reaches the browser; tool authorization
sits where session auth already lives; Pine/voice can reuse the same tools later.
**Reversibility:** costly — the RPC surface becomes a contract AIUI, and later the voice
pipeline, are written against; moving the loop browser-side afterwards means re-homing key
handling and re-implementing every tool in TypeScript.
- **D-02:** **One assistant, many front doors.** Extend the existing mesh assistant into a
shared service: one tool registry, one backend selector, one place keys live. Mesh/LoRa,
AIUI chat and (later) Pine voice are callers distinguished by permission scope. Avoids two
divergent security models. Note the existing peer-facing controls — `trusted_only`,
`allowed_contacts`, `denied_askers` — are a per-caller scope mechanism that already exists.
- **D-03:** **Split by nature.** The node-side registry owns everything that reads or changes
the node (system, bitcoin, network, wallet, files, media). The existing `ContextBroker`
keeps only what must run in the browser — `navigate`, `open-app`, `launch-app`, `theme`
and remains the consent surface pushing `permissions:update`. Nothing is discarded; each
side owns what only it can do.
- **D-08:** Chat history lives **node-side in the per-node data dir** (`/var/lib/archipelago`),
inheriting the node's backup, factory-reset and future LUKS story rather than growing a
second sensitive-data location.
### Model backends
- **D-04:** Backend chain is **local Ollama first, with Claude *and* Routstr as fallbacks**.
Node data never leaves the node when a local model is available. The assistant already
reports `ollama_detected` / `claude_available`, so the selection signals exist.
**Routstr (<https://github.com/routstr>) is explicitly in scope at the user's request**
it is an OpenAI-compatible endpoint paid per request in Cashu ecash, with providers, models
and prices discovered over Nostr. All three of those substrates already exist in this
codebase (`core/archipelago/src/streaming/` holds Cashu token handling and the
`list-mints`/`configure-mints` RPCs; Nostr discovery is ADR-003/ADR-006).
- **D-05:** Routstr spending is authorized by a **prepaid budget the user sets**. Inference
spends silently within the allowance, then stops and asks. The ceiling is hard — a
prompt-injected model cannot exceed it.
**Reversibility:** reversible — the ceiling is a config value, not a contract.
- **D-07:** The **local model does get tools**, and every write needs confirmation regardless
of backend. A mis-called tool from a weak local model surfaces as a confirmation prompt the
user rejects, not a wrong action. Consequence: the confirm gate does the safety work, so
**backend choice stays a privacy decision rather than a safety one**.
### Authority and sandboxing
- **D-06:** Tools are a **curated allowlist of hand-written tools** — each with its own
schema, permission category, and destructive/confirm flag. The model never sees the full RPC
surface. No auto-generation from the dispatcher: every capability the chat has must be a
decision someone made, which is the only way the sandbox claim stays true.
**Reversibility:** reversible — adding tools later is additive; the allowlist is the point.
- **D-09:** First-cut authority is **reads within granted categories + app lifecycle
(start/stop/restart) + settings writes**. Explicitly excluded from chat reach: keys, seeds,
wallet spends, federation trust, factory reset. Those stay UI-only.
**Reversibility:** costly — widening later is safe, but any capability shipped and then
withdrawn breaks a behaviour users will have learned.
- **D-10:** **Tool authority never derives from content.** Peer-supplied text (file names,
content descriptions, mesh chat, Nostr posts) enters the context inside explicit
untrusted-content delimiters that mark it as data, not instructions. The tool layer takes
its permissions solely from the user's grants and the confirm gate. An injected "now restart
bitcoin" still has to clear a human confirmation naming the real action. Pattern-stripping
filters were considered and **rejected** as an arms race that reads as a guarantee it isn't.
- **D-11:** Write confirmations render **in neode-ui's trusted chrome, outside the iframe**,
drawn by the host from the node's own description of the pending action — never by AIUI and
never from model-authored text. The iframe cannot spoof, restyle or pre-click it. Uses the
project's mandated Teleport-to-body modal pattern.
**Reversibility:** costly — this is the load-bearing anti-spoofing property; moving the
dialog inside the iframe later would invalidate the threat model, not just the styling.
- **D-16:** All 10 permission categories (`apps`, `system`, `network`, `wallet`, `files`,
`media`, `search`, `ai-local`, `notes`, `bitcoin`) **default closed** on a fresh node.
Nothing is shared with the model until deliberately granted. The assistant looks
unconfigured until the user opens categories — accepted cost.
- **Hard constraint from Phase 10:** the `UNAUTHENTICATED_METHODS` hard-refuse gates and the
loopback/auth boundaries must hold with AIUI on the other side of them. They are not to be
widened to accommodate this phase. See `10-CONTEXT.md` D-01..D-04.
### Content surfaces
- **D-12:** **Feed the existing grids from Archy, replacing the LLM-synth source.** AIUI's
design is kept exactly — `FilmGrid`, `SongGrid`, `NewsGrid`, the detail views — and what
fills them changes: peer files, IndeeHub movies, owned/paid content and node media arrive as
real records instead of being regex-scraped out of model prose.
**Reversibility:** reversible — the grids are prop-driven; the data source behind them is
swappable.
- **D-13:** **Build a real music library** — albums, artists, tracks, tag/metadata extraction,
an index that stays fresh. The user chose this over the narrower MIME-filtered-files option
after being told no library domain exists today. It lands as **its own wave of plans inside
Phase 13, not blocking the rest** — peer files, movies and conversational control ship on
their own track and the library lights up `SongGrid` when ready.
**Reversibility:** one-way — an album/artist/track schema and its on-disk index become a
persisted data model with a migration cost once nodes have indexed libraries; changing the
entity model afterwards needs a reindex path, not just a code change.
- **D-14:** **IndeeHub and peer video are surfaced through the content + paid-unlock
subsystem that already exists** (invoices, `X-Payment-Token`, Range streaming). No new
payment rail. The cross-node "archipelago content source" from the Phase 2 note is deferred
— it is a distribution and payments feature spanning federation, not an AIUI surface.
### Delivery and the two-repo split
- **D-15:** AIUI is **built and shipped with the frontend, versioned and verified** — the
rsync path is kept because it is the one that works, but made deliberate: AIUI's commit
pinned in this repo, `VITE_BASE_PATH=/aiui/` enforced by the build script rather than
remembered, and a post-deploy check that **fetches a live asset** instead of trusting a
directory listing. Making AIUI a signed-catalog app was considered and rejected for this
phase: `*-ui` apps are outside the catalog by design today, and changing that platform rule
mid-phase is its own work.
- **D-17:** AIUI **keeps its standalone mode**; embedded mode delegates to the node. It goes
on working on its own with its own proxy for development and for anyone running it outside a
node; when `embedded=true` it hands the loop, the tools and the key to Archy. The dev loop
stays fast — no node required to work on the UI.
- **D-18:** **Push access to the AIUI repo is confirmed before planning starts**, treated as a
prerequisite rather than discovered mid-plan. Last time this surfaced at execution and left
neode-ui shipping two query params that were inert no-ops against every deployed AIUI build
until a maintainer merged (see `.planning/WINDOWS.md` window 4).
### Claude's Discretion
- What the music library indexes over (own filebrowser `Music` folder, peer audio, or both),
the tag-extraction library, and where the index lives — within D-13's bounds.
- Streaming/token delivery for chat responses; context-window budgeting over node data.
- Which specific tools make the first curated allowlist, within D-09's authority ceiling.
- Routstr provider selection strategy among Nostr-advertised providers.
- Per-category mapping of the 10 permission categories onto individual tools.
</decisions>
<canonical_refs>
## Canonical References
**Downstream agents MUST read these before planning or implementing.**
### The existing AIUI bridge (this is NOT greenfield — read before designing anything)
- `neode-ui/src/types/aiui-protocol.ts` — protocol v1.0.0, `aiui:` message prefix, the
request/response contract. Defines `AIContextCategory` (10 categories) and `AIActionType`
(`install-app | open-app | navigate | launch-app | search-web | read-file | tail-logs`).
- `neode-ui/src/stores/aiPermissions.ts` — the 10 user-toggled permission categories with
labels; `isEnabled` / `toggle`.
- `neode-ui/src/services/contextBroker.ts` — the 624-line origin-scoped postMessage broker
that "checks permissions, fetches data from Pinia stores, sanitizes it (strips sensitive
fields), and responds". The asset D-03 splits.
- `neode-ui/src/services/__tests__/contextBroker.test.ts`, `neode-ui/src/views/__tests__/chatAiuiEmbed.test.ts` — existing coverage to keep green.
- `neode-ui/src/views/Chat.vue` — the iframe embed, `aiuiUrl` construction, origin check, the
`ready` handshake, `allow="microphone"`.
### The existing node-side assistant (the thing D-02 extends)
- `core/archipelago/src/api/rpc/mesh/assistant.rs``mesh.assistant-status` /
`mesh.assistant-configure`; reports `ollama_detected`, `claude_available`, `models`,
`trusted_only`, `allowed_contacts`, `denied_askers`; key at `data_dir/secrets/claude-api-key`.
- `core/archipelago/src/mesh/listener/assist.rs``run_assist`, `is_sender_allowed`,
`call_ollama`, `call_claude`, `cap_reply`. **Q&A only — no tool-calling today.**
- `core/archipelago/src/api/rpc/dispatcher.rs` — the method registry (`mesh.assistant-*` at
~445). **Confirmed: there are no `pine.*` methods** — Pine has no RPC surface.
### Routstr (new integration, user-requested)
- <https://github.com/routstr> — org; `routstr-core`, `routstrd`, `routstr-sdk`, `routstr-chat`.
- <https://docs.routstr.com/> — protocol docs.
- `core/archipelago/src/streaming/` — existing Cashu handling (`gate.rs` verifies/receives
tokens, `pricing.rs`, `session.rs`) and the `streaming.list-mints` / `.configure-mints` RPCs.
Note: currently `#![allow(dead_code)]`, "suppress dead_code until callers land".
### Content subsystem (what D-12/D-14 wire the grids to)
- `core/archipelago/src/content_server.rs``ContentItem` shape (`id`, `filename`,
`mime_type`, `size_bytes`, `description`, `access`, `availability`, `added_at`),
`AccessControl` (`Free | PeersOnly | Paid`), `parse_range_header`, the paid-preview logic
and the ISOBMFF faststart check.
- `core/archipelago/src/api/handler/content.rs``GET /content`, `/content/<id>`,
`/preview`, `/invoice`; Range → 206 with `Content-Range`; 402 body with `price_sats`.
- `core/archipelago/src/api/handler/proxy.rs:188-265` — the peer Range-streaming proxy
(`/api/peer-content/<onion>/<id>`). Its docstring explains why base64 blobs broke seeking.
- `core/archipelago/src/api/rpc/content.rs` — the `content.*` RPCs including
`browse-peer`, `download-peer*`, `preview-peer`; auto-filing by MIME at ~668.
- `neode-ui/src/composables/useAudioPlayer.ts`, `neode-ui/src/components/GlobalAudioPlayer.vue`
— the singleton bottom-bar player. **Audio never opens the lightbox** — enforced in 5 places.
- `neode-ui/src/api/filebrowser-client.ts` — the scoped-token pattern (`app.filebrowser-token`)
that D-01 follows. **Known leak to fix rather than propagate:** `streamUrl` puts the JWT in
the URL query string.
### Prior phase context (locked decisions that constrain this phase)
- `.planning/phases/10-key-material-hardening/10-CONTEXT.md` — D-01..D-04, the
`UNAUTHENTICATED_METHODS` hard-refuse gates. **Must not be widened.**
- `.planning/phases/02-ui-performance/02-CONTEXT.md` — D-14 (the shipped AIUI embed defaults)
and the Deferred Ideas block, which is the origin of this phase.
- `.planning/phases/02-ui-performance/02-AIUI-D14.md` — the embed parameter contract, AIUI's
repo location and branch, and the push-access history.
- `.planning/WINDOWS.md` window 4 — the 403 that made D-18 a prerequisite.
### Project invariants
- `CLAUDE.md` — commit/push discipline, rootless-Podman invariant, the frontend-build verify
rule (grep the built bundle), "verify on the real node before any tag".
- `.planning/PROJECT.md` — ADR-003 (Nostr discovery), ADR-006 (DID-signed, trust tiers),
ADR-008 (dual keys from one seed), ADR-009 (container security).
</canonical_refs>
<code_context>
## Existing Code Insights
### Reusable Assets
- **The permission + consent layer already exists** — 10 categories, a store, a broker that
sanitizes, and tests. This phase extends it rather than inventing it.
- **The assistant already abstracts two model backends** and already holds a key server-side
at `data_dir/secrets/claude-api-key` — the pattern D-01 generalizes.
- **Cashu, Nostr and Lightning are all already in-tree**, which is why Routstr is a smaller
lift here than it would be elsewhere.
- **Range-streaming media delivery is solved** — both own files (filebrowser `/api/raw`) and
peer files (the Rust proxy). The grids need data, not a transport.
### Established Patterns
- Audio belongs to the global bottom-bar player, never the lightbox (enforced in 5 call sites).
- Modals Teleport to body for a full-screen backdrop (project rule, repeatedly reinforced).
- Scoped tokens minted by an authenticated RPC, credentials never reaching the browser.
### Integration Points
- `dispatcher.rs` — where new assistant/tool RPCs register.
- `ContextBroker.handleMessage` — where the browser-only action split (D-03) lands.
- `ChatPage.vue``ContentGridView.vue` → the `*Grid` components — the live render tree the
Archy data must reach (**note `ContentPanel.vue` is dead; do not build through it**).
### Landmines found during scouting (verified, not assumed)
- **AIUI's grids are fed by regex-parsing the model's own reply text** (`updatePanelFromText`
`contentExtraction.ts`), resolving IDs against fixture catalogs that are themselves
injected into the system prompt (`useAI.ts:24-34`). The largest data bucket is
LLM-synthesized, not an API awaiting a base URL.
- **Every "real" data path in AIUI is Vite dev middleware** — all six plugins are
`configureServer`/`configurePreviewServer` only, so they are **absent from a static `dist/`
deploy**. On a node, TMDB posters, web search, RSS and filesystem all 404.
- **`vite-fs.ts:7` hardcodes `PROJECTS_ROOT = '/Users/dorian/Projects'`** — broken on any
other machine, including the Linux dev box.
- **`ContentPanel.vue` is dead code**, taking `ArchyAppsGrid` (the Archy bridge grid),
`FavoritesGrid`, `DiscoverPanel`, `RecipeDetail` and `AppDetail` with it. Clicking a recipe
or an app currently does nothing.
- **`ShareModal.vue`'s mime map omits `m4a`/`aac`/`opus`/`wma`** — those share as
`application/octet-stream`, so they never route to the audio player and are auto-filed to
`Documents` instead of `Music`. Relevant to D-13.
</code_context>
<specifics>
## Specific Ideas
- Routstr was named by the user directly, with the repo link, and asked to be planned in as
part of the backend work — not treated as a future option.
- The sandbox framing is the user's own: "we must sandbox and protect the users sensitive
keys, information, etc whatever they allow access to." The last clause is the design brief —
authority is bounded by what the user allows, not by what the model asks for.
- The origin of this phase is the user's Phase 2 wording: AIUI "talks to the node safely when
permissioned, without leaking data, **using the same command surface as Pine** and everything
else enableable in settings." D-02's shared-service shape is that sentence made concrete.
</specifics>
<deferred>
## Deferred Ideas
- **Cross-node "archipelago content source" with payments** — any IndeeHub install plugs into
every node's content, with payments; same for music. A federation distribution + payments
feature; its own phase (deferred at D-14).
- **AIUI Nostr integration polish** — "make the Nostr integration in AIUI more beautiful."
Carried over from Phase 2's deferred list, still not scoped here.
- **Reviving `ContentPanel.vue` and the plugin-renderer path** — considered and rejected for
this phase; the dead-code inventory should be resolved as cleanup, not as architecture.
- **AIUI's dev-only Vite middleware** (`vite-tmdb`, `vite-rss`, `vite-web-search`, `vite-fs`,
`vite-music-search`, `vite-dev-chats`) — needs a production answer eventually; only the parts
D-12 replaces are in scope now.
- **Pine voice reusing the tool registry** — D-02 makes it possible and is the reason for the
shared-service shape, but wiring the voice pipeline to it is not in this phase.
### Reviewed Todos (not folded)
- *Connected-nodes list must scroll at row-matched height* — keyword match only; belongs to
Phase 1 (UIFIX-02, already complete).
- *Fedimint gateway must not install with a pre-set password* — keyword match only; Phase 1
FED-07 territory.
- *Keep FIPS/Tor pills on cloud files and show them on mobile* — keyword match only; Phase 1
UIFIX-01.
</deferred>
---
*Phase: 13-AIUI — Conversational Node Control & Content Surfaces*
*Context gathered: 2026-08-03*
@@ -0,0 +1,201 @@
# Phase 13: AIUI — Conversational Node Control & Content Surfaces - Discussion Log
> **Audit trail only.** Do not use as input to planning, research, or execution agents.
> Decisions are captured in CONTEXT.md — this log preserves the alternatives considered.
**Date:** 2026-08-03
**Phase:** 13-aiui-functional-conversational-node-control-and-content-surf
**Areas discussed:** Where the tool-calling loop lives, How much authority the chat gets, Content scope and what "music" means, Two-repo split and delivery
---
## Where the tool-calling loop lives
| Option | Description | Selected |
|--------|-------------|----------|
| Node-side in Rust | Binary owns loop, tool registry and key; AIUI is a thin client | ✓ |
| Node proxies the model, AIUI runs the loop | Key stays server-side but tools execute through the ContextBroker | |
| Browser-side in AIUI | AIUI calls the model directly; key lives in the browser | |
**User's choice:** Node-side in Rust
**Notes:** Chosen with the tradeoff stated — this makes the RPC surface a contract AIUI and the later voice pipeline are written against.
| Option | Description | Selected |
|--------|-------------|----------|
| One assistant, many front doors | Extend the existing mesh assistant into a shared service | ✓ |
| Separate subsystem for AIUI | Leave the radio-shaped mesh assistant alone, build beside it | |
| Shared backend, separate authority | Share the key/plumbing, keep tool registries strictly separate | |
**User's choice:** One assistant, many front doors
**Notes:** Directly realizes the user's Phase 2 wording — "the same command surface as Pine".
| Option | Description | Selected |
|--------|-------------|----------|
| Split by nature: node does data+control, broker does UI | Node owns node-touching tools; broker keeps navigate/open-app/theme + consent | ✓ |
| Broker becomes consent-only | Strip back to permissions and theme | |
| Keep the broker as the single front door | Everything forwards through the broker | |
**User's choice:** Split by nature
| Option | Description | Selected |
|--------|-------------|----------|
| Local Ollama when present, Claude as fallback | Node data stays local when a local model exists | ✓ (amended) |
| Claude by default, Ollama opt-in | Best tool-calling reliability, context leaves the node | |
| User picks at setup, no default | Explicit choice, no implicit default | |
**User's choice:** Option 1, **amended by the user** — "but we also want to integrate this as part of it, please plan that too `https://github.com/routstr` so it would be local Ollama or Claude/Routstr as fallback"
**Notes:** Routstr was researched during the discussion rather than assumed: OpenAI-compatible endpoint, Cashu ecash per request, Nostr provider/model/price discovery. All three substrates already exist in-tree, which is why it is a smaller lift here than elsewhere.
| Option | Description | Selected |
|--------|-------------|----------|
| Prepaid budget, auto-spend within it | Hard ceiling a prompt-injected model cannot exceed | ✓ |
| Confirm every paid request | Maximum control, unusable with a multi-call tool loop | |
| Routstr only when explicitly selected | No automatic fallback to a paid path | |
**User's choice:** Prepaid budget the user sets
| Option | Description | Selected |
|--------|-------------|----------|
| Curated allowlist of hand-written tools | Every capability is a deliberate decision | ✓ |
| Auto-generate from the RPC dispatcher | Fast coverage, blast radius = whatever the allowlist forgets | |
| Tiered: curated for writes, generated for reads | Broad reads, hand-written mutations | |
**User's choice:** Curated allowlist of hand-written tools
| Option | Description | Selected |
|--------|-------------|----------|
| Local model gets tools, writes confirmed anyway | Confirm gate does the safety work | ✓ |
| Reads local, writes escalate to the strong model | Undercuts the privacy default at the sensitive moment | |
| Require a tool-capable local model | Honest but costs weak-hardware users the feature | |
**User's choice:** Local model gets tools; every write needs confirmation regardless
**Notes:** Consequence recorded in CONTEXT.md — backend choice becomes a privacy decision, not a safety one.
| Option | Description | Selected |
|--------|-------------|----------|
| Node-side, in the existing per-node data dir | Inherits backup, factory-reset, future LUKS | ✓ |
| Browser-only, never persisted server-side | Nothing accumulates on disk | |
| Ephemeral — no history at all | Strongest privacy, no memory | |
**User's choice:** Node-side in the per-node data dir
---
## How much authority the chat gets
| Option | Description | Selected |
|--------|-------------|----------|
| Reads + app lifecycle + settings writes | Keys, seeds, wallet spends, federation trust, factory reset excluded | ✓ |
| Read-only first | Prove the sandbox before granting power | |
| Full control including wallet and payments | An LLM adjacent to spending authority | |
**User's choice:** Reads within granted categories + app lifecycle + settings writes
| Option | Description | Selected |
|--------|-------------|----------|
| Authority never derives from content; untrusted text fenced and labelled | Injected instructions still face a human confirm | ✓ |
| Keep peer content out of the model entirely | Removes the injection path and much of the appeal | |
| Sanitize and strip suspicious patterns | Rejected as an arms race that reads as a guarantee | |
**User's choice:** Fenced and labelled; authority never derives from content
| Option | Description | Selected |
|--------|-------------|----------|
| In neode-ui's trusted chrome, outside the iframe | Iframe cannot spoof, restyle or pre-click it | ✓ |
| Inside AIUI, styled as part of the conversation | Better feel, drawn by the influenced context | |
| Node-issued confirmation token, UI-agnostic | Strongest and works for voice; more protocol to build | |
**User's choice:** neode-ui's trusted chrome, outside the iframe
| Option | Description | Selected |
|--------|-------------|----------|
| All closed; user opens what they want | Matches the sandbox promise literally | ✓ |
| Low-sensitivity open, sensitive closed | Immediately useful, harder claim to defend | |
| Open on first grant, per-category prompts in context | Just-in-time consent, more moving parts | |
**User's choice:** All 10 categories default closed
---
## Content scope — and what "music" means
Presented alongside verified research findings: AIUI's grids are fed by regex-parsing the model's own reply text against fixture catalogs injected into the system prompt; every "real" data path is Vite dev middleware absent from a static `dist/` deploy; `vite-fs.ts:7` hardcodes `/Users/dorian/Projects`; `ContentPanel.vue` is dead code taking `ArchyAppsGrid`, `FavoritesGrid`, `DiscoverPanel`, `RecipeDetail` and `AppDetail` with it.
| Option | Description | Selected |
|--------|-------------|----------|
| Feed the existing grids from Archy | Keep the design, replace the LLM-synth source | ✓ |
| New Archy-native surfaces alongside | Doubles surface area, splits the design language | |
| Revive ContentPanel and the Archy bridge path | Risks investing in an abandoned architecture | |
**User's choice:** Feed the existing grids from Archy
| Option | Description | Selected |
|--------|-------------|----------|
| Audio files from the two transports you already have | MIME-filtered files, no new entities; folds in the m4a/aac/opus mime bug | |
| Build a real library — albums, artists, metadata | A substantial backend domain | ✓ |
| Leave music to wavlake, wire only files and video | Music already works in prod against wavlake | |
**User's choice:** Build a real library
**Notes:** Chosen after being told explicitly that no library domain exists today and that it deserves its own phase. Concern raised once, user decided, proceeded — sequencing handled by the follow-up below.
| Option | Description | Selected |
|--------|-------------|----------|
| Its own plans inside Phase 13, not blocking the rest | Phase still delivers if the library runs long | ✓ |
| Library first — the rest follows | Cleanest data model, delays everything visible | |
| Split it into its own phase | Its own discussion round | |
**User's choice:** Its own non-blocking wave inside Phase 13
| Option | Description | Selected |
|--------|-------------|----------|
| Surface this node's + peers' existing content; no new payment rail | Uses the invoice/X-Payment-Token/Range flow that exists | ✓ |
| Include the cross-node content source with payments | The full Phase 2 vision; a federation distribution feature | |
| Movies out of scope this phase | Narrowest cut | |
**User's choice:** Surface existing content through the existing paid-unlock subsystem
---
## Two-repo split and delivery
| Option | Description | Selected |
|--------|-------------|----------|
| Built and shipped with the frontend, versioned and verified | Pin the commit, enforce the base path, fetch a live asset to verify | ✓ |
| Make AIUI a real catalog app | Architecturally right; changes a platform rule mid-phase | |
| Vendor AIUI's build output into this repo | One artifact, loses source separation | |
**User's choice:** Built and shipped with the frontend, versioned and verified
| Option | Description | Selected |
|--------|-------------|----------|
| Keep standalone; embedded mode delegates to the node | Dev loop stays fast, no node needed to work on the UI | ✓ |
| Embedded-only from here | Less surface, loses AIUI's independent life | |
| Standalone with the node as an optional backend | "Optional" risks the secure path being the forgotten one | |
**User's choice:** Keep standalone; embedded mode delegates
| Option | Description | Selected |
|--------|-------------|----------|
| Confirm push access before planning starts | Treats it as a prerequisite, not a mid-plan discovery | ✓ |
| Work on a branch, hand merges to a maintainer | Human gate mid-phase, same inert-until-merged risk | |
| Plan the archy side to degrade gracefully | Robust, but designs for a half-landed state throughout | |
**User's choice:** Confirm push access before planning starts
---
## Claude's Discretion
- What the music library indexes over, the tag-extraction library, and where the index lives.
- Streaming/token delivery for chat responses; context-window budgeting over node data.
- Which specific tools make the first curated allowlist, within the authority ceiling.
- Routstr provider selection among Nostr-advertised providers.
- Per-category mapping of the 10 permission categories onto individual tools.
## Deferred Ideas
- Cross-node "archipelago content source" with payments (federation distribution feature).
- AIUI Nostr integration polish (carried from Phase 2's deferred list).
- Reviving `ContentPanel.vue` and the plugin-renderer path — cleanup, not architecture.
- A production answer for AIUI's dev-only Vite middleware beyond what this phase replaces.
- Wiring Pine's voice pipeline to the shared tool registry.