Files
vdm/docs/agents/PROMPTS.md
T
samiandClaude Opus 5 e88edd5bd6 docs: add dispatch prompts and correct the toolchain survey
- docs/agents/PROMPTS.md: six copy-paste agent prompts, wave ordering
  (PROTO + PKG/QA first, the other four at contract freeze) and the
  git worktree commands
- Correct the bootstrap section: an earlier check ran with a restricted
  PATH and wrongly reported the machine as bare. git 2.53, CMake 4.2.3,
  g++ 15.2, ninja, Qt 6 dev, libcurl, SQLite, nlohmann-json, ffmpeg and
  clang-format/tidy are all present; only libqt6svg6-dev, libsecret-1-dev,
  nodejs/npm and clang are missing. Flag that CMake 4.x rejects
  cmake_minimum_required below 3.5.

Co-Authored-By: Claude Opus 5 <[email protected]>
2026-09-09 18:23:12 +04:00

391 lines
20 KiB
Markdown

# 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<T> 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<T>, 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.