Skip to content

Commit 1344c04

Browse files
authored
refactor: remove deprecated remotes + fix WS auth ttl + fix SQL splitter (#31)
* refactor: remove deprecated remotes + fix WS auth + fix SQL splitter Remove deprecated remotes: - Delete sql/003_remotes.sql, sql/007_remotes_to_devices.sql - Delete functions/hub_api/templates/remotes.html - Remove all /api/remotes/* endpoints from app.py (541 lines) - Remove remotes GUI route, nav link, dashboard section - Remove TestRemotesGui and related tests (178+ lines) - Remove unused imports: generate_device_token, hash_token - Update SQL comments to remove remotes references - Update 5 documentation files to reflect devices-only model Fix WS auth crash (DynamoDB reserved keyword): - handler.py user auth path used bare ttl in UpdateExpression - DynamoDB rejects this: ttl is a reserved keyword - Fix: alias as #ttl in ExpressionAttributeNames (matches device auth path) Fix SQL splitter for DO $$ blocks: - _split_sql() in ops.py now tracks dollar-quoted block state - Prevents splitting PL/pgSQL blocks at internal semicolons Add .marvain-*.pid to .gitignore Tests: 326 passed, 15 skipped, 0 failures Ruff: All checks passed * chore: remove commit helper script * style: ruff format gui tests * ci: pin ruff version * ci: install jsonschema for planner tests * X
1 parent 5c8b1a8 commit 1344c04

20 files changed

+124
-1737
lines changed

.github/workflows/ci.yml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -22,7 +22,7 @@ jobs:
2222
python-version: "3.11"
2323

2424
- name: Install ruff
25-
run: pip install ruff
25+
run: pip install ruff==0.14.14
2626

2727
- name: ruff check
2828
run: ruff check functions/ layers/ apps/ marvain_cli/ tests/
@@ -59,6 +59,7 @@ jobs:
5959
python-multipart \
6060
python-dateutil \
6161
python-jose[cryptography] \
62+
jsonschema \
6263
itsdangerous \
6364
websockets \
6465
python-dotenv \

.gitignore

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -190,9 +190,9 @@ cython_debug/
190190
.abstra/
191191

192192
# Visual Studio Code
193-
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
193+
# Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
194194
# that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
195-
# and can be added to the global gitignore or merged into this file. However, if you prefer,
195+
# and can be added to the global gitignore or merged into this file. However, if you prefer,
196196
# you could uncomment the following to ignore the entire vscode folder
197197
# .vscode/
198198

@@ -215,3 +215,7 @@ marimo/_lsp/
215215
__marimo__/
216216
.livekit
217217
.livekit:
218+
219+
220+
# Marvain PID files
221+
.marvain-*.pid

ADVANCED_FEATURE_IMPLEMENTATION_PLAN.md

Lines changed: 21 additions & 100 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ This plan implements the Advanced Feature Plan (Specs 0–5) to create a fully f
1616

1717
2. **Spec 1**: Make devices fully functional with scope enforcement, heartbeat, and command channel.
1818

19-
3. **Spec 2**: Unify remotes as devices, create a remote satellite daemon, and implement heartbeat-based presence.
19+
3. **Spec 2**: Devices fully unified (legacy remotes removed); remote satellite daemon implemented.
2020

2121
4. **Spec 3**: Complete action lifecycle with approval persistence, execution status, result storage, and tool execution.
2222

@@ -25,8 +25,8 @@ This plan implements the Advanced Feature Plan (Specs 0–5) to create a fully f
2525
6. **Spec 5**: Implement real-time event broadcasting to make the GUI feel alive.
2626

2727
**Trade-offs**:
28-
- **Gain**: Coherent permission model, working devices/remotes/actions/memories, real-time GUI, and session continuity.
29-
- **Lose**: Migration effort to remove legacy table references; remote daemon is a new app to maintain.
28+
- **Gain**: Coherent permission model, working devices/actions/memories, real-time GUI, and session continuity.
29+
- **Lose**: Remote satellite daemon is a new app to maintain.
3030

3131
---
3232

@@ -36,7 +36,7 @@ This plan implements the Advanced Feature Plan (Specs 0–5) to create a fully f
3636
flowchart TD
3737
S0[Spec 0: Fix Identity/Permission Spine]
3838
S1[Spec 1: Devices Fully Functional]
39-
S2[Spec 2: Remotes as Devices]
39+
S2[Spec 2: Devices (legacy remotes removed)]
4040
S3[Spec 3: Actions Fully Functional]
4141
S4[Spec 4: Core Agent + Memories]
4242
S5[Spec 5: Real-time Event Stream]
@@ -55,7 +55,7 @@ flowchart TD
5555
**Critical Path**: Spec 0 → (Spec 1 + Spec 3) → Spec 5
5656

5757
**Parallel Workstreams** (after Spec 0):
58-
- Spec 1 + Spec 2 (devices/remotes)
58+
- Spec 1 + Spec 2 (devices)
5959
- Spec 3 + Spec 4 (actions/memories)
6060
- All converge at Spec 5 (broadcasting)
6161

@@ -85,7 +85,7 @@ ALTER TABLE IF EXISTS memberships RENAME TO legacy_memberships;
8585
| File | Change |
8686
|------|--------|
8787
| `functions/hub_api/app.py` | Replace all `memberships` JOIN with `agent_memberships` (8 occurrences found) |
88-
| `sql/003_remotes.sql` | Already creates `memberships` - mark as legacy reference only |
88+
| `sql/003_remotes.sql` | Already creates `memberships` - mark as legacy reference only | (DELETED)
8989

9090
**Lines to modify in `app.py`** (found via search):
9191
- Line 428, 452, 503, 723, 783, 819, 1009, 1524, 1790, 1825, 2073
@@ -99,7 +99,7 @@ ALTER TABLE IF EXISTS memberships RENAME TO legacy_memberships;
9999

100100
**Acceptance Criteria**:
101101
- [ ] No code references `memberships` table (only `legacy_memberships` or `agent_memberships`)
102-
- [ ] GUI pages (revoke device, delete memory, approve action, delete remote) work correctly
102+
- [ ] GUI pages (revoke device, delete memory, approve action) work correctly
103103
- [ ] All existing tests pass
104104

105105
**Risk**: Medium — requires careful search/replace and testing
@@ -168,92 +168,14 @@ ALTER TABLE devices ADD COLUMN IF NOT EXISTS last_heartbeat_at timestamptz;
168168

169169
---
170170

171-
### Phase 2: Spec 2 Remotes as Devices
171+
### Phase 2: Spec 2 -- Devices (formerly "Remotes as Devices") -- COMPLETE, legacy removed
172172

173-
**Goal**: Unify remotes and devices; create remote satellite daemon.
173+
> **Status**: Complete. The legacy `remotes` table, GUI page (`/remotes`), API endpoints
174+
> (`/api/remotes/*`), and all related tests have been fully removed from the codebase (2026-02-06).
175+
> Satellite devices are managed exclusively through the `devices` table.
174176
175-
#### 3.2.1 Design Decision
176-
177-
**Recommendation**: Fold `remotes` into `devices` (Option A from spec).
178-
179-
- Remotes are devices with `metadata.is_remote = true`
180-
- Remote-specific fields stored in `devices.metadata` and `devices.capabilities`
181-
- Existing `remotes` table becomes `legacy_remotes` or is migrated
182-
183-
#### 3.2.2 Database Migration
184-
185-
**File**: `sql/007_remotes_to_devices.sql`
186-
187-
```sql
188-
-- Migrate existing remotes to devices
189-
-- Each remote gets a new device entry with appropriate metadata
190-
191-
INSERT INTO devices (agent_id, name, capabilities, metadata, scopes)
192-
SELECT
193-
agent_id,
194-
name,
195-
capabilities,
196-
jsonb_build_object(
197-
'is_remote', true,
198-
'address', address,
199-
'connection_type', connection_type
200-
),
201-
'["events:write", "presence:write"]'::jsonb
202-
FROM remotes
203-
WHERE NOT EXISTS (
204-
SELECT 1 FROM devices d
205-
WHERE d.metadata->>'migrated_from_remote_id' = remotes.remote_id::text
206-
)
207-
ON CONFLICT DO NOTHING;
208-
209-
-- Rename remotes table to legacy
210-
ALTER TABLE IF EXISTS remotes RENAME TO legacy_remotes;
211-
```
212-
213-
#### 3.2.3 New App: Remote Satellite Daemon
214-
215-
**Directory**: `apps/remote_satellite/`
216-
217-
| File | Purpose |
218-
|------|---------|
219-
| `apps/remote_satellite/__init__.py` | Package init |
220-
| `apps/remote_satellite/daemon.py` | Main daemon entry point |
221-
| `apps/remote_satellite/hub_client.py` | WebSocket + REST Hub client |
222-
| `apps/remote_satellite/requirements.txt` | Dependencies (websockets, requests) |
223-
| `apps/remote_satellite/README.md` | Installation and usage |
224-
225-
**Daemon Responsibilities**:
226-
1. Connect to Hub WebSocket using device token
227-
2. Send `hello` on connect
228-
3. Send heartbeat every 15–30 seconds
229-
4. Respond to `cmd.ping` with `cmd.pong`
230-
5. Execute device-local tools on `cmd.run_action` (Phase 2 stretch)
231-
232-
#### 3.2.4 GUI Updates
233-
234-
| File | Change |
235-
|------|--------|
236-
| `functions/hub_api/app.py` | Update `/remotes` route to query devices with `is_remote` metadata |
237-
| `functions/hub_api/templates/remotes.html` | Show device-based remotes |
238-
| `functions/hub_api/app.py` | `POST /api/remotes` creates a device with remote metadata |
239-
240-
#### 3.2.5 Tests
241-
242-
| Test File | Tests |
243-
|-----------|-------|
244-
| `tests/test_remote_satellite.py` (new) | Daemon heartbeat, ping/pong |
245-
| `tests/test_gui_remotes_as_devices.py` (new) | GUI shows device-based remotes |
246-
247-
**Acceptance Criteria**:
248-
- [ ] Adding a remote produces a device with token + install snippet
249-
- [ ] Remote shows "online" when daemon is connected
250-
- [ ] Ping works over WebSocket (server→remote→server)
251-
- [ ] Existing remote data migrated successfully
252-
253-
**Risk**: Medium — new app, migration
254-
**Estimate**: 4–5 hours
255-
256-
---
177+
The remote satellite daemon (`apps/remote_satellite/`) authenticates as a device and connects
178+
via WebSocket for heartbeats, ping/pong, and action execution.
257179

258180
### Phase 3: Spec 3 — Actions Fully Functional
259181

@@ -316,7 +238,7 @@ def execute(payload: dict, ctx: ToolContext) -> ToolResult:
316238
**Acceptance Criteria**:
317239
- [ ] GUI approve → action executes
318240
- [ ] GUI shows action results (result/error)
319-
- [ ] `device_command` action can ping a remote
241+
- [ ] `device_command` action can ping a device
320242
- [ ] `send_message` stores event even if broadcast fails
321243

322244
**Risk**: Medium
@@ -459,7 +381,6 @@ def broadcast_to_subscribers(
459381
| `functions/hub_api/static/js/marvain.js` | Handle broadcast messages, update UI |
460382
| `functions/hub_api/templates/actions.html` | Auto-refresh on `actions.updated` |
461383
| `functions/hub_api/templates/devices.html` | Auto-refresh on `presence.updated` |
462-
| `functions/hub_api/templates/remotes.html` | Auto-refresh on `presence.updated` |
463384

464385
#### 3.5.6 Tests
465386

@@ -470,7 +391,7 @@ def broadcast_to_subscribers(
470391

471392
**Acceptance Criteria**:
472393
- [ ] Actions page updates without manual refresh
473-
- [ ] Remotes/devices online state updates without polling
394+
- [ ] Devices online state updates without polling
474395
- [ ] Events page shows new events in real-time
475396

476397
**Risk**: Medium — requires DynamoDB GSI for efficient subscription lookup
@@ -539,9 +460,9 @@ Each commit should be a logical unit:
539460
- `feat(ws): add device command messages`
540461

541462
3. **Phase 2 commits**:
542-
- `feat(db): add migration 007 for remotes to devices`
463+
- `refactor(db): remove migration 007 (remotes to devices) -- legacy cleanup`
543464
- `feat(app): add remote satellite daemon skeleton`
544-
- `refactor(gui): update remotes page for device-based remotes`
465+
- `refactor: remove legacy remotes page entirely`
545466

546467
4. **Phase 3 commits**:
547468
- `feat(db): add migration 008 for action enhancement`
@@ -599,7 +520,7 @@ git revert <commit-hash>
599520
### 6.4 Data Safety
600521

601522
- `legacy_memberships` preserved (not dropped)
602-
- `legacy_remotes` preserved (not dropped)
523+
- `legacy_remotes (REMOVED)` preserved (not dropped)
603524
- Audit bucket has Object Lock (immutable)
604525

605526
---
@@ -636,7 +557,7 @@ git revert <commit-hash>
636557
|-------|-------------|----------|--------------|
637558
| Phase 0 | Fix identity/permission spine | 2–3 hours | None |
638559
| Phase 1 | Devices fully functional | 3–4 hours | Phase 0 |
639-
| Phase 2 | Remotes as devices | 4–5 hours | Phase 0, Phase 1 |
560+
| Phase 2 | Devices (legacy remotes removed) | 4–5 hours | Phase 0, Phase 1 |
640561
| Phase 3 | Actions fully functional | 4–5 hours | Phase 0 |
641562
| Phase 4 | Core agent + memories | 4–5 hours | Phase 0 |
642563
| Phase 5 | Real-time event stream | 4–5 hours | Phase 3, Phase 4 |
@@ -672,7 +593,7 @@ All 6 phases (Specs 0-5) have been successfully implemented.
672593
|-------|------|--------|--------|
673594
| 0 | Fix Identity/Permission Spine | `c5abd1f` | ✅ Complete |
674595
| 1 | Devices Fully Functional | `24f9d3f` | ✅ Complete |
675-
| 2 | Remotes as Devices | `2c73b39` | ✅ Complete |
596+
| 2 | Devices (legacy remotes removed) | `2c73b39` | ✅ Complete |
676597
| 3 | Actions Fully Functional | `8938244` | ✅ Complete |
677598
| 4 | Core Agent + Memories | `806bdd7` | ✅ Complete |
678599
| 5 | Real-time Event Stream | `4604d61` | ✅ Complete |
@@ -687,7 +608,7 @@ All 6 phases (Specs 0-5) have been successfully implemented.
687608
**New Files Created:**
688609
- `sql/005_users_columns_and_legacy_cleanup.sql` - Migration for users columns and legacy memberships cleanup
689610
- `sql/006_devices_enhancement.sql` - Device scopes, heartbeat, command channel
690-
- `sql/007_remotes_to_devices.sql` - Migrate remotes to devices table
611+
- `sql/007_remotes_to_devices.sql (DELETED)` - Migrate remotes to devices table
691612
- `sql/008_actions_enhancement.sql` - Action lifecycle columns (result, error, timestamps)
692613
- `layers/shared/python/agent_hub/broadcast.py` - WebSocket broadcast module
693614
- `layers/shared/python/agent_hub/tools/device_command.py` - Device command tool

0 commit comments

Comments
 (0)