Archipelago — open-source initial import

This commit is contained in:
Archipelago
2026-08-12 10:55:49 +00:00
commit 25d938cd1a
2315 changed files with 510085 additions and 0 deletions
@@ -0,0 +1,150 @@
---
phase: 01-federation-mesh-hardening
plan: 02
subsystem: demo
tags: [mock-backend, rpc, parity, mesh, federation, harness]
requires:
- phase: 01-federation-mesh-hardening
provides: "mock-backend.js's per-session store (mesh.dynamic/blobs) and the attachment-parity handlers landed in c2ce71c6 (send-content-inline / send-content / fetch-content / transport-advice)"
provides:
- "Ten previously-missing mesh/federation RPC handlers, each mirroring and citing its daemon counterpart"
- "Stateful chat mutations (reaction, reply, read-receipt, edit, delete, forward, channel) visible on the next mesh.messages read"
- "scripts/mock-rpc-parity.mjs + npm run test:mock-parity — a single command that fails when the demo drifts from the UI's call surface"
affects: [demo, mesh, federation]
tech-stack:
added: []
patterns:
- "Parity harnesses must match call syntax (`method: '<x>'`), not bare string literals. The same dotted names are used as resource-cache keys in the UI (`key: 'federation.nodes'`), so a literal scan reports phantom gaps that can never be closed — which would make the harness permanently red and therefore ignored."
- "Mirror-don't-invent: every mock handler carries a comment naming the Rust file and function it mirrors, so a future reader can re-check parity instead of guessing what the demo is 'supposed' to do."
key-files:
created:
- neode-ui/scripts/mock-rpc-parity.mjs
modified:
- neode-ui/mock-backend.js
- neode-ui/package.json
key-decisions:
- "The STATIC stage matches `method: '<x>'` rather than every `'mesh.*'`/`'federation.*'` string literal as the plan specified — see Deviations. This is what let KNOWN_GAPS actually reach zero."
- "Edits and deletes are applied through a per-session `overrides` overlay keyed by sender_seq rather than by mutating a message array, because the demo's seed message list is rebuilt on every mesh.messages read. The overlay makes edit/delete observable on seeded messages too, not just ones sent this session."
- "Delete tombstones in place (plaintext '🗑 message deleted', typed_payload { deleted: true }, message_type 'delete') because that is precisely what mesh/mod.rs apply_local_delete does — it does not remove the row. A comment says so, since 'simplifying' it to a splice would be a silent divergence."
- "mesh.refresh and mesh.reboot-radio were deliberately LEFT as bare acknowledgements: the daemon's handlers have no message-store effect either, so giving them demo state would be divergence rather than parity. The comment records this so a later reader does not 'fix' them."
- "mesh.peers and mesh.contacts-list now read from one shared DEMO_MESH_PEERS constant, so the two can never disagree about who is on the mesh — the daemon merges contacts over the same peer map for the same reason."
- "An outbound/'sent' pending request was added to the demo seed. federation.cancel-request faithfully rejects anything that is not outbound-and-sent, so without such a request the demo's cancel button could only ever produce an error."
requirements-completed: [FED-04]
coverage:
- id: D1
description: "Every mesh.*/federation.* RPC the UI calls has a mock handler — the demo never answers a UI call with 'Method not found'"
requirement: "FED-04"
verification:
- kind: integration
ref: "neode-ui/scripts/mock-rpc-parity.mjs STATIC stage — 57 UI-called methods, 0 unhandled"
status: pass
human_judgment: false
- id: D2
description: "Renaming a mesh peer on the demo persists across a contacts-list read"
requirement: "FED-04"
verification:
- kind: integration
ref: "…LIVE stage — contacts-save then contacts-list round-trips the alias"
status: pass
human_judgment: false
- id: D3
description: "Reaction, reply, edit, delete and forward mutate demo state and are visible on the next mesh.messages read"
requirement: "FED-04"
verification:
- kind: integration
ref: "…LIVE stage — six mutation assertions, each re-reading mesh.messages and checking the UI-expected shape"
status: pass
human_judgment: false
- id: D4
description: "The parity check fails when a UI-called method has no mock handler"
requirement: "FED-04"
verification:
- kind: integration
ref: "Fail-first proof: disabling the mesh.clear-all case → exit 1, 'no mock handler for mesh.clear-all'; restored → exit 0 twice consecutively"
status: pass
human_judgment: false
- id: D5
description: "The harness leaves no stray listener behind"
requirement: "FED-04"
verification:
- kind: integration
ref: "Two consecutive runs both exit 0 (child killed in a finally block; ephemeral port via MOCK_BACKEND_PORT)"
status: pass
human_judgment: false
duration: 75min
completed: 2026-08-01
status: complete
---
# Phase 1 Plan 2: Demo/Real Mesh RPC Parity (FED-04) Summary
**Closed the ten mesh/federation methods the demo answered with "Method not found", made the six ack-only chat mutations actually mutate demo state, and replaced "squint at the browser console" with a single command that fails when the demo drifts.**
## Performance
- **Duration:** ~75 min
- **Completed:** 2026-08-01
- **Tasks:** 3/3
- **Files modified:** 3 (mock backend, new harness, package.json)
## Accomplishments
- Ten new handlers, each citing the Rust it mirrors: `mesh.contacts-list`/`-save` (typed_messages.rs), `mesh.clear-all` (status.rs), `mesh.schedule-message`/`list-scheduled`/`cancel-scheduled` (assistant.rs + scheduler.rs), `mesh.assistant-status`/`-configure` (assistant.rs), `federation.cancel-request` and `federation.notify-did-change` (federation/handlers.rs).
- The chat mutations are no longer bare acknowledgements. Reactions, replies and read-receipts push typed messages carrying the `{ sender_pubkey, sender_seq }` target key the UI's `reactionIndex`/`replyTargetPreview` read; edits rewrite the text and set `edited_at`; deletes tombstone in place; forwards copy to the destination peer; channel sends are channel-addressed.
- `mesh.peers` and `mesh.contacts-list` share one `DEMO_MESH_PEERS` list, and the peer with no `pubkey_hex` is omitted from contacts exactly as the daemon's `if let Some(pk)` guard omits it.
- `scripts/mock-rpc-parity.mjs` runs a static cross-reference then boots the mock on an ephemeral port and drives 17 live assertions. No `|| fallback` escapes anywhere in it — a failed spawn or fetch fails the run rather than producing a green run that measured nothing.
## Task Commits
1. **Tasks 1 + 2: contacts round-trip, harness, and the ten missing methods**`b8979f36` (feat)
2. **Task 3: make the message-mutation stubs mutate demo state** — committed with this SUMMARY
## Deviations from Plan
### The STATIC scan matches call syntax, not every string literal — and the plan's gap list had two false positives
**Found during:** Task 1, building the static cross-reference
**Issue:** The plan specifies scanning `src/**` for "every `'mesh.<verb>'` / `'federation.<verb>'` string literal". That over-reports badly: `Mesh.vue` and `Federation.vue` use the same dotted names as **resource-cache keys** (`key: 'mesh.self-did'`, `key: 'mesh.transport-status'`, `key: 'federation.nodes'`, `key: 'federation.dwn-status'`), and `stores/sync.ts` invalidates by the same strings. None are RPC methods. A literal scan reports 18 gaps where 10 exist.
Two of those phantoms — **`federation.nodes` and `federation.dwn-status`** — are named in the plan's own Task 2 gap list. They are not RPC methods and the daemon's dispatcher has no such routes; `Federation.vue` uses them purely as cache keys. Implementing them would have added demo-only endpoints the real node does not serve, which the plan's own transparency prohibition forbids.
**Resolution:** The scan matches `method:\s*['"]…['"]`, i.e. an actual call site. The gap list becomes exactly the 10 real methods, `KNOWN_GAPS` is empty, and the harness is genuinely failable — proven by disabling a case and watching it exit 1.
**Files modified:** `neode-ui/scripts/mock-rpc-parity.mjs`
### Added an outbound pending request to the demo seed
**Found during:** Task 2, implementing `federation.cancel-request`
**Issue:** The daemon only permits cancelling an **outbound** request in **sent** state. The demo seed contained a single inbound/pending request, so a faithful handler could only ever return an error and the cancel path was unexercisable.
**Resolution:** Added `preq-demo-out-1` (outbound, state `sent`) to `pendingPeerRequests`. The handler stays faithful — it still rejects non-outbound and non-sent requests with the daemon's own message.
**Files modified:** `neode-ui/mock-backend.js`
### Edits/deletes use an overlay rather than in-place mutation
**Found during:** Task 3
**Issue:** `mesh.messages` rebuilds its seeded message array on every read, so mutating "the message" in place would be lost immediately for any seeded message and only work for messages sent in this session.
**Resolution:** A per-session `overrides` map keyed by `sender_seq`, applied over the merged list on read. This matches the daemon's matching rule (own-Sent message by `sender_seq`) and makes edit/delete observable for seeded messages too.
**Files modified:** `neode-ui/mock-backend.js`
## Known Stubs
`mesh.assistant-status` reports `ollama_detected: false`, `claude_available: false` and an empty model list. That is the honest answer for a browser demo with no local model — the UI's "not detected" path is what a visitor should see, and fabricating a model list would be exactly the demo-only divergence this plan's prohibition forbids.
## Threat Flags
None. This plan touches only the demo/mock backend and a dev-time harness; no production code path, endpoint or trust boundary is involved. The `T-01-SC` package-install threat does not apply — no dependencies were added (the harness uses only `node:` builtins and global `fetch`).
## Self-Check: PASSED
- CONFIRMED: `node --check mock-backend.js` exits 0
- CONFIRMED: `node scripts/mock-rpc-parity.mjs` exits 0 — 17 live assertions, 0 unhandled methods
- CONFIRMED: fail-first proof (disable a case → exit 1 naming it; restore → exit 0 twice in a row)
- CONFIRMED: each of the ten methods appears exactly once as a `case`
- CONFIRMED: `KNOWN_GAPS` is present and empty; `MOCK_BACKEND_PORT` honoured; `typed_messages.rs` cited twice
- CONFIRMED: `npm run test:mock-parity` registered in package.json
</content>