docs(13-11): complete music library + share MIME fix plan
This commit is contained in:
@@ -79,7 +79,7 @@ declared exit criteria (multinode pass + workstreams B/C/F), `.planning/codebase
|
||||
|
||||
- [x] **AIUI-01**: Human-language node control — a typed request in AIUI chat ("restart bitcoin", "how much space is left", "who's connected") reaches a real node action and returns a real result, over a permissioned tool-calling bridge rather than raw RPC
|
||||
- [ ] **AIUI-02**: Conversational settings — the system settings surfaced across neode-ui become reachable by conversation, scoped to what the user has granted
|
||||
- [ ] **AIUI-03**: Content surfaces made real — AIUI's designed-but-empty content views render live node data (peer files, music, IndeeHub movies, owned/paid content); audio belongs to the global bottom-bar player and media streams via Range requests, never base64 blobs
|
||||
- [x] **AIUI-03**: Content surfaces made real — AIUI's designed-but-empty content views render live node data (peer files, music, IndeeHub movies, owned/paid content); audio belongs to the global bottom-bar player and media streams via Range requests, never base64 blobs
|
||||
- [x] **AIUI-04**: Sandboxed by construction, permissioned by the user — secrets never reach the browser or the model context; the chat gets an explicit, user-granted, default-closed, revocable capability scope; destructive and identity-touching operations are human-confirmed; tool authority never derives from peer-controlled content (BLOCKER)
|
||||
- [ ] **AIUI-05**: Delivery and build — AIUI reaches nodes on a delivery path an operator can actually receive updates through, with `VITE_BASE_PATH=/aiui/` enforced by the build script so a hand-built bundle cannot ship a black page
|
||||
- [ ] **AIUI-06**: Verified on device — in the real embedded iframe on archi-dev-box, mobile included, not only in the local `dev:mock` loop
|
||||
@@ -155,7 +155,7 @@ Which phases cover which requirements. Updated during roadmap creation.
|
||||
| MKT-04 | Phase 8 | Pending |
|
||||
| AIUI-01 | Phase 13 | Complete |
|
||||
| AIUI-02 | Phase 13 | Pending |
|
||||
| AIUI-03 | Phase 13 | Pending |
|
||||
| AIUI-03 | Phase 13 | Complete |
|
||||
| AIUI-04 | Phase 13 | Complete |
|
||||
| AIUI-05 | Phase 13 | Pending |
|
||||
| AIUI-06 | Phase 13 | Pending |
|
||||
|
||||
@@ -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:** 10/15 plans executed
|
||||
**Plans:** 11/15 plans executed
|
||||
|
||||
Plans:
|
||||
|
||||
@@ -370,7 +370,7 @@ Plans:
|
||||
|
||||
- [x] 13-09-PLAN.md — AIUI delivery: enforced build, pinned commit, live-asset verify, iframe sandbox mechanism (AIUI-04/05)
|
||||
- [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)
|
||||
- [x] 13-11-PLAN.md — SongGrid lit from the real library + the m4a/aac/opus/wma share-mime fix (AIUI-03)
|
||||
|
||||
**Wave 5**
|
||||
|
||||
|
||||
+10
-7
@@ -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-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"
|
||||
stopped_at: "Completed 13-11-PLAN.md (music library SongGrid wiring + ShareModal MIME fix + GAP-FOUND live-trigger fix). Next: wave 4 has 13-11 as its only plan (terminal per D-13) — remaining phase-13 plans are waves 5-8 (13-12..13-15)."
|
||||
last_updated: "2026-08-05T22:34:57.971Z"
|
||||
last_activity: 2026-08-05
|
||||
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: 48
|
||||
completed_plans: 49
|
||||
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: 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)
|
||||
Plan: 11 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-10 complete (D-04 Ollama tool-calling backend + D-08 node-side chat history)
|
||||
|
||||
Progress: [████████░░] 80%
|
||||
Progress: [████████░░] 82%
|
||||
|
||||
## Performance Metrics
|
||||
|
||||
@@ -67,6 +67,7 @@ Progress: [████████░░] 80%
|
||||
| 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 |
|
||||
| Phase 13 P11 | 27min | 3 tasks | 12 files |
|
||||
|
||||
## Accumulated Context
|
||||
|
||||
@@ -139,6 +140,8 @@ Decisions are logged in PROJECT.md (10 locked ADRs in the `<decisions>` block +
|
||||
- [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
|
||||
- [Phase ?]: 13-11: kind:'library' content:request routes to music.list-tracks (not content.*) via a new contextBroker.ts fetchLibraryContent branch — Rule 2 deviation, contextBroker.ts's diff-clean acceptance criterion could not hold alongside genuine music.* wiring (ContentItem has no artist/album/duration field at all)
|
||||
- [Phase ?]: 13-11: closed the plan's GAP-FOUND must_have — useArchy.ts's init() now fires requestArchyContent + requestArchyLibrary automatically as a live init-time event, and useContentPanel.ts's setArchyContent opens the panel/sets tabs for non-empty content, instead of leaving the fetch merely callable with nothing in the UI ever invoking it (13-06's own documented Known Limitation)
|
||||
|
||||
### Pending Todos
|
||||
|
||||
@@ -216,8 +219,8 @@ The 5x lifecycle gate was NOT run.
|
||||
|
||||
## Session Continuity
|
||||
|
||||
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
|
||||
Last session: 2026-08-05T22:34:57.917Z
|
||||
Stopped at: Completed 13-11-PLAN.md (music library SongGrid wiring + ShareModal MIME fix + GAP-FOUND live-trigger fix). Next: wave 4 has 13-11 as its only plan (terminal per D-13) — remaining phase-13 plans are waves 5-8 (13-12..13-15).
|
||||
`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,
|
||||
|
||||
+194
@@ -0,0 +1,194 @@
|
||||
---
|
||||
phase: 13-aiui-functional-conversational-node-control-and-content-surf
|
||||
plan: 11
|
||||
subsystem: ui
|
||||
tags: [vue, typescript, postmessage, content-adapter, music, mime, gap-found]
|
||||
|
||||
requires:
|
||||
- phase: 13-aiui-functional-conversational-node-control-and-content-surf
|
||||
provides: "13-06's archyContentAdapter.ts conventions (classifyByMime, sortDeterministic, source-badge literals) and content:request/content:push channel; 13-07's music.* RPC surface (music.list-tracks) and 13-MUSIC-MODEL.md's entity model"
|
||||
provides:
|
||||
- "archyContentAdapter.ts: adaptLibraryTracks/adaptLibraryAlbums mapping music.list-tracks records onto AIUI's Song shape with real tag-extracted metadata (title/artist/album/duration), order-preserving, no cover art, no credential-bearing URLs"
|
||||
- "ShareModal.vue: SHARE_MIME_MAP gains m4a/aac/opus/wma -> real audio/* MIME types (was application/octet-stream), extracted to a testable module-scope export"
|
||||
- "contextBroker.ts: kind:'library' branch (fetchLibraryContent) routing to music.list-tracks instead of content.* — the one addition 13-06's kind discriminator was built to absorb"
|
||||
- "useArchy.ts: requestArchyLibrary(scope), and init() now fires requestArchyContent + requestArchyLibrary automatically as a live init-time event (GAP-FOUND fix) instead of leaving them merely callable"
|
||||
- "useContentPanel.ts: setArchyContent now opens the panel and populates availableTabs/activeTab/panelTitle when Archy supplied non-empty content, so it actually renders instead of sitting populated-but-invisible"
|
||||
affects: [13-15]
|
||||
|
||||
tech-stack:
|
||||
added: []
|
||||
patterns:
|
||||
- "Named exports from a .vue SFC's top-level <script> block (shared module scope with <script setup>) for fixture-testable constants without changing runtime behavior"
|
||||
- "kind discriminator on the existing content:request/content:push channel routes to a different node-side RPC family (music.* vs content.*) based on kind, not just a data-shape label — the design 13-06 intended, made concrete"
|
||||
|
||||
key-files:
|
||||
created:
|
||||
- neode-ui/src/components/cloud/__tests__/ShareModal.test.ts
|
||||
- aiui/packages/app/src/composables/__tests__/useArchy.test.ts
|
||||
modified:
|
||||
- neode-ui/src/composables/archyContentAdapter.ts
|
||||
- neode-ui/src/composables/__tests__/archyContentAdapter.test.ts
|
||||
- neode-ui/src/components/cloud/ShareModal.vue
|
||||
- neode-ui/src/types/aiui-protocol.ts
|
||||
- neode-ui/src/services/contextBroker.ts
|
||||
- neode-ui/src/services/__tests__/contextBroker.test.ts
|
||||
- aiui/packages/app/src/services/archyBridge.ts
|
||||
- aiui/packages/app/src/composables/useArchy.ts
|
||||
- aiui/packages/app/src/composables/useContentPanel.ts
|
||||
- aiui/packages/app/src/composables/__tests__/useContentPanel.test.ts
|
||||
|
||||
key-decisions:
|
||||
- "adaptLibraryTracks resolves own-library playback URLs through the EXISTING FileBrowser raw-file route (/app/filebrowser/api/raw<path>, the T-13-39 fix from 13-06) by locating the '/filebrowser/' segment in TrackId's absolute canonicalized path, and peer tracks through the EXISTING Range-streaming proxy (/api/peer-content/<onion>/<content_id>) — no new endpoint minted, matching the plan's own instruction"
|
||||
- "adaptLibraryAlbums groups already-adapted Songs by (album_artist, album) client-side, exported for shape completeness (mirrors adaptToPodcast's status from 13-06) — not consumed by this plan's own wiring since SongGrid renders a flat track list, but real and tested for a future album-detail view"
|
||||
- "Deviation (Rule 2, documented in full below): contextBroker.ts gained a one-branch kind==='library' dispatch (fetchLibraryContent) despite the plan's literal acceptance criterion requiring its diff to stay clean — genuine music.list-tracks wiring is structurally impossible without it, since content.* has no field for tag-extracted artist/album/duration at all"
|
||||
- "requestArchyLibrary reuses archyBridge.requestArchyContent (kind:'library') rather than a new bridge method — 'using the same bridge call with the library kind' per the plan's own Task 3 instruction; archyBridge.ts's kind param widened to match, same as aiui-protocol.ts's AIUIContentRequest.kind union"
|
||||
- "init() calls requestArchyContent('all','own') and requestArchyLibrary('own') fire-and-forget immediately after archyBridge.init() — closes the GAP-FOUND must_have: the fetch now fires from a real init-time UI event, not merely from a direct unit-test call"
|
||||
- "setArchyContent computes availableTabs/activeTab/panelOpen from non-empty buckets only — an empty/ungranted library never force-opens the panel, but real content now becomes visible without any further chat turn"
|
||||
|
||||
requirements-completed: [AIUI-03]
|
||||
|
||||
coverage:
|
||||
- id: D1
|
||||
description: "adaptLibraryTracks maps music.list-tracks records onto Song with real title/artist/album/duration, artist-tag-absent fallback to album_artist then '', order preserved from the index's own deterministic sort, no cover-art URL, peer-vs-own source distinction via the pinned funkwhale/plex literals, no credential in any produced URL, empty library -> []"
|
||||
requirement: AIUI-03
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "neode-ui/src/composables/__tests__/archyContentAdapter.test.ts (34/34 passing, includes adaptLibraryTracks/adaptLibraryAlbums describe blocks, one test per behavior bullet)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D2
|
||||
description: "ShareModal.vue's SHARE_MIME_MAP maps all eight audio extensions (mp3/flac/ogg/wav/m4a/aac/opus/wma) to a real audio/* MIME, agrees with archyContentAdapter.ts's classifyByMime and content.rs's prefix-only auto-filing check, unknown extensions still fall back to the generic binary type"
|
||||
requirement: AIUI-03
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "neode-ui/src/components/cloud/__tests__/ShareModal.test.ts (13/13 passing, fixture-table convention matching useFileType.test.ts)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D3
|
||||
description: "kind:'library' content:request routes to music.list-tracks (not content.list-mine) and adapts the result into the songs bucket, gated on the same media/files permission check as every other content:request; degrades to an empty songs bucket on RPC failure rather than throwing"
|
||||
requirement: AIUI-03
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "neode-ui/src/services/__tests__/contextBroker.test.ts (21/21 passing, including the two new kind:'library' tests)"
|
||||
status: pass
|
||||
human_judgment: false
|
||||
- id: D4
|
||||
description: "AIUI's useArchy.ts init() fires requestArchyContent + requestArchyLibrary as a live init-time event, and requestArchyLibrary sends a content:request with kind:'library' over the real postMessage bridge — the GAP-FOUND fix (content actually appears without anyone typing a magic phrase)"
|
||||
requirement: AIUI-03
|
||||
verification:
|
||||
- kind: unit
|
||||
ref: "aiui/packages/app/src/composables/__tests__/useArchy.test.ts (3/3 passing) + useContentPanel.test.ts's new setArchyContent-visibility describe block (3/3 passing)"
|
||||
status: pass
|
||||
human_judgment: true
|
||||
rationale: "The unit tests prove the postMessage is sent and the panel/tab-bar state updates correctly for a synthetic bundle — they cannot prove a real node's music index actually produces non-empty tracks, or that a human visually sees SongGrid populate on a live embedded AIUI session. That end-to-end visual confirmation needs the phase's own node-verification step (CLAUDE.md's 'verify on the real node before any tag'), which is out of this frontend-only plan's execution."
|
||||
|
||||
duration: ~27min
|
||||
completed: 2026-08-05
|
||||
status: complete
|
||||
---
|
||||
|
||||
# Phase 13 Plan 11: AIUI Music Library + Share MIME Fix Summary
|
||||
|
||||
**`adaptLibraryTracks`/`adaptLibraryAlbums` map `music.list-tracks`'s real tag-extracted metadata onto `SongGrid`'s `Song` shape, `ShareModal.vue`'s MIME map stops filing m4a/aac/opus/wma as Documents, and — closing the plan's own GAP-FOUND must_have — `requestArchyContent`/`requestArchyLibrary` now fire automatically from a live `useArchy.ts` init-time event instead of sitting merely callable with nothing in the UI ever invoking them.**
|
||||
|
||||
## Performance
|
||||
|
||||
- **Duration:** ~27 min
|
||||
- **Started:** 2026-08-05T18:06:00-04:00 (approx, immediately after 13-10's completion)
|
||||
- **Completed:** 2026-08-05T18:33:00-04:00
|
||||
- **Tasks:** 3/3 completed (the plan file has 3 tasks, not 4)
|
||||
- **Files modified:** 12 (2 created, 10 modified)
|
||||
|
||||
## Accomplishments
|
||||
|
||||
- `archyContentAdapter.ts`: `adaptLibraryTracks`/`adaptLibraryAlbums` map `music.list-tracks`'s wire records (`core/archipelago/src/music/mod.rs::Track`, 13-07) onto AIUI's `Song` shape — real `title`/`artist`/`album`/`duration` from extracted tags, not the filename-derived guesses `adaptToSong` produces for generic `ContentItem`s. Artist falls back to album artist then `''`, never `null`/`undefined`. Ordering is preserved from the RPC's own deterministic sort (never re-sorted browser-side, honoring 13-07's `(disc, track, title)` comparator). No cover-art URL is ever produced (`Track` carries no artwork field) — `SongGrid`'s existing no-artwork state renders. Own-library tracks resolve through the existing FileBrowser raw-file route; peer tracks through the existing Range-streaming proxy. No produced URL ever carries a credential in its query string.
|
||||
- `ShareModal.vue`: the four missing audio extensions (`m4a`/`aac`/`opus`/`wma`) now map to real `audio/*` MIME types instead of falling through to `application/octet-stream` — they now route to the global bottom-bar player (never the lightbox) and auto-file to Music instead of Documents. The map was extracted from a local `const` inside `save()` to an exported, module-scope `SHARE_MIME_MAP` so it is directly fixture-testable, matching `useFileType.test.ts`'s convention. Cross-checked against `classifyByMime` (13-06) and `content.rs`'s auto-filing check (prefix-only `starts_with("audio/")`) — all three agree on all eight extensions.
|
||||
- Closed the plan's own explicit GAP-FOUND must_have: `useArchy.ts`'s `init()` now calls `requestArchyContent('all', 'own')` and the new `requestArchyLibrary('own')` automatically, fire-and-forget, immediately after `archyBridge.init()` — a real init-time UI event, not a function nobody calls. `useContentPanel.ts`'s `setArchyContent` also now opens the panel and populates `availableTabs`/`activeTab`/`panelTitle` when Archy supplied non-empty content, so real data doesn't sit fully populated in refs while the tab bar stays closed.
|
||||
- `requestArchyLibrary(scope)` — sibling of 13-06's `requestArchyContent`, same bridge call, `kind: 'library'` — routes to `music.list-tracks` node-side instead of `content.*`, since a library track carries real tag-extracted metadata `ContentItem` has no field for at all.
|
||||
- Zero changes to `SongGrid.vue`, `ContentGridView.vue`, or `aiui/packages/core/src/types/content.ts` (D-12) — verified by `git diff --exit-code`.
|
||||
|
||||
## Task Commits
|
||||
|
||||
Each task was committed atomically:
|
||||
|
||||
1. **Task 1: Map the library onto the Song shape the grid already renders** - `7bea8f6b` (feat)
|
||||
2. **Task 2: Four missing audio types — the AAC family stops being filed as Documents** - `abe77ebe` (fix)
|
||||
3. **Task 3: AIUI asks for the library the same way it asks for content** - `d25aea12` (feat)
|
||||
|
||||
**Plan metadata:** this commit (docs: complete plan) — pending, see below.
|
||||
|
||||
## Files Created/Modified
|
||||
|
||||
- `neode-ui/src/composables/archyContentAdapter.ts` — `adaptLibraryTracks`, `adaptLibraryAlbums`, `ArchyLibraryTrack`, `ArchyLibraryAlbum`, `ArchyMusicSource`, `buildLibraryTrackUrl`, `libraryTrackId`
|
||||
- `neode-ui/src/composables/__tests__/archyContentAdapter.test.ts` — 15 new tests, one per `<behavior>` bullet plus own-library/peer URL-shape pins
|
||||
- `neode-ui/src/components/cloud/ShareModal.vue` — `SHARE_MIME_MAP` extracted to a module-scope export in a new top-level `<script>` block, gains 4 audio entries
|
||||
- `neode-ui/src/components/cloud/__tests__/ShareModal.test.ts` (new) — fixture-table test over all 8 audio extensions + 1 unknown extension + cross-map agreement + lightbox-routing predicate
|
||||
- `neode-ui/src/types/aiui-protocol.ts` — `AIUIContentRequest.kind` gains the `'library'` literal
|
||||
- `neode-ui/src/services/contextBroker.ts` — `fetchLibraryContent()`, one `kind === 'library'` branch in `handleContentRequest`
|
||||
- `neode-ui/src/services/__tests__/contextBroker.test.ts` — 2 new tests for `kind: 'library'` (success + RPC-failure degradation)
|
||||
- `aiui/packages/app/src/services/archyBridge.ts` — `requestArchyContent`'s `kind` param widened to include `'library'`
|
||||
- `aiui/packages/app/src/composables/useArchy.ts` — `requestArchyLibrary(scope)`, `init()` fires both content/library fetches automatically
|
||||
- `aiui/packages/app/src/composables/useContentPanel.ts` — `setArchyContent` now sets `availableTabs`/`activeTab`/`panelTitle`/`panelOpen` when non-empty
|
||||
- `aiui/packages/app/src/composables/__tests__/useContentPanel.test.ts` — 3 new tests for the visibility behavior
|
||||
- `aiui/packages/app/src/composables/__tests__/useArchy.test.ts` (new) — 3 tests pinning the init-time auto-trigger and `requestArchyLibrary`'s message shape
|
||||
|
||||
## Decisions Made
|
||||
|
||||
See `key-decisions` in frontmatter for the full list. The one requiring the most explanation:
|
||||
|
||||
**Playback URL construction for library tracks.** `TrackId.path` (13-07) is an absolute, canonicalized filesystem path, not a URL or a FileBrowser-relative path. For `MusicSource::OwnLibrary` tracks, `buildLibraryTrackUrl` locates the `/filebrowser/` path segment and takes everything after it as the FileBrowser-relative path, then builds `/app/filebrowser/api/raw/<that path>` — the exact same route `filebrowser-client.ts`'s `streamUrl` already serves (the T-13-39 credential-in-URL fix from 13-06), reused rather than reinvented. For `MusicSource::Peer { onion }` tracks, the `onion` comes directly from the wire `MusicSource`, and the content id is the path's basename (`purchased-content/<onion>/<content_id>`'s own layout, 13-07) — resolving through the existing `/api/peer-content/<onion>/<id>` Range-streaming proxy, mirroring `buildMediaUrl`'s peer branch above it in the same file.
|
||||
|
||||
## Deviations from Plan
|
||||
|
||||
### Auto-fixed Issues
|
||||
|
||||
**1. [Rule 2 - Missing critical functionality] `contextBroker.ts` needed a `kind === 'library'` dispatch branch, despite the plan's literal acceptance criterion requiring its diff to stay clean**
|
||||
|
||||
- **Found during:** Task 3, wiring `requestArchyLibrary` end to end.
|
||||
- **Issue:** The plan's Task 1/3 acceptance criteria and top-level `<verification>` block both state `cd neode-ui && git diff --exit-code -- src/services/contextBroker.ts` must exit 0 — i.e. this file must be completely untouched by the whole plan. But `contextBroker.ts`'s existing `handleContentRequest`/`fetchAdaptedContent` (13-06) never branches on `kind` for RPC selection — every `content:request`, regardless of `kind`, calls `content.list-mine`/`content.browse-peer`/`content.owned-list`. Those RPCs return `ContentItem`, which has **no field at all** for tag-extracted `artist`/`album`/`duration` — `adaptToSong` (13-06) always sets `artist: ''`. So a `kind: 'library'` request sent through the untouched broker would silently resolve to the same `content.*` data every other kind gets, and `SongGrid` would never receive real library metadata no matter how correct `adaptLibraryTracks` itself is. This is precisely the failure mode this plan's own GAP-FOUND must_have exists to prevent: "complete, green-tested machinery and empty grids... every unit test passes and the feature visibly does not work."
|
||||
- **Fix:** Added exactly one branch to `handleContentRequest` (`kind === 'library' ? fetchLibraryContent() : fetchAdaptedContent(...)`) and one new private method `fetchLibraryContent()` that calls `music.list-tracks` and maps the result via `adaptLibraryTracks`. No second postMessage channel, no new message type, no new `window.addEventListener` — the existing `content:request`/`content:push` channel and its `kind` discriminator carry this exactly as 13-06's own doc comment says they were built to: *"so 13-11's music-library wave can extend `kind` without touching this file again"* — extending `kind`'s *values* (`aiui-protocol.ts`) turned out to require exactly one line of routing logic inside the file the values are interpreted by, which is a different and much narrower claim than "this file will never change."
|
||||
- **Files modified:** `neode-ui/src/services/contextBroker.ts`, `neode-ui/src/types/aiui-protocol.ts` (widened the `kind` union), `aiui/packages/app/src/services/archyBridge.ts` (widened the matching `kind` param).
|
||||
- **Verification:** `contextBroker.test.ts` 21/21 passing (2 new tests: success path calls `music.list-tracks` not `content.list-mine`; RPC-failure path degrades to an empty songs bucket, matching `fetchAdaptedContent`'s own error handling). Whole neode-ui suite 926/926. `vue-tsc -b` clean.
|
||||
- **Committed in:** `d25aea12` (Task 3 commit), logged to `.planning/WINDOWS.md` as a `deviation` entry (append failed due to a pre-existing malformed ledger entry unrelated to this plan — id 18 is missing its `reason` field; the ledger append call errored on validating the *whole* ledger before writing, not on this entry. Not fixed here per the Scope Boundary rule — out of this plan's `files_modified` and owned by whichever plan wrote entry 18).
|
||||
|
||||
**2. [Rule 2 - Missing critical functionality] `useContentPanel.ts`'s `setArchyContent` needed to open the panel / set tabs, not just populate data refs**
|
||||
|
||||
- **Found during:** Task 3, verifying the GAP-FOUND must_have actually holds end to end.
|
||||
- **Issue:** `setArchyContent` (13-06) only ever set `panelFilms`/`panelSongs`/`panelPodcasts` and `archyContentActive`. `availableTabs`/`activeTab`/`panelOpen` were untouched — only `updatePanelFromText`'s regex path ever set them (13-06's own documented Known Limitation). Even with `requestArchyContent`/`requestArchyLibrary` now firing automatically (this plan's main fix), the fetched data would populate refs that `ContentGridView.vue`'s `v-if="panelOpen && hasGridContent"` never renders, because nothing ever opened the panel or added a `song`/`film` tab for it. This is the same "content actually APPEARS... without anyone typing a magic phrase" truth the plan's must_haves state explicitly — populating the refs alone doesn't satisfy it.
|
||||
- **Fix:** `setArchyContent` now computes `availableTabs` (`film`/`song`/`podcast`, whichever are non-empty, plus `prompt`), sets `activeTab` to the first non-empty bucket, sets a real `panelTitle`, and opens the panel — but **only** when at least one bucket is non-empty, so an empty library or an ungranted permission never force-opens the panel on every mount.
|
||||
- **Files modified:** `aiui/packages/app/src/composables/useContentPanel.ts`.
|
||||
- **Verification:** `useContentPanel.test.ts` 12/12 passing (3 new tests: opens + tabs for non-empty songs; both film and song tabs together; does NOT force-open on an empty bundle). Full `aiui/packages/app` suite: 341/344 (3 pre-existing failures, see Issues Encountered).
|
||||
- **Committed in:** `d25aea12` (Task 3 commit).
|
||||
|
||||
---
|
||||
|
||||
**Total deviations:** 2 auto-fixed (both Rule 2 — missing critical functionality the plan's own GAP-FOUND must_have required, discovered while making the wiring genuinely end-to-end rather than merely present). No scope creep — both were necessary for the plan's own explicitly-stated intent (the GAP-FOUND override) to actually hold, and both are additive, narrowly-scoped changes with tests.
|
||||
**Impact on plan:** The literal "contextBroker.ts diff clean" acceptance criterion could not be satisfied simultaneously with genuine `music.*` wiring — the two are structurally in tension for any design where `kind: 'library'` must reach a different RPC than every other `kind`. Real, tested, end-to-end wiring was prioritized over the literal grep, exactly as 13-06's own precedent (the `archyBridge.ts` deviation, documented in `13-06-SUMMARY.md`) established for this same class of conflict.
|
||||
|
||||
## Issues Encountered
|
||||
|
||||
- **`grep -c` counts matching lines, not occurrences.** The plan's acceptance criteria (`grep -cE "m4a:|aac:|opus:|wma:" ShareModal.vue` returns 4) would return 1 if all four entries were written on one line (as the pre-existing `mp3:|flac:|ogg:|wav:` entries already were — that criterion would have returned 1 even in the pre-13-11 ground truth). Reformatted `SHARE_MIME_MAP` to one extension per line so both grep criteria return 4 literally, rather than relying on a documented false-positive.
|
||||
- **The `ContentPanel` grep false-positive from 13-06 recurs here.** `grep -c 'ContentPanel' useArchy.ts` returns 4 (not 0) purely because the substring `"ContentPanel"` appears inside `"useContentPanel"` (the correct, required import). Verified via the same targeted grep 13-06 used: `grep -c "ContentPanel\.vue\|ArchyAppsGrid\|FavoritesGrid\|DiscoverPanel\|RecipeDetail\|AppDetail" useArchy.ts` returns `0` — the dead `ContentPanel.vue` component and its siblings were not revived.
|
||||
- **3 pre-existing `aiui/packages/app` test failures, unrelated to this plan**, already documented in `13-06-SUMMARY.md` and `13-10-SUMMARY.md`: `seed-conversations.test.ts` and `seedExtraction.test.ts` (song-count mismatch, 6 vs 10 expected) and `useAI.test.ts`'s web-search-integration test. Confirmed unrelated: `aiui/packages/app` was 332/335 before 13-11 (13-06's own recorded baseline) with the exact same 3 named failures; after 13-11's 6 new tests it's 341/344 — the delta is entirely additive.
|
||||
- **`windows append` for the contextBroker.ts deviation failed** due to a pre-existing malformed `.planning/WINDOWS.md` ledger entry (id 18, missing its `reason` field, committed in `de058bac` before this plan started) — the append command validates the whole ledger before writing. Not fixed here (out of scope, not owned by this plan); the deviation is fully documented above and in this SUMMARY's frontmatter instead. Per the executor's own instructions, the ledger is best-effort and its unavailability does not block execution.
|
||||
- **A benign race exists between the two init-time fire-and-forget calls.** `requestArchyContent('all','own')` and `requestArchyLibrary('own')` both call `setArchyContent`, and `requestArchyLibrary` reads `panel.panelFilms.value`/`panel.panelPodcasts.value` at its own resolution time to avoid clobbering them with `[]`. If `requestArchyLibrary` resolves before `requestArchyContent`, films/podcasts are transiently `[]` until the content call resolves and corrects them — a fleeting intermediate state, not a lost-data bug (both calls always converge to the correct final state), but worth naming honestly rather than silently.
|
||||
|
||||
## User Setup Required
|
||||
|
||||
None — no external service configuration required by this plan.
|
||||
|
||||
## Next Phase Readiness
|
||||
|
||||
- `SongGrid` will render a real node's indexed library the moment a node has one (13-07's `music.list-tracks` is live; this plan is the browser-side consumer). No music tool was added to the assistant's curated registry — deliberately out of scope, matching 13-07's own scoping (track independence, D-13).
|
||||
- **This plan is terminal by design (D-13, wave 4)** — nothing lists `13-11` in `depends_on`. No control-track or content-track plan, and not `13-15`'s phase-closing gate, depends on it. If node-level playback verification (streaming an actual `.flac`/`.m4a` file through the FileBrowser raw route, or the peer proxy) surfaces an issue, it does not block the rest of the phase — `13-15` records this plan's result as a best-effort input, per D-13's own text.
|
||||
- **Node-level verification is still open** (this is a frontend-only plan, no `cargo` was run): whether a real node's `music.list-tracks` response, once fed through `adaptLibraryTracks` and the new `content:request`/`kind:'library'` path, actually plays back through `/app/filebrowser/api/raw<path>` requires a live node with an indexed library and the FileBrowser session cookie set — the same category of gap 13-06's `streamUrl` fix carried (D2 in `13-06-SUMMARY.md`), now extended to library tracks. CLAUDE.md's "verify on the real node before any tag" gate is the mechanism that closes this; it is out of this plan's execution.
|
||||
- The `useFileType.ts` `opus`-extension gap (Cloud-view icon/badge classification, cosmetic only) is logged in `deferred-items.md`, not fixed — out of Task 2's named three-map scope.
|
||||
|
||||
---
|
||||
*Phase: 13-aiui-functional-conversational-node-control-and-content-surf*
|
||||
*Completed: 2026-08-05*
|
||||
|
||||
## Self-Check: PASSED
|
||||
|
||||
All 12 created/modified source files verified present on disk; all 3 task commits (`7bea8f6b`,
|
||||
`abe77ebe`, `d25aea12`) verified present in `git log --oneline --all`. No missing items.
|
||||
+20
@@ -54,3 +54,23 @@ Out-of-scope discoveries found while executing this phase. Logged, not fixed.
|
||||
- Why deferred: cosmetic, does not block 13-08's confirm-gate checkpoint; needs on-device
|
||||
mobile + companion reproduction to fix honestly (feedback_test_before_claiming_fixed).
|
||||
- Suggested owner: fold into 13-10/13-11 (wave 4 AIUI work) or a verify-work gap plan.
|
||||
|
||||
## Discovered during 13-11 Task 2 (ShareModal MIME map): a FOURTH audio-extension map also disagrees
|
||||
|
||||
- Discovered: 2026-08-05, while cross-checking `ShareModal.vue`'s MIME map against
|
||||
`classifyByMime` and `content.rs` per Task 2's own instruction.
|
||||
- `neode-ui/src/composables/useFileType.ts`'s `AUDIO_EXTS` set (`mp3, flac, wav, ogg, aac, m4a,
|
||||
wma`) is missing `opus` — the same class of gap Task 2 fixed in `ShareModal.vue`, in a fourth
|
||||
place. This map drives Cloud-view file icons/badges/category labels (`getFileCategory`,
|
||||
`useFileType`), not MIME typing or share/playback routing, so a `.opus` file in Cloud would
|
||||
render with the generic "file" icon/badge instead of the audio one — a cosmetic
|
||||
classification gap, not a playback or auto-filing bug (those are governed by the three maps
|
||||
Task 2's own acceptance criteria name: `ShareModal.vue`, `classifyByMime`, `content.rs` —
|
||||
all three now agree on all eight extensions including `opus`).
|
||||
- Why deferred: Task 2's action text and acceptance criteria explicitly name exactly three maps
|
||||
to reconcile ("record which one is authoritative... align the other two to it"); `useFileType.ts`
|
||||
is a fourth, distinct concern (Cloud-view display classification) not in that named set, and not
|
||||
touched by any file in 13-11's `files_modified`. Per the Scope Boundary rule, out-of-scope
|
||||
discoveries are logged, not fixed.
|
||||
- Suggested fix: add `'opus'` to `AUDIO_EXTS` in `useFileType.ts` (one line, matches this same
|
||||
extension's already-correct treatment in `classifyByMime`/`ShareModal.vue`/`content.rs`).
|
||||
|
||||
Reference in New Issue
Block a user