scaffold: project structure, wire contract, roadmap and agent briefs

Lays out Velox Download Manager (IDM-class download manager for Ubuntu
26.04) as a monorepo ready for parallel lane development. No implementation
code by design.

- docs/: architecture, roadmap M0-M7, IDM-parity GUI spec, engine design,
  Firefox extension spec, risks/spikes, packaging
- contracts/: wire-contract skeleton (JSON Schema + fixture templates) —
  the single synchronization point between lanes
- docs/agents/: one brief per lane (PROTO, CORE, DAEMON, GUI, EXT, PKG/QA)
  with owned directories, build order and definition of done
- CLAUDE.md: rules of engagement — lane ownership, layering, non-negotiables
- CMake scaffolding with dev/tsan/release/ci presets

Two environment findings shape the design: Firefox here is the Mozilla snap
(native-messaging risk, so the extension carries a loopback-WebSocket
fallback), and Wayland forbids passive clipboard monitoring (so clipboard
capture is explicit-action-first).

Co-Authored-By: Claude Opus 5 <[email protected]>
This commit is contained in:
2026-09-09 18:21:11 +04:00
co-authored by Claude Opus 5
commit 8bb683b09d
90 changed files with 1814 additions and 0 deletions
+111
View File
@@ -0,0 +1,111 @@
# 06 — Risks and spikes
Each spike is a **timeboxed M0/M1 investigation with a written answer in `docs/adr/`**.
Do not let any of these be discovered in M6.
---
## R1 — Snap-packaged Firefox blocks native messaging ⚠ HIGH
**Evidence gathered on this machine (2026-09-09):**
```
$ snap list firefox → firefox 154.0 mozilla**
$ snap connections firefox → home connected
network connected
network-bind connected
personal-files (dot-mozilla-firefox)
```
Ubuntu ships Firefox as a strictly-confined snap. Executing a native-messaging host binary
that lives outside the snap's confinement has a long history of breaking, and even when the
manifest is found, the host runs under the snap's constraints.
**Why it's already handled:** `network` and `network-bind` are connected, so a loopback
WebSocket to `127.0.0.1` is available regardless. The dual-transport design in
`docs/05-extension-spec.md` §4 is not belt-and-braces engineering for its own sake — it is
the direct consequence of this finding.
**Spike S1 (2 days, M0, lane EXT):** on a clean 26.04 VM, install a trivial native host
into each of the four manifest locations and record exactly which ones snap Firefox can
launch, and whether the launched process can reach `$XDG_RUNTIME_DIR`. Write the result to
`docs/adr/0003-native-messaging-under-snap.md`.
**Decision rule:** if native messaging works → prefer it, keep WS as fallback. If it does
not → WS becomes the primary path, `velox-nmhost` still ships for deb/flatpak/tarball
Firefox users, and the installer detects the snap and configures pairing automatically.
Either way, M2 ships. The *installer* must detect which Firefox is in use and say so.
---
## R2 — Wayland clipboard monitoring ⚠ HIGH (feature-shaping)
Ubuntu 26.04 defaults to GNOME on Wayland. A Wayland client **cannot** passively observe
clipboard changes made by other applications — that is a deliberate security property, not
a bug, and it is the mechanism IDM's clipboard capture relies on.
**Spike S2 (2 days, M1, lane GUI):** test, on this exact desktop, (a) whether Qt receives
`QClipboard::dataChanged` for copies made in another app, (b) whether Mutter exposes
`ext-data-control-v1` / `wlr-data-control`, (c) whether `org.freedesktop.portal.GlobalShortcuts`
gives a reliable "grab clipboard now" hotkey, (d) XWayland fallback behaviour.
**Ship-regardless design:** the extension context menu covers the browser case (where
almost all copied download links come from), the portal global shortcut covers explicit
capture, and the Add-URL dialog pre-fills from the clipboard when opened. Background
monitoring is a bonus if the spike says yes. **Do not let this block the release, and do
not promise it in the UI before S2 answers.**
---
## R3 — AMO review friction 🟠 MEDIUM
`<all_urls>` + `webRequestBlocking` + `nativeMessaging` is a heavyweight permission set;
a download manager legitimately needs it, but reviews take longer and can bounce.
**Mitigation:** run `web-ext lint` in CI from day one, no remote code execution *at all*
(no CDN scripts, no `eval`), ship readable source with a build-reproduction script, write
the permission justification in M1 rather than at submission, and submit an early
unlisted build in M3 to shake out review problems while there's still time.
---
## R4 — Servers that lie about ranges 🟠 MEDIUM
`Accept-Ranges: bytes` present but `206` never delivered; ETags that change per request;
CDNs that 403 a second connection; signed URLs that expire mid-download.
**Mitigation:** resumability is *proven* by an actual 206 with a matching `Content-Range`
(`docs/04` §2); `tools/testserver` implements each of these as an explicit hostile mode and
CORE's DoD requires passing all of them; `download.refreshUrl` exists so the user can paste
a fresh signed URL into a running task.
---
## R5 — Qt 6 LGPL compliance 🟢 LOW but do it right
Dynamic linking against unmodified system Qt satisfies LGPLv3. **Do not** static-link Qt
into the AppImage without reading the terms; if the AppImage bundles Qt, bundle it as
shared objects and ship the relink information. Record in `docs/adr/0002-qt-licensing.md`.
---
## R6 — ffmpeg/libav licensing for the media grabber 🟢 LOW
Depend on the distro's ffmpeg rather than bundling; keep the muxer behind a runtime check
so the app degrades gracefully when ffmpeg is absent. Never bundle a GPL build into a
package whose licence conflicts.
---
## R7 — Contract drift between lanes 🟠 MEDIUM
The classic parallel-development failure: GUI and extension each "fix" the protocol in
their own tree and integration in M2 becomes a rewrite.
**Mitigation:** the entire `contracts/` discipline — single owner, generated code, golden
fixtures, conformance suite as a merge gate. If a lane finds the contract wrong, it opens
a `contracts/`-only PR; it does **not** work around it locally. This is the single most
important process rule in the project.
---
## R8 — Scope creep into a browser-agnostic product 🟢 LOW
Chrome/Chromium support means losing blocking `webRequest` and rebuilding capture on
`declarativeNetRequest` + `downloads.onDeterminingFilename`, which is a different design.
Ship Firefox 1.0 first. Revisit after M7 as its own project, not as an M3 side quest.