First pass counted one buffer per download; it is one per segment. 20
active downloads at the default 8 segments = 160 buffers, so at 20 tasks
the binding constraint is the global cap, not the per-segment default —
256 MiB and the "<=60 MB RSS / 20 downloads" target (line 125) cannot both
hold whatever the default is.
Floor (64 KiB) and ceiling (16 MiB) unchanged — the 256/64 unreachability
argument is stronger under per-segment accounting. Changes:
- default 1 MiB (was 2): with the cap below, 32 live segments x 1 MiB =
32 MiB buffers -> ~45-50 MiB RSS, line 125 holds with margin.
- NEW maxActiveSegments (default 32): a global concurrent-segment cap is
the actual mechanism that bounds "20 active downloads"; docs/01 §2
implies it, docs/04 never states it. Without it no buffer policy hits
60 MB.
- maxTotalBufferBytes 128 MiB (was 256) and it must be ADDED to the
contract — currently absent, so the clamp CORE implements has no wire
representation and Options can't show/set it. Folded into B2a.
- line 125: keep 60 MB "given maxActiveSegments=32 and default buffers",
or explicitly raise to 120 MB — ADR records which. Flagged that
changing it is a defensible outcome CORE owns, not a number that
quietly loses.
- bufferBytes bounds are in FOUR schema files, not three:
Settings.schema.json connection.bufferBytes also has 4096-8388608.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
buffer-sizing.md: the frozen 4 KiB–8 MiB and docs/04's 64 KiB–64 MiB both
miss. Recommend 64 KiB – 16 MiB, default 2 MiB, max_total_buffer_bytes
unchanged at 256 MiB:
- 4 KiB floor is smaller than one libcurl write callback -> a syscall per
chunk; 64 KiB is the smallest floor that coalesces.
- throughput vs write size is flat past ~8 MiB on NVMe; 8–16 MiB is
disk-stall absorption headroom for the fast-pipe/slow-disk case; 64 MiB
is cache pressure for zero gain.
- 32 segments x 64 MiB = 2 GiB vs the 256 MiB cap means the docs/04 max is
unreachable past 4 total active segments — a misleading Options value.
16 MiB is reachable for single-/light-multitask and clamps to 8 MiB
under heavy parallelism, which is correct.
- default 4 MiB x 20 downloads = 80 MiB, busting the "<=60 MB RSS / 20
downloads" DoD; 2 MiB fits. Filed as request B4.
proto-requests-m1.md: B3 endByte accepted as inclusive (HTTP Range
semantics, no curl-boundary off-by-one); [start,end) ask withdrawn; stage
6 designed against inclusive. New B3a: the Content-Length: 0 whole-file
case needs a representable zero-length segment — min_segment_bytes means
CORE never makes empty segments mid-download, so it's only the degenerate
case; mild preference for startByte+length over an endByte=startByte-1
sentinel.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
Pure formatting, no behaviour change. PKG landed .clang-format (Google
base, 4-space indent, 100 cols); this brings util/ and the test harness
into conformance so `clang-format --dry-run -Werror` is clean. Build and
all six test binaries unchanged and green.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
util/ carries no wire surface, so it lands before the contract freeze.
- error: enum class Error, the engine-wide failure taxonomy; is_retryable
enumerates every value (no default:) so -Wswitch forces the retry
decision on each future addition. ErrorInfo carries context/http_status.
- result: Result<T> over std::expected<T, ErrorInfo>, Result<void>,
VDM_TRY / VDM_TRY_ASSIGN. Errors returned, never thrown, on the
transfer path.
- bytes: span aliases, LE load_le/store_le (debug-asserted precondition,
not input validation), and a bounds-checked latching ByteReader for the
.veloxpart.meta reader.
- event_bus: typed thread-safe pub/sub; header states plainly that
unsubscribe is not a quiesce point and download_task will need its own
drain.
- thread_pool: std::jthread pool; dtor joins in the body before members
die (fixed a use-after-destruction on cv_/mu_). Header notes shutdown is
drain-only and DAEMON will need a cancel mode.
- log: sink interface (core does no I/O); DAEMON installs one.
Tested: -Werror clean, 6 binaries green under plain / ASan+UBSan / TSan.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
Self-contained core/CMakeLists.txt (veloxcore STATIC + velox::core alias),
warnings at target scope so the sanitizer presets' CMAKE_CXX_FLAGS override
doesn't drop -Werror. vtest: ~150-line header-only harness (VT_TEST /
VT_CHECK / VT_REQUIRE / VT_CHECK_EQ) behind a one-function vdm_add_test(),
so the swap to a real framework once PKG picks one is mechanical.
Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01HPPSGhiArbvQgwC2DNiURS
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]>