|
| 1 | +# Gap Analysis — Marvain (2026-02-07) |
| 2 | + |
| 3 | +**Branch:** `main` (post-merge of PR #31) |
| 4 | +**Auditor:** Forge (Augment Agent) |
| 5 | +**Method:** Full read-only review of all Python, SQL, JavaScript, HTML templates, and documentation. |
| 6 | + |
| 7 | +--- |
| 8 | + |
| 9 | +## Executive Summary |
| 10 | + |
| 11 | +**Overall completeness: ~97%** — Specs 0-5 are implemented end-to-end. The codebase has working pipelines for event ingestion → memory creation → action execution → real-time broadcast. All GUI pages exist with functional CRUD and WebSocket live updates. |
| 12 | + |
| 13 | +**Gaps found: 6** (0 critical, 2 medium, 4 minor) |
| 14 | + |
| 15 | +| # | Severity | Gap | Impact | |
| 16 | +|---|----------|-----|--------| |
| 17 | +| G1 | **MEDIUM** | Home dashboard Devices stat hardcoded to "—" | Dashboard shows wrong data | |
| 18 | +| G2 | **MEDIUM** | Main nav bar missing 5 pages | Users can't discover Events, Memories, People, Audit, LiveKit | |
| 19 | +| G3 | MINOR | Home Devices card always shows empty-state | No device preview like Agents card has | |
| 20 | +| G4 | MINOR | People/Consent page unreachable from any nav/link | Only accessible via direct URL `/people` | |
| 21 | +| G5 | MINOR | No device daemon onboarding UX | GUI-created devices show "Offline" with no guidance | |
| 22 | +| G6 | MINOR | README line 262 says worker is a "skeleton" | Outdated — worker is fully functional | |
| 23 | + |
| 24 | +--- |
| 25 | + |
| 26 | +## Verified as COMPLETE (no gaps) |
| 27 | + |
| 28 | +All items below were verified by tracing code from ingestion → storage → query → GUI rendering → WebSocket broadcast. |
| 29 | + |
| 30 | +| Spec | Summary | Key Evidence | |
| 31 | +|------|---------|--------------| |
| 32 | +| Spec 0 | Identity spine: `agent_memberships`, `users.display_name`, `users.last_seen` | `sql/005`, 21 refs in `app.py` | |
| 33 | +| Spec 1 | Consent: `people`, `consent_grants`, privacy_mode enforcement | `app.py:1595-1730`, `planner:216-220` | |
| 34 | +| Spec 2 | Devices: legacy remotes fully removed, satellite daemon functional | PR #31 merged | |
| 35 | +| Spec 3 | Actions: proposal → approval → SQS → tool_runner → broadcast | `tool_runner:103-127`, `actions.html` | |
| 36 | +| Spec 4 | Context hydration: agent fetches events+memories on session start | `worker.py:256-271` | |
| 37 | +| Spec 5 | Broadcast: DynamoDB subscriptions → API Gateway Management API | `broadcast.py:99-166`, `marvain.js` | |
| 38 | +| Memory chain | Events → TranscriptQueue → Planner → memories w/embeddings → broadcast | `planner:305-326`, `template.yaml:514` | |
| 39 | +| Action chain | GUI approve → ActionQueue → ToolRunner → execute → broadcast | `tool_runner:127`, `actions.html:414-465` | |
| 40 | +| LiveKit test | Full WebRTC page with audio/video/chat/transcript/debug panel | `livekit_test.html` (1205 lines) | |
| 41 | +| Audit log | GUI viewer exists and renders entries from S3 | `app.py:gui_audit`, `audit.html` | |
| 42 | + |
| 43 | +--- |
| 44 | + |
| 45 | +## Gap Details |
| 46 | + |
| 47 | +### G1 — Home Dashboard Devices Stat Hardcoded (MEDIUM) |
| 48 | + |
| 49 | +**What:** The Devices stat card on the home dashboard displays a literal `—` dash instead of the actual device count. |
| 50 | + |
| 51 | +**Evidence:** |
| 52 | +- `functions/hub_api/templates/home.html` line 18: `<div class="stat-value">—</div>` (hardcoded) |
| 53 | +- `functions/hub_api/app.py` lines 604-617: `gui_home()` passes `agents_count`, `spaces_count`, `pending_actions` to the template but **never queries or passes** `devices_count` |
| 54 | +- Compare to Agents: `"agents_count": len(agents_data)` (line 612) — works correctly |
| 55 | +- Compare to Spaces: `"spaces_count": len(spaces)` (line 613) — works correctly |
| 56 | + |
| 57 | +**Fix:** Query device count for the user's agents and pass `devices_count` to the template. Update `home.html` line 18 to `{{ devices_count }}`. |
| 58 | + |
| 59 | +**Effort:** ~15 lines of code. |
| 60 | + |
| 61 | +--- |
| 62 | + |
| 63 | +### G2 — Main Nav Bar Missing 5 Pages (MEDIUM) |
| 64 | + |
| 65 | +**What:** The header navigation (`base.html` lines 36-52) only links to: Home, Agents, Spaces, Devices, Actions. Five implemented pages are absent: |
| 66 | + |
| 67 | +| Page | Route | Has GUI? | In Nav? | In Quick Actions? | |
| 68 | +|------|-------|----------|---------|-------------------| |
| 69 | +| Events | `/events` | ✅ Full page | ❌ | ✅ | |
| 70 | +| Memories | `/memories` | ✅ Full page | ❌ | ✅ | |
| 71 | +| People/Consent | `/people` | ✅ Full page | ❌ | ❌ | |
| 72 | +| Audit | `/audit` | ✅ Full page | ❌ | ✅ | |
| 73 | +| LiveKit Test | `/livekit-test` | ✅ Full page | ❌ | ✅ | |
| 74 | + |
| 75 | +**Impact:** Users must know URLs or find links buried in the home Quick Actions section. People/Consent has **zero** navigation links anywhere — it's only reachable by typing `/people` directly. |
| 76 | + |
| 77 | +**Fix:** Add Events, Memories, and People to the main nav. Consider a "More" dropdown for Audit, LiveKit, Artifacts. |
| 78 | + |
| 79 | +**Effort:** ~20 lines in `base.html`. |
| 80 | + |
| 81 | +--- |
| 82 | + |
| 83 | +### G3 — Home Devices Card Always Shows Empty State (MINOR) |
| 84 | + |
| 85 | +**What:** The home page Devices card (`home.html` lines 56-69) always renders an empty-state placeholder ("Manage your devices") regardless of how many devices exist. The Agents card (lines 71-103) dynamically lists up to 5 agents. |
| 86 | + |
| 87 | +**Fix:** Query recent devices in `gui_home()` and render them like the agents list. |
| 88 | + |
| 89 | +**Effort:** ~30 lines. |
| 90 | + |
| 91 | +--- |
| 92 | + |
| 93 | +### G4 — People/Consent Page Has No Navigation Path (MINOR) |
| 94 | + |
| 95 | +**What:** The People & Consent page (`/people`, `gui_people`) is not linked from: |
| 96 | +- Main nav bar |
| 97 | +- Home page Quick Actions section |
| 98 | +- Any other page |
| 99 | + |
| 100 | +It is only reachable by typing `/people` in the browser. The page itself is fully functional (create people, manage consent grants). |
| 101 | + |
| 102 | +**Fix:** Add to nav bar (G2 fix) and/or add to Quick Actions on home. |
| 103 | + |
| 104 | +**Effort:** 1-2 lines. |
| 105 | + |
| 106 | +--- |
| 107 | + |
| 108 | +### G5 — No Device Daemon Onboarding UX (MINOR) |
| 109 | + |
| 110 | +**What:** When a user creates a device via the GUI, the device shows "Offline" because no satellite daemon is running for it. There is no guidance in the GUI on how to: |
| 111 | +1. Download/install the remote satellite daemon |
| 112 | +2. Configure it with the device token |
| 113 | +3. Start it |
| 114 | + |
| 115 | +The satellite daemon (`apps/remote_satellite/daemon.py`) is fully functional — it just has no GUI-driven setup flow. |
| 116 | + |
| 117 | +**Fix:** Add an onboarding panel on the device detail page with copy-pasteable setup commands (similar to the "Running an Agent Worker" collapsible in `livekit_test.html`). |
| 118 | + |
| 119 | +**Effort:** ~40 lines of HTML. |
| 120 | + |
| 121 | +--- |
| 122 | + |
| 123 | +### G6 — README Says Worker Is a "Skeleton" (MINOR) |
| 124 | + |
| 125 | +**What:** `README.md` line 262: _"This repo does not ship a full satellite app yet; it ships a worker skeleton."_ |
| 126 | + |
| 127 | +This is outdated. The agent worker (`apps/agent_worker/worker.py`) is a fully functional LiveKit agent with: |
| 128 | +- OpenAI Realtime API integration |
| 129 | +- Transcript ingestion to Hub |
| 130 | +- Context hydration (events + memories) |
| 131 | +- Typed chat via data channel |
| 132 | + |
| 133 | +**Fix:** Update the README paragraph to reflect current state. |
| 134 | + |
| 135 | +**Effort:** 5 lines. |
| 136 | + |
| 137 | +--- |
| 138 | + |
| 139 | +## Prioritized Implementation Plan |
| 140 | + |
| 141 | +All 6 gaps can be fixed in a single focused session. No architectural changes needed. |
| 142 | + |
| 143 | +### Phase 1: Dashboard Data (G1 + G3) — ~45 lines |
| 144 | + |
| 145 | +1. In `gui_home()` (`app.py`): query device count and recent devices for the user's agents |
| 146 | +2. Pass `devices_count` and `recent_devices` to the template |
| 147 | +3. In `home.html`: replace hardcoded `—` with `{{ devices_count }}` |
| 148 | +4. In `home.html`: replace devices empty-state with dynamic device list (matching agents card pattern) |
| 149 | + |
| 150 | +### Phase 2: Navigation (G2 + G4) — ~25 lines |
| 151 | + |
| 152 | +1. In `base.html`: add Events, Memories, People to header nav |
| 153 | +2. Consider a "More" dropdown or second row for Audit, LiveKit Test, Artifacts |
| 154 | +3. In `home.html`: add People/Consent to Quick Actions section |
| 155 | + |
| 156 | +### Phase 3: Device Onboarding UX (G5) — ~40 lines |
| 157 | + |
| 158 | +1. In `device_detail.html`: add collapsible "Getting Started" panel with: |
| 159 | + - Install instructions for the satellite daemon |
| 160 | + - Pre-filled command with the device token |
| 161 | + - Link to `apps/remote_satellite/README.md` |
| 162 | + |
| 163 | +### Phase 4: Documentation (G6) — ~5 lines |
| 164 | + |
| 165 | +1. Update `README.md` line 262 to accurately describe the agent worker |
| 166 | + |
| 167 | +### Total Estimated Effort |
| 168 | + |
| 169 | +| Phase | Lines Changed | Files | |
| 170 | +|-------|--------------|-------| |
| 171 | +| Phase 1 | ~45 | `app.py`, `home.html` | |
| 172 | +| Phase 2 | ~25 | `base.html`, `home.html` | |
| 173 | +| Phase 3 | ~40 | `device_detail.html` | |
| 174 | +| Phase 4 | ~5 | `README.md` | |
| 175 | +| **Total** | **~115** | **4 files** | |
| 176 | + |
| 177 | +--- |
| 178 | + |
| 179 | +## What Was NOT Found (Claims Verified) |
| 180 | + |
| 181 | +These items were explicitly checked and found to be **correctly implemented**, contradicting any assumption that they might be missing: |
| 182 | + |
| 183 | +- ✅ Memory persistence (planner creates episodic + semantic memories with vector embeddings) |
| 184 | +- ✅ Action approval/rejection workflow (GUI buttons + API endpoints + SQS processing) |
| 185 | +- ✅ broadcast_fn wired in tool_runner (line 127: `broadcast_fn=_make_broadcast_fn(agent_id, space_id)`) |
| 186 | +- ✅ Context hydration in agent worker (fetches 50 events + 8 recalled memories) |
| 187 | +- ✅ Privacy mode blocks event processing in planner AND event ingestion in api_app |
| 188 | +- ✅ Consent grants CRUD (create, revoke-all-then-recreate pattern) |
| 189 | +- ✅ WebSocket real-time updates on all GUI pages (events, actions, memories, presence) |
| 190 | +- ✅ Device command channel (cmd.ping, cmd.run_action, cmd.config) via DynamoDB GSI |
| 191 | +- ✅ LiveKit test page with full audio/video/chat/transcript/debug panel |
| 192 | +- ✅ Audit trail viewer reading from S3 Object Lock bucket |
| 193 | + |
| 194 | +--- |
| 195 | + |
| 196 | +## Methodology |
| 197 | + |
| 198 | +1. Read every `.py`, `.sql`, `.html`, `.js` file in the repository |
| 199 | +2. Traced 3 end-to-end pipelines: transcript→memory, action→execution, event→broadcast |
| 200 | +3. Compared `GAPANALYSIS.md` claims against actual code (all claims verified) |
| 201 | +4. Compared `ADVANCED_FEATURE_PLAN.md` specs against implementation |
| 202 | +5. Checked every GUI template for functional completeness |
| 203 | +6. Checked nav bar and home page for discoverability of all features |
| 204 | +7. Checked `template.yaml` for SQS queue wiring correctness |
| 205 | +8. Verified all 18 CLI commands exist and are documented |
| 206 | + |
| 207 | +--- |
| 208 | + |
| 209 | +**Awaiting review.** No code changes will be made until this analysis is approved. |
| 210 | + |
0 commit comments