# Dispatch prompts Copy-paste one prompt per agent session. Each is self-contained. ## Dispatch order — this matters ``` WAVE 1 (start now, in parallel) WAVE 2 (start at contract freeze) ┌──────────────┐ ┌──────────────┐ ┌──────┐ ┌────────┐ ┌─────┐ ┌─────┐ │ PROTO │ │ PKG/QA │ │ CORE │ │ DAEMON │ │ GUI │ │ EXT │ │ the contract │ │ build + CI + │ └──────┘ └────────┘ └─────┘ └─────┘ │ is the │ │ testserver │ all four run fully in parallel │ blocker │ │ │ and never block each other └──────────────┘ └──────────────┘ ``` **Do not start Wave 2 before PROTO announces the freeze.** Four agents guessing at the protocol is precisely the failure this structure exists to prevent. PKG/QA runs in Wave 1 because CORE's definition of done is written against `tools/testserver`, which PKG/QA builds. ## Running each lane in its own worktree Branches already exist. Give each agent its own working copy: ```bash cd /home/sami/vdm git worktree add ../vdm-proto lane/proto git worktree add ../vdm-pkg lane/pkg-qa git worktree add ../vdm-core lane/core git worktree add ../vdm-daemon lane/daemon git worktree add ../vdm-gui lane/gui git worktree add ../vdm-ext lane/ext ``` Then start a Claude Code session in each directory and paste that lane's prompt. Lanes merge to `main` via PR; `main` stays green. --- # WAVE 1 ## → PROTO ``` You are lane PROTO on the Velox Download Manager project (worktree: ../vdm-proto, branch lane/proto). Read these first, in order: 1. CLAUDE.md — rules of engagement, lane ownership 2. docs/agents/AGENT-PROTO.md — your brief; it is the spec, not background 3. contracts/README.md — the v1 method surface you must formalize 4. docs/01-architecture.md §4-§5 — why the contract works the way it does You own contracts/, tools/mockd/, tests/conformance/ and nothing else. You are the only committer to contracts/ for the life of the project. You are the blocker for four other lanes, so work in this order and announce each completion: 1. JSON Schema (draft 2020-12) for every type, method and event in contracts/README.md. Two templates exist — contracts/schema/types/ TaskSummary.schema.json and contracts/schema/methods/capture.offer.schema.json. Match their shape, including the x-privileged / x-transports / x-deadlineMs annotations. 2. contracts/codegen/gen_cpp.py -> core/generated/velox_proto.{hpp,cpp} contracts/codegen/gen_ts.py -> extension/src/shared/protocol/ Commit the generated output. Other lanes must never run the generator to build. 3. contracts/fixtures/ — a success fixture for every method, plus error fixtures for auth failure, timeout and not-found. Use $uuid / $isoDate placeholders. 4. tools/mockd — Node/TS mock daemon serving the fixtures over BOTH transports (Unix socket NDJSON and loopback WebSocket), emitting synthetic progress events at 4 Hz, with flags --slow --flaky --drop-connection --refuse-pairing --tasks N. GUI and EXT build against this for all of M1, so it must land early and be solid. 5. tests/conformance/ — replays every fixture against a live veloxd and through the generated TS client. Coordinate with PKG/QA to make it a required CI check. Definition of done: mockd answers every fixture on both transports; both generated clients round-trip every fixture; conformance is a required CI check; contracts/VERSION is set to 1.0.0 and you have announced the freeze. Constraints: - Do not write outside your owned directories. Ever. - Node/npm are not installed yet — `sudo apt install -y nodejs npm` first. - Ask before inventing a method that is not in contracts/README.md; that list was designed against the GUI and extension specs and additions have downstream cost. Start by reading the four documents, then post a short plan of the schema files you intend to create before writing them. ``` ## → PKG/QA ``` You are lane PKG/QA on the Velox Download Manager project (worktree: ../vdm-pkg, branch lane/pkg-qa). Read these first: 1. CLAUDE.md 2. docs/agents/AGENT-PKG-QA.md — your brief 3. docs/07-packaging.md 4. docs/06-risks-and-spikes.md — R1 (snap Firefox) and R5 (Qt LGPL) are yours later You own packaging/, .github/workflows/, tools/testserver/, tests/integration/, tests/e2e/, and the root build files (CMakeLists.txt, CMakePresets.json, .clang-format, .clang-tidy, .editorconfig, .gitignore). Never write feature code in a lane's directory. Work in this order — lanes CORE and DAEMON are blocked on item 2: 1. tools/bootstrap.sh — installs the full apt list for a clean 26.04 machine. This machine already has git 2.53, CMake 4.2.3, g++ 15.2, ninja, Qt 6 dev, libcurl, SQLite, nlohmann-json, ffmpeg, clang-format/tidy. Missing here: libqt6svg6-dev, libsecret-1-dev, nodejs, npm, clang. ⚠ CMake 4.x hard-errors on cmake_minimum_required below 3.5 — verify every dependency, and record anything that breaks in docs/adr/. 2. tools/testserver — a deliberately hostile HTTP server. CORE's entire definition of done is written against it, so it is your highest-priority deliverable. Modes, each selectable by URL path or flag: no-range, lies-about-accept-ranges, etag-changes, flaky-reset, slow-loris, redirect-chain, 401-basic, 401-digest, 403-without-referer, 416-always, content-length-mismatch, expiring-signed-url, throttled, chunked-no-length, utf8-content-disposition, legacy-content-disposition 3. Flesh out CMakeLists.txt and CMakePresets.json (presets already drafted: dev with ASan/UBSan, tsan, release with LTO, ci). C++23, -Wall -Wextra -Werror. 4. CI in .github/workflows/: gcc+clang matrix, unit tests, ASan/UBSan/TSan jobs, clang-format --dry-run -Werror, clang-tidy, web-ext lint, and PROTO's conformance suite as a required check on every PR. Then: nightly integration runs, Playwright e2e, the 72-hour soak job, and in M6 the .deb/PPA, Flatpak and AMO packaging per docs/07-packaging.md. Definition of done for M0: one command builds from a clean checkout on a clean 26.04 VM; CI goes red on format, tidy, sanitizer, conformance or web-ext lint failure. Start with bootstrap.sh and testserver — two other lanes are waiting on the latter. ``` --- # WAVE 2 — start only after PROTO announces the contract freeze ## → CORE ``` You are lane CORE on the Velox Download Manager project (worktree: ../vdm-core, branch lane/core). You are on the project's critical path. Read these first: 1. CLAUDE.md 2. docs/agents/AGENT-CORE.md — your brief 3. docs/04-engine-design.md — read every word; this is your specification 4. docs/01-architecture.md §3 — the layering rule you must not violate You own core/, tools/bench/, tools/fuzz/. You never touch daemon/, gui/, extension/ or contracts/. If the daemon needs something, expose it as a core API and tell lane DAEMON — do not reach into their tree. Hard constraints, enforced in review: - No JSON, no SQL, no Qt, no RPC anywhere in core/. Your API takes a DownloadSpec and emits typed callbacks. If you are including a protocol header, stop. - C++23, -Wall -Wextra -Werror, no raw new/delete, no naked pthread. - Errors are returned as Result on the transfer path, not thrown. - Zero allocation in the curl write callback. The ring buffer is preallocated at task start. This is checked by review and by a bench assertion. Build in this order (each step lands with tests): 1. util/ — Result, event bus, thread pool, logging, byte spans 2. net/http_client — libcurl multi wrapper, one curl_multi per worker thread 3. net/probe — HEAD then ranged-GET fallback; Content-Disposition per RFC 5987/6266 including legacy forms. Give it a test table and a fuzz target — this is a classic mojibake source. 4. io/sparse_file + io/write_buffer — posix_fallocate, per-segment ring buffer sized by buffer_bytes (the user-facing "buffer" setting), pwrite at absolute offsets, posix_fadvise(DONTNEED), timed fdatasync 5. meta/veloxpart — the resume sidecar in docs/04 §5. Write the READER first and fuzz it; this file lives in a download directory and is attacker-adjacent. 6. segment/segmenter + segment/stealer — dynamic segment stealing per docs/04 §3 7. rate/token_bucket — hierarchical global -> queue -> task 8. task/download_task — the state machine in docs/04 §1, retry/backoff, mirrors 9. rules/ — filename sanitization, collision policy, category matching (pure fns) Leave core/src/media/ empty — HLS/DASH is M4. Definition of done for M1: - 5 GB download saturates 1 Gbit at <=8% of one core, recorded in tools/bench/ - kill -9 at ~60%, resume, SHA-256 matches the reference byte for byte - every hostile mode in tools/testserver handled - ASan + UBSan + TSan clean under a 20-task load test - fuzz targets for Content-Disposition, the .veloxpart.meta reader and URL parsing run 1M+ execs with no crash - public API documented in core/include/vdm/README.md and reviewed by DAEMON Do not start with io_uring — it is a post-1.0 experiment gated on a >=10% bench win. Start with util/ and net/http_client, and post your DownloadSpec/callback API design for DAEMON to review before you build on top of it. ``` ## → DAEMON ``` You are lane DAEMON on the Velox Download Manager project (worktree: ../vdm-daemon, branch lane/daemon). You own all persistent state in this system. Read these first: 1. CLAUDE.md 2. docs/agents/AGENT-DAEMON.md — your brief 3. docs/01-architecture.md §2-§5 4. contracts/README.md — the methods you implement 5. docs/05-extension-spec.md §4 — you implement the daemon half of pairing You own daemon/, cli/, nmhost/, packaging/nativehost/. Never write in core/, gui/, extension/ or contracts/. Build in this order: 1. rpc/ — uds_server (NDJSON on $XDG_RUNTIME_DIR/velox/velox.sock, mode 0600, SO_PEERCRED same-UID check) and ws_server (bind 127.0.0.1 ONLY, first free port in 52000-52016, write it to ws.port). One dispatcher generated from contracts/. Never block the RPC loop on disk or DNS. 2. Auth and pairing — session.pair triggers a user prompt (GUI dialog if connected, else a notification with actions). 256-bit tokens stored hashed, per-install, revocable. Failed-auth rate limit 5/min then 60s lockout. Enforce x-transports and x-privileged from the schema; refuse privileged methods over WS with -32003. 3. store/ — SQLite WAL: tasks, segments, categories, queues, rules, settings, history, pairings. Numbered migrations applied at startup, with a forward-only test from every released version. Credentials go to the Secret Service via libsecret, NEVER into SQLite. 4. sched/ — concurrency governor (global, per-queue, per-host caps), time windows, days of week, one-shot vs periodic, on-queue-complete actions. 5. Event fan-out — per-subscription filtering, and event.task.progress BATCHED into one array message at <=4 Hz. One message per task per tick will burn a core in the GUI; do not do it. 6. capture.offer — must answer within 750 ms, always. Apply the rules table, resolve the category folder, dedupe against active tasks, return take/ignore. If anything is slow internally, answer "ignore" and let Firefox have the download. Never make the browser wait. 7. integration/ — systemd user units (velox.service + velox.socket for socket activation), single-instance lock, XDG autostart, desktop notifications, graceful shutdown that flushes buffers and meta files. 8. cli/ — velox add|ls|pause|resume|rm|queue|settings with --json. Build this EARLY; it is how you test the daemon before any GUI exists. 9. nmhost/ — 4-byte-length-prefixed stdio <-> Unix socket pump. Under 300 lines, zero business logic, exits cleanly when Firefox closes the pipe. Install manifests to all four locations in docs/05 §4. Definition of done for M1: full conformance suite passes as a server over both transports; daemon restart mid-download reloads every task with correct state and resumes; 1000 tasks -> download.list paged under 50 ms; pairing works from real Firefox and unpair revokes immediately; systemctl --user socket activation verified from cold; security review passes (loopback only, no path traversal in saveDir — canonicalize and check allowed roots, returning -32011 — no plaintext secrets). Do not put download logic here; that is CORE. You schedule and persist, CORE transfers. Do not invent protocol fields — file a request with PROTO. Start with rpc/ and the CLI so you have something testable end to end on day one. ``` ## → GUI ``` You are lane GUI on the Velox Download Manager project (worktree: ../vdm-gui, branch lane/gui). You develop entirely against tools/mockd and never wait for the real daemon. Read these first: 1. CLAUDE.md 2. docs/agents/AGENT-GUI.md — your brief 3. docs/03-gui-spec.md — screen-by-screen spec; build in its order 4. docs/06-risks-and-spikes.md R2 — the Wayland clipboard constraint, before you write any clipboard code You own gui/ and nothing else. Qt 6 Widgets, C++23. Zero download logic: a grep for curl, pwrite or sqlite in gui/ must come back empty. Build in this order: 1. rpc/ client — wrap the generated C++ client. Async calls on a worker thread, Qt signals on the main thread, auto-reconnect with exponential backoff, an offline BANNER rather than a modal error, connection dot in the status bar. Everything else depends on this, so get it right first. 2. models/DownloadTableModel — QAbstractItemModel over TaskSummary. Apply event.task.progress batches as row patches with narrow dataChanged ranges. Never beginResetModel() on a progress tick. QSortFilterProxyModel for the category tree selection. 3. Main window — menus, toolbar, splitter, category tree, table, status bar, column layout persisted in QSettings. 4. widgets/ — ProgressDelegate (in-cell bar), SegmentBarsWidget, SpeedGraphWidget (60s rolling, 1 Hz, QPainterPath, no per-frame allocation). 5. Dialogs in this order: Add URL -> Download File Info (async probe with a spinner) -> Download Progress -> Options (every control bound to a settings.* key that actually exists in the schema) -> Scheduler -> Speed Limiter -> Batch -> Grabber. 6. Tray icon and the frameless always-on-top drop target. 7. clipboard/ — read docs/06 R2 FIRST. Under Wayland an app cannot passively observe other apps' clipboard changes. Implement the explicit paths as primary (Add-URL prefill, portal global shortcut) and treat passive monitoring as a bonus that spike S2 may unlock. Do not advertise it in the UI until S2 answers. 8. Theming — resources/qss/idm-like.qss and dark.qss, all colours in one variables block, follow QStyleHints::colorScheme(). Definition of done for M1: every screen in docs/03-gui-spec.md exists and is driven only by mockd; 10000 synthetic rows scroll at 60 fps with flat memory over 10 minutes (mockd --tasks 10000); mockd --slow/--flaky/--drop-connection produce a banner and clean recovery, never a freeze; all strings in tr() with a stub Arabic .ts proving RTL survives; no blocking call on the UI thread, verified by a 200 ms debug watchdog. Icons: ship an ORIGINAL or compatibly-licensed set (Papirus/Breeze-derived is fine) in IDM's layout positions. Do not copy IDM's artwork. Record the licence in gui/resources/icons/LICENSE. libqt6svg6-dev is not installed yet — apt install it before starting. Start with the rpc client and the table model; get one live-updating row on screen from mockd before building any dialog. ``` ## → EXT ``` You are lane EXT on the Velox Download Manager project (worktree: ../vdm-ext, branch lane/ext). You develop against tools/mockd over the WebSocket transport and never wait for the real daemon. Read these first: 1. CLAUDE.md 2. docs/agents/AGENT-EXT.md — your brief 3. docs/05-extension-spec.md — full spec 4. docs/06-risks-and-spikes.md — R1 (snap Firefox) and R3 (AMO review) You own extension/ only. Not nmhost/ — that belongs to DAEMON; file a request if you need it changed. Zero download logic: you collect URL + headers + cookies and hand them over. That is the whole job. YOUR FIRST TASK IS A SPIKE, NOT CODE. Spike S1 (2 days): Firefox on this machine is the Mozilla snap (firefox 154.0). Snap confinement has a long history of blocking native-messaging hosts. On a clean 26.04 VM, determine empirically which of the four manifest locations in docs/05 §4 snap Firefox can actually launch, and whether the launched host can reach $XDG_RUNTIME_DIR. Write docs/adr/0003-native-messaging-under-snap.md. Then build the WebSocket transport FIRST regardless of the outcome — snap connections firefox shows network and network-bind connected, so loopback is the one path known to work here. Build in this order: 1. transport/ — the Transport interface; WebSocketTransport (port discovery across 52000-52016 verified by session.hello, pairing token in browser.storage.local, reconnect with backoff); then NativeTransport; then runtime selection with a manual override in Options. 2. capture/headers.ts — onBeforeSendHeaders stash keyed by requestId, ring buffer, 5-minute TTL, bounded size. A leak here eats the browser's memory. 3. capture/rules.ts — shouldCapture() exactly per docs/05 §2. Write the test table BEFORE the implementation. This function is where the bugs will be: too eager and you hijack page navigations, too shy and you are not a download manager. Cover attachment, monitored extension, monitored MIME, size threshold, excluded host, HTML navigation, blob URL, bypass modifier held, streaming media, and a range request the page itself issued. 4. capture/index.ts — the blocking onHeadersReceived hook: gather cookies, call capture.offer with a 750 ms budget, return {cancel:true} only on "take". FAIL-OPEN IS A HARD REQUIREMENT: daemon down, slow or erroring means Firefox downloads normally. Write that test before the feature. A download manager that eats downloads when its daemon is down is worse than none. 5. capture/downloads-api.ts — downloads.onCreated safety net for what slips past. 6. context-menus.ts, popup/ (live progress at <=4 Hz, pause/resume, status dot), options/ (transport, pair/unpair, monitored types synced via capture.getRules, min size, exclusions, default category, bypass modifier). 7. content/ media detection — .m3u8/.mpd/MIME sniffing plus a MediaSource observer, and the in-page "Download this video" panel listing variants from media.listVariants. The extension NEVER parses manifests; the daemon does. Detect DRM/EME and grey the button out with "Protected content". Definition of done for M1: intercepts a real download in real Firefox and hands it to mockd; fail-open proven by an automated test (kill the mock mid-flow, the file still downloads through Firefox with no error dialog); pairing works, unpair revokes, token survives a browser restart, wrong token is rejected and rate-limited; web-ext lint clean with no remote code, no eval, no CDN script; permission justification written for AMO. Do not add React/Vue for a popup and an options page — plain TS keeps the bundle and the AMO review small. Do not invent protocol fields; file a request with PROTO. nodejs and npm are not installed yet — apt install them first. Start with spike S1 and the WebSocket transport in parallel. ``` --- ## Integration discipline (all lanes) - Rebase on `main` daily. `main` stays green. - A contract problem is a PR to `contracts/` filed with PROTO — **never** a local workaround. That single rule is what makes M2 an integration week instead of a rewrite. - Record real decisions as ADRs in `docs/adr/`. The next agent has none of your context. - Code + tests + docs in the same change. A feature with no test does not exist.