Parity map: V1 to Apollo Download Manager
Every user-visible behaviour of the original Velocity Download Manager (V1), with where it lives in V1, the V1 tests that prove it, what Apollo Download Manager does with it, and the issue that delivers it (#22). The default is keep every V1 feature. Rows that changed meaning without aria2 were decided in #23 (ADR-0005); any further scope change needs a new PO decision.
V1 reference:
Singlbase-Technologies-Limited/velocity-download-managerat commite9d717a8. Every link below opens that exact commit.Dispositions:
Reuse: the same behaviour, ported in substance.
Reimplement: the same behaviour, written again for Apollo Download Manager's architecture.
Adapt: the same user outcome, on Apollo Download Manager's own engine.
Decided (ADR-0005): a behaviour whose meaning changed without aria2, with the PO's decision.
V1 tests: where a behaviour has no V1 test, the row says so, and the issue that delivers it adds one (#15).
Final acceptance of this map is #85 (V1 against Apollo Download Manager parity).
Tauri commands (46)
All commands in commands.rs.
| Command | V1 code | V1 tests | Apollo DM disposition | Apollo DM tests needed | Apollo DM issue |
|---|---|---|---|---|---|
get_bootstrap | commands.rs:71 | tests/unit/app.test.tsx | Reimplemented (#29) as app_info (with the contract check), get_shell_settings and get_snapshot; the start-up states in src/app/App.test.tsx | Contract fixture; UI startup and error states | #58, #29 |
get_snapshot | commands.rs:91 | tests/unit/app.test.tsx (live updates) | Adapt: snapshot from the Apollo DM engine's events, not aria2 polling | Snapshot contract; event-driven updates | #58, #59, #25 |
add_downloads | commands.rs:100 | tests/unit/app.test.tsx, tests/unit/torrent.test.tsx; engine_integration: http_download_completes_with_integrity, engine_integration: invalid_urls_are_rejected_before_the_engine, engine_integration: typed_names_keep_the_file_type_of_the_link, engine_integration: links_without_a_file_name_use_the_server_name | Reimplemented (#60) as add_downloads on the Apollo DM engine: a result for each link, duplicates by link and info hash, .torrent links fetched first (velocity-core manager::adding, manager::torrent_links; src/features/add/) | Fixture download with SHA-256; validation and naming unit tests | #60, #58, #31, #67 |
check_destination | commands.rs:108 | tests/unit/app.test.tsx | Reimplemented (#31): conflict check before adding. Ask or rename only: an existing file is never replaced, and V1's partial downloads are never resumed (PO decision on #31). The Add dialog (#60) shows it before adding and asks after | Conflict tests: ask, rename, a V1 download in the same folder | #60, #31 |
pause_downloads | commands.rs:129 | tests/unit/app.test.tsx; engine_integration: pause_and_resume | Adapt: pause through DirectTransfer; resume from checkpoints | Pause during a split; resume after restart | #58, #43 |
resume_downloads | commands.rs:135 | tests/unit/app.test.tsx; engine_integration: pause_and_resume | Adapt (as pause) | Resume with validators (#45) | #58, #43, #45 |
cancel_downloads | commands.rs:141 | engine_integration: queue_reordering_and_cancel_and_remove_with_files | Reimplement | Cancel closes files before any deletion | #58, #72 |
retry_downloads | commands.rs:147 | tests/unit/app.test.tsx; engine_integration: not_found_fails_and_manual_retry_succeeds | Reimplement on the Apollo DM retry classes | Manual retry after a permanent failure | #58, #48 |
retry_failed | commands.rs:153 | none in V1: Apollo DM adds one | Reimplement | Retry-all over mixed failure classes | #58 |
remove_downloads | commands.rs:158 | tests/unit/api.test.ts, tests/unit/app.test.tsx; engine_integration: queue_reordering_and_cancel_and_remove_with_files | Reimplement; delete only owned files | Remove with and without files; never follow links | #58, #72 |
move_download | commands.rs:168 | engine_integration: queue_reordering_and_cancel_and_remove_with_files | Adapt: a Velocity-owned queue instead of aria2 changePosition | Queue reordering | #50 |
pause_all | commands.rs:177 | none in V1: Apollo DM adds one | Reimplement | Pause-all and resume-all over a mixed queue | #58, #50 |
resume_all | commands.rs:182 | none in V1: Apollo DM adds one | Reimplement | (as above) | #58, #50 |
clear_completed | commands.rs:187 | none in V1: Apollo DM adds one | Reimplement | Clears completed records only, keeps files | #58, #32 |
set_download_connections | commands.rs:192 | tests/unit/app.test.tsx; engine_integration: connections_can_be_changed_while_downloading_and_the_file_stays_intact | Adapt: live change without a restart (aria2 restarted the transfer) | Change connections mid-download; file intact | #47, #57 |
set_download_speed_limit | commands.rs:201 | engine_integration: per_download_speed_limit_is_enforced | Adapt: Apollo DM token-bucket limits (set_limits), applied live; at least 1 KiB/s | A limit holds within the tolerance | #49 |
set_download_category | commands.rs:206 | none in V1: Apollo DM adds one | Reimplement | Category change moves nothing unexpectedly | #31 |
open_download_file | commands.rs:229 | none in V1: Apollo DM adds one | Reimplemented (#61): Manager::file_to_open checks the file is the download’s own and not a link, and refuses a program with CONFIRMATION_REQUIRED until the user confirms; src-tauri/src/opening.rs opens it with Explorer or xdg-open, with an argument list | Program warning; missing file; link refused (manager::tests::opening, DetailsPanel.test.tsx) | #61, #72 |
open_download_folder | commands.rs:250 | none in V1: Apollo DM adds one | Reimplemented (#61): Manager::folder_to_open; Explorer selects the file, xdg-open opens its folder | Selects the file, or shows the folder if the file is gone (manager::tests::opening) | #61 |
get_download_details | commands.rs:268 | none in V1: Apollo DM adds one | Decided (PO, #61): not needed. The panel reads the live snapshot, which carries the connection detail (#47), the download’s options and the torrent activity | — | #61, #47 |
get_history | commands.rs:273 | none in V1: Apollo DM adds one | Reimplemented (#32): Manager::history, with search, a status filter and pages | Paging, filters (manager::tests::history, HistoryView.test.tsx) | #32 |
clear_history | commands.rs:278 | none in V1: Apollo DM adds one | Reimplemented (#32): entries out of the list are forgotten, those in it only leave the history | Clears history, keeps files (manager::tests::history) | #32 |
delete_history_entries | commands.rs:283 | none in V1: Apollo DM adds one | Reimplemented (#32); only finished downloads, never a running one | Deletes selected entries only (manager::tests::history) | #32 |
get_settings | commands.rs:289 | tests/unit/app.test.tsx | Reimplemented (#30): get_settings, every section | Settings contract (settings.json) | #30 |
update_settings | commands.rs:294 | tests/unit/app.test.tsx, tests/unit/layout.test.tsx, tests/unit/torrent.test.tsx, tests/unit/torrentControls.test.tsx | Reimplemented (#30): set_settings, checked whole, saved in one write and applied at once | Refused natively out of bounds (settings_screen) | #30 |
reset_settings | commands.rs:302 | tests/unit/app.test.tsx, tests/unit/layout.test.tsx | Reimplemented (#30): keeps the download folder, after asking | Reset to defaults (SettingsView.test.tsx) | #30 |
list_categories | commands.rs:317 | none in V1: Apollo DM adds one | Reimplement | Category CRUD | #31 |
create_category | commands.rs:322 | none in V1: Apollo DM adds one | Reimplement | (as above) | #31 |
update_category | commands.rs:330 | none in V1: Apollo DM adds one | Reimplement | (as above) | #31 |
delete_category | commands.rs:339 | none in V1: Apollo DM adds one | Reimplement: downloads in it fall back to no category | Delete with downloads assigned | #31 |
choose_directory | commands.rs:358 | none in V1: Apollo DM adds one | Reuse in substance (native dialog) | Manual on each platform | #60, #30 |
choose_metadata_file | commands.rs:369 | none in V1: Apollo DM adds one | Reuse: .torrent and Metalink files | Manual on each platform | #60, #54, #62 |
inspect_torrent_file | commands.rs:384 | tests/unit/torrent.test.tsx; torrent_integration: corrupt_and_malicious_torrents_are_rejected_before_the_engine | Reimplemented (#63): preview_torrent_file, with the duplicate it would repeat; the Add dialog shows its file tree | Malicious torrent corpus; preview (manager::tests::adding) | #62, #63 |
start_torrent_files | commands.rs:390 | tests/unit/torrent.test.tsx; torrent_integration: magnet_can_wait_for_file_selection | Reimplemented (#63): choose_torrent_files, from the Choose files dialog | Choose files after metadata (Torrents.test.tsx) | #62, #63 |
stop_seeding | commands.rs:400 | tests/unit/torrent.test.tsx, tests/unit/torrentControls.test.tsx; torrent_integration: unlimited_seeding_can_be_paused_resumed_and_stopped | Reimplemented (#58, #63): set_torrent_seeding with { "mode": "never" }; "Stop seeding" and "Leave seeding queue" in the details | Seeding stop | #62, #63 |
set_upload_limit | commands.rs:406 | torrent_integration: download_limit_and_global_upload_limit_reach_the_engine | Reimplemented (#63): set_torrent_upload_limit, live and stored; the Add dialog sets one too | Per-torrent upload limit (manager::tests::torrents) | #62, #63 |
reannounce | commands.rs:412 | tests/unit/torrentControls.test.tsx; torrent_integration: tracker_errors_are_shown_and_a_reannounce_recovers | Reimplemented (#63): reannounce_torrent; trackers asked for every second while shown | Tracker error and re-announce | #62, #63 |
change_torrent_files | commands.rs:420 | tests/unit/torrentControls.test.tsx; torrent_integration: files_can_be_added_and_dropped_while_a_torrent_downloads, torrent_integration: file_changes_are_validated_and_never_delete_kept_or_foreign_files | Reimplemented (#63): choose_torrent_files with deleteDropped; only the torrent’s own dropped files are deleted, with delete_owned | File changes; never deleting kept files (manager::tests::torrents) | #62, #63, #72 |
choose_torrent_files | commands.rs:437 | none in V1: Apollo DM adds one | Reimplemented (#60) as choose_torrents, from Rust only (Apollo DM's choose_torrent_files chooses files inside a torrent, #58) | Manual on each platform | #60, #63 |
get_diagnostics | commands.rs:459 | torrent_integration: diagnostics_describe_bittorrent_without_secrets | Reimplemented (#28): DiagnosticReport, with libcurl's and libtorrent's versions, the engines' health and logs, and download counts without names | Report fields; no names or links (diagnostics::tests) | #28 |
export_diagnostics | commands.rs:464 | none in V1: Apollo DM adds one | Reimplemented (#28): every string redacted again; the panel has no Copy | No test secret in the export (an_export_holds_no_secret_from_any_log) | #28 |
restart_engine | commands.rs:480 | engine_integration: engine_crash_is_restarted_and_download_resumes, engine_integration: engine_restart_recovers_unfinished_download | Decided (ADR-0005): "Restart transfer engine" kept, because the engine is a helper process (ADR-0012) | Crash and restart recovery | #38, #39, #28 |
complete_onboarding | commands.rs:489 | tests/unit/app.test.tsx | Reimplemented (#29): the folder must exist and can't be a link; it doesn't wait for the engine (velocity-core manager::settings, src/app/App.test.tsx) | Onboarding flow | #29 |
get_browser_integration | commands.rs:518 | none in V1: Apollo DM adds one | Reimplemented (#65): src-tauri/src/browser.rs, BrowserIntegration in crates/velocity-core/src/browser/registration.rs | Registration state per browser | #65 |
register_browser | commands.rs:538 | tests/unit/app.test.tsx | Reimplemented (#65) with Apollo DM's host name; the Browser section's tests in src/features/settings/SettingsView.test.tsx | Register and unregister per browser and OS | #65 |
unregister_browser | commands.rs:550 | none in V1: Apollo DM adds one | Reimplemented (#65); unregistering leaves V1's registration alone (tested) | (as above) | #65 |
Settings (71 fields)
Every field of AppSettings and its nested sections in models.rs (AppSettings at models.rs:897). Fields that were aria2 options carry the PO's decision from #23. Every field Apollo Download Manager saves is on the Settings screen (#30), saved whole and refused natively when out of bounds; its tests are manager::tests::settings_screen, settings::tests and src/features/settings/SettingsView.test.tsx, and the contract fixture settings.json holds the defaults.
| Field | V1 code | Apollo DM disposition | Apollo DM issue |
|---|---|---|---|
onboarding_completed | models.rs:898 | Reused in app_settings.rs (#29; SETTINGS.md) Apollo DM (#30): onboarding.completed, on the Settings screen | #29, #30 |
general.close_to_tray | models.rs:485 | Reused in app_settings.rs (#29; SETTINGS.md) Apollo DM (#30): general.closeToTray, on the Settings screen | #29, #30 |
general.start_minimized | models.rs:486 | Reused in app_settings.rs (#29; SETTINGS.md) Apollo DM (#30): general.startMinimized, on the Settings screen | #29, #30 |
general.confirm_exit_with_active | models.rs:487 | Reused in app_settings.rs (#29; SETTINGS.md) Apollo DM (#30): general.confirmExitWithActive, on the Settings screen | #29, #30 |
downloads.default_directory | models.rs:504 | Reuse Apollo DM (#30): downloads.defaultDirectory, on the Settings screen | #30, #31 |
downloads.category_subfolders | models.rs:508 | Reuse (V1 test: saved_settings_keep_category_subfolders_off) Apollo DM (#30): downloads.categorySubfolders, on the Settings screen | #31, #30 |
downloads.start_immediately | models.rs:509 | Reuse Apollo DM (#30): queue.startImmediately, on the Settings screen | #58, #30 |
downloads.auto_categorize | models.rs:510 | Reuse (V1 tests: automatic_category_also_chooses_the_category_folder, turning_automatic_categories_off_keeps_the_default_folder) Apollo DM (#30): downloads.autoCategorize, on the Settings screen | #31, #30 |
queue.max_concurrent_downloads | models.rs:407 | Adapt: Apollo Download Manager's own queue, 2 at once by default (#351; V1's was 3) (V1 test: concurrent_downloads_respect_engine_queue) Apollo DM (#30): queue.maxConcurrentDownloads, on the Settings screen | #50, #30 |
queue.max_connections_per_download | models.rs:408 | Adapt: per-download budget Apollo DM (#30): performance.custom.maxConnectionsPerServer (the profile's cap), on the Settings screen | #47, #30 |
queue.global_download_limit_kib | models.rs:410 | Adapt: global token bucket (set_global_limits, contract 3), shared among active downloads, kept across engine restarts; the setting itself is #30 Apollo DM (#30): advanced.globalDownloadLimitKib, on the Settings screen | #49, #30 |
queue.schedule.enabled | models.rs:384 | Reuse (schedule window) Apollo DM (#30): queue.schedule.enabled, on the Settings screen | #50, #30 |
queue.schedule.start_time | models.rs:386 | Reuse Apollo DM (#30): queue.schedule.startTime, on the Settings screen | #50, #30 |
queue.schedule.stop_time | models.rs:388 | Reuse Apollo DM (#30): queue.schedule.stopTime, on the Settings screen | #50, #30 |
| A download's own start time, and the time format | none in V1: Apollo DM adds them (#350) | — | #350 |
queue.schedule.days | models.rs:390 | Reuse Apollo DM (#30): queue.schedule.days, on the Settings screen | #50, #30 |
performance.profile | models.rs:446 | Decided (ADR-0005): kept; each profile sets caps for the adaptive engine Apollo DM (#30): performance.profile, on the Settings screen | #57, #30 |
performance.custom.split | models.rs:428 | Decided (ADR-0005): removed; Apollo DM splits adaptively | #57, #30 |
performance.custom.max_connection_per_server | models.rs:429 | Decided (ADR-0005): kept as the per-host connection cap Apollo DM (#30): performance.custom.maxConnectionsPerServer, on the Settings screen | #47, #57, #30 |
performance.custom.min_split_size_mib | models.rs:430 | Decided (ADR-0005): kept as the minimum segment size Apollo DM (#30): performance.custom.minSegmentMib, on the Settings screen | #42, #57, #30 |
performance.adaptive | models.rs:449 | Adapt: measured ramp-up (Apollo DM changes connections live) | #47, #57 |
performance.allow_mid_download_tuning | models.rs:451 | Decided (ADR-0005): removed (live changes are always safe; profile caps still apply); upgrade note | #30, #47 |
performance.connection_budget | models.rs:453 | Adapt: global connection budget Apollo DM (#30): engine.connectionBudget, on the Settings screen | #47, #30 |
performance.max_tries | models.rs:454 | Adapt: Apollo DM retry classes; a download gives up after 5 failures in a row with no new byte (RetryPolicy::failures_in_a_row) Apollo DM (#30): engine.failuresInARow, on the Settings screen | #48, #30 |
performance.retry_wait_secs | models.rs:455 | Adapt: waits doubling from 1 s up to 60 s, with full jitter; at least Retry-After (up to 10 min), or 60 s, after throttling Apollo DM (#30): engine.maxRetryWaitSecs, on the Settings screen | #48, #30 |
performance.timeout_secs | models.rs:456 | Adapt: aria2's timeout was a read timeout; Apollo DM resets a request after 30 s without a byte (RetryPolicy::reset_after), with no limit on the whole transfer Apollo DM (#30): engine.resetAfterSecs, on the Settings screen | #48, #30 |
performance.connect_timeout_secs | models.rs:457 | Adapt: LoopOptions::connect_timeout, 30 s Apollo DM (#30): engine.connectTimeoutSecs, on the Settings screen | #48, #30 |
performance.lowest_speed_limit_kib | models.rs:459 | Adapt: stall watchdog on time without bytes, not speed: Stalled after 15 s, reset after 30 s; a slow but steady transfer is never reset Apollo DM (#30): engine.stalledAfterSecs, on the Settings screen | #48, #30 |
performance.app_retries | models.rs:461 | Adapt: the engine starts a failed download again up to 2 times, after 5 s and 10 s, or 60 s and 120 s after throttling (PO decision on #48) Apollo DM (#30): engine.downloadRetries, on the Settings screen | #48, #30 |
browser.accept_requests | models.rs:528 | Reused (#65): BrowserSettings::accept_requests, the Browser section | #65 |
browser.confirm_before_adding | models.rs:530 | Reused (#65): BrowserSettings::confirm_before_adding, on by default as in V1; torrents always ask | #64, #65 |
notifications.on_complete | models.rs:545 | Reused in app_settings.rs (#29; SETTINGS.md) Apollo DM (#30): notifications.onComplete, on the Settings screen | #29, #30 |
notifications.on_failure | models.rs:546 | Reused in app_settings.rs (#29; SETTINGS.md) Apollo DM (#30): notifications.onFailure, on the Settings screen | #29, #30 |
notifications.on_start | models.rs:547 | Reused in app_settings.rs (#29; SETTINGS.md) Apollo DM (#30): notifications.onStart, on the Settings screen | #29, #30 |
notifications.on_engine_failure | models.rs:548 | Decided (ADR-0005): kept, because the engine is a helper process (ADR-0012). Reused in app_settings.rs (#29), for the transfer engine and the torrent helper Apollo DM (#30): notifications.onEngineFailure, on the Settings screen | #38, #29, #30 |
appearance.theme | models.rs:586 | Reused in app_settings.rs (#29; SETTINGS.md) Apollo DM (#30): appearance.theme, on the Settings screen | #29, #30 |
appearance.density | models.rs:587 | Reused in app_settings.rs (#29; SETTINGS.md) Apollo DM (#30): appearance.density, on the Settings screen | #29, #30 |
appearance.visible_columns | models.rs:589 | Reused (#59): visibleColumns in the appearance settings, as typed ColumnIds (SETTINGS.md) | #59 |
appearance.column_widths | models.rs:594 | Reused (#59): columnWidths, 48 to 1200 px, saved once a drag ends | #59 |
appearance.layout.sidebar_width | models.rs:624 | Reused in app_settings.rs (#29; SETTINGS.md) | #29 |
appearance.layout.sidebar_collapsed | models.rs:626 | Reused in app_settings.rs (#29; SETTINGS.md) | #29 |
appearance.layout.sidebar_visible | models.rs:627 | Reused in app_settings.rs (#29; SETTINGS.md) | #29 |
appearance.layout.details_width | models.rs:629 | Reused in app_settings.rs (#29; SETTINGS.md) | #29, #61 |
appearance.layout.details_visible | models.rs:630 | Reused in app_settings.rs (#29; SETTINGS.md) | #29, #61 |
storage.history_retention_days | models.rs:699 | Reuse Apollo DM (#30): storage.historyRetentionDays, on the Settings screen | Reused (#32): StorageSettings (SETTINGS.md); applied at start and on change |
storage.max_history_entries | models.rs:700 | Reuse Apollo DM (#30): storage.maxHistoryEntries, on the Settings screen | Reused (#32): StorageSettings (SETTINGS.md) |
advanced.poll_interval_ms | models.rs:736 | Decided (ADR-0005): removed (progress arrives as events); upgrade note | #30, #58 |
advanced.file_allocation | models.rs:737 | Decided (ADR-0005): mapped to Apollo DM preallocation (none, sparse, full); default by measurement Apollo DM (#30): engine.preallocation, on the Settings screen | #43, #30 |
advanced.enable_bittorrent | models.rs:739 | Reuse Apollo DM (#30): advanced.enableBittorrent, on the Settings screen | #62, #30 |
advanced.enable_metalink | models.rs:741 | Reuse (Apollo DM implements Metalink itself) Apollo DM (#30): advanced.enableMetalink, on the Settings screen | #54, #30 |
advanced.user_agent | models.rs:743 | Reuse: TransferSpec::user_agent, at most 256 bytes as in V1; without one, ApolloDownloadManager/<version> (PO decision on #40; VelocityDownloadManager/… until 0.2.0, #347) Apollo DM (#30): advanced.userAgent, on the Settings screen | #40, #30 |
advanced.disk_cache_mib | models.rs:744 | Decided (ADR-0005): mapped to the writer buffer size Apollo DM (#30): engine.writeBufferMib, on the Settings screen | #49, #30 |
torrent.listen_port | models.rs:766 | Adapt (libtorrent): one port, listenPort, for TCP, uTP and DHT (TORRENTS.md) Apollo DM (#30): torrent.listenPort, on the Settings screen | #62, #30 |
torrent.dht_listen_port | models.rs:768 | Adapt (libtorrent): DHT uses listenPort (TORRENTS.md) Apollo DM (#30): torrent.listenPort, on the Settings screen | #62, #30 |
torrent.enable_dht | models.rs:769 | Reuse Apollo DM (#30): torrent.dht, on the Settings screen | #62, #30 |
torrent.enable_dht6 | models.rs:770 | Adapt: libtorrent runs DHT on IPv4 and IPv6 together, under dht, as V1's libtorrent engine did (TORRENTS.md) Apollo DM (#30): torrent.dht, on the Settings screen | #62, #30 |
torrent.enable_peer_exchange | models.rs:771 | Reuse Apollo DM (#30): torrent.peerExchange, on the Settings screen | #62, #30 |
torrent.enable_local_peer_discovery | models.rs:772 | Reuse Apollo DM (#30): torrent.localPeerDiscovery, on the Settings screen | #62, #30 |
torrent.dht_entry_point | models.rs:774 | Adapt (libtorrent bootstrap nodes): dhtBootstrapNodes (TORRENTS.md) Apollo DM (#30): torrent.dhtBootstrapNodes, on the Settings screen | #62, #30 |
torrent.extra_trackers | models.rs:776 | Reuse Apollo DM (#30): torrent.extraTrackers, on the Settings screen | #62, #30 |
torrent.max_peers_per_torrent | models.rs:777 | Reuse Apollo DM (#30): torrent.maxPeersPerTorrent, on the Settings screen | #62, #30 |
torrent.global_upload_limit_kib | models.rs:779 | Reuse Apollo DM (#30): torrent.uploadLimit, on the Settings screen | #62, #63, #30 |
torrent.default_upload_limit_kib | models.rs:781 | Reuse Apollo DM (#30): newTorrents.uploadLimitKib, on the Settings screen | #62, #63, #30 |
torrent.request_peer_speed_limit_kib | models.rs:783 | Decided (ADR-0005): kept if libtorrent supports it; otherwise dropped with a note. Dropped: libtorrent 2.0.15 has no such setting (TORRENTS.md) | #62 |
torrent.seeding.mode | models.rs:147 | Reuse (V1 tests: seeding ratio, queue and restart) Apollo DM (#30): newTorrents.seeding, on the Settings screen | #62, #63, #30 |
torrent.seeding.time_minutes | models.rs:148 | Reuse Apollo DM (#30): newTorrents.seeding.minutes, on the Settings screen | #62, #63, #30 |
torrent.seeding.ratio | models.rs:149 | Reuse Apollo DM (#30): newTorrents.seeding.ratio, on the Settings screen | #62, #63, #30 |
torrent.choose_files_for_magnets | models.rs:787 | Reuse Apollo DM (#30): newTorrents.chooseFilesForMagnets, on the Settings screen | #63, #30 |
torrent.engine | models.rs:791 | Decided (ADR-0005): removed; libtorrent is the only torrent engine | #62, #30 |
torrent.libtorrent_listen_port | models.rs:794 | Reuse Apollo DM (#30): torrent.listenPort, on the Settings screen | #62, #30 |
torrent.port_mapping | models.rs:797 | Reuse Apollo DM (#30): torrent.portMapping, on the Settings screen | #62, #30 |
torrent.max_active_seeds | models.rs:800 | Reuse Apollo DM (#30): torrent.maxActiveSeeds, on the Settings screen | #62, #63, #30 |
Views and UI (22)
| View or behaviour | V1 code | V1 tests | Apollo DM disposition | Apollo DM issue |
|---|---|---|---|---|
| App shell, routing and startup | App.tsx | tests/unit/app.test.tsx | Reimplemented (#29): src/app/App.tsx, src/app/App.test.tsx | #29 |
| Shell layout and splitters | Shell.tsx | tests/unit/layout.test.tsx, tests/unit/splitter.test.tsx, tests/unit/layoutLogic.test.ts | Reimplemented (#29): src/app/Shell.tsx; V1's layoutLogic and splitter tests ported (src/layout/layoutLogic.test.ts, src/components/Splitter.test.tsx) | #29 |
| Sidebar (filters, categories, collapse) | Sidebar.tsx | tests/unit/layout.test.tsx | Reimplemented (#29): src/app/Sidebar.tsx, with History (#32). Settings come with #30 | #29, #32 |
| Status bar (speeds, counts, engine state) | StatusBar.tsx | none in V1: Apollo DM adds one | Adapted (#29): the transfer engine and the torrent helper's health, not aria2's (src/app/StatusBar.tsx) | #29 |
| Downloads view (search, filters, bulk actions) | DownloadsView.tsx | tests/unit/app.test.tsx | Reimplemented (#59): src/features/downloads/DownloadsView.tsx, with the retry countdown, partial results and the login dialog (UI_ARCHITECTURE.md) | #59 |
| Download table (columns, resizing, checkboxes, keyboard) | DownloadTable.tsx | tests/unit/app.test.tsx (keyboard navigation), tests/unit/logic.test.ts | Reimplemented (#59): DownloadTable.tsx and src/downloads/list.ts; the accessibility audit is #66 | #59, #66 |
| Columns | columns.tsx | tests/unit/format.test.ts | Reimplemented (#59): columns.tsx, with Apollo DM's binary units in src/lib/format.ts | #59 |
| Context menus | menus.tsx | tests/unit/app.test.tsx | Reimplemented (#59, #61): menus.tsx, with "Open file", "Open folder" and "Details" (#61); choosing a torrent’s files is #63’s | #59, #61 |
Add dialog (single, multiple, conflicts, browser prefill, .torrent, magnets, duplicates) | AddDownloadDialog.tsx | tests/unit/app.test.tsx, tests/unit/addLogic.test.ts, tests/unit/torrent.test.tsx | Reimplemented (#60): src/features/add/, with FTP, FTPS and SFTP links, the SFTP host-key question, and what the engine can't do. V1's tests ported in src/features/add/AddDownloadDialog.test.tsx and addLogic.test.ts; the file tree of a .torrent is #63's | #60 |
| Details panel (connections, errors, files, torrent sections) | DetailsPanel.tsx | tests/unit/app.test.tsx | Reimplemented (#61): DetailsPanel.tsx, from the live snapshot; V1’s tests ported in DetailsPanel.test.tsx. No per-connection or per-segment figures (the engine reports none); trackers, peers and re-announce are #63’s | #61, #63 |
| Categories view | CategoriesView.tsx | none in V1: Apollo DM adds one | Reimplemented with the sidebar (#29), on #31's commands: src/features/categories/ | #29, #31 |
| History view | HistoryView.tsx | none in V1: Apollo DM adds one | Reimplemented (#32): src/features/history/HistoryView.tsx; "Again" opens the Add dialog with the link | #32 |
| Settings view (every section) | SettingsView.tsx | tests/unit/app.test.tsx, tests/unit/torrent.test.tsx | Reimplemented (#30): src/features/settings/; the Browser section since #65; the Proxy section since #192; V1's tests in SettingsView.test.tsx | #30 |
| Diagnostics panel | DiagnosticsPanel.tsx | none in V1: Apollo DM adds one | Adapt (no aria2) | #28 |
| Onboarding | Onboarding.tsx | tests/unit/app.test.tsx | Reimplemented (#29): src/features/onboarding/Onboarding.tsx. The browser extension step since #65, pointing to Settings, Browser, as V1's did | #29 |
| Torrents: choose files | ChooseFilesDialog.tsx | tests/unit/torrent.test.tsx, tests/unit/torrentLogic.test.ts | Reimplemented (#63): src/features/torrents/ChooseFilesDialog.tsx; V1’s tests in Torrents.test.tsx | #63 |
| Torrents: file tree | FileTree.tsx | tests/unit/torrentLogic.test.ts | Reimplemented (#63): FileTree.tsx and torrentLogic.ts, without per-file progress, which the helper doesn’t report; V1’s tests in torrentLogic.test.ts | #63 |
| Torrents: seeding fields | SeedingFields.tsx | tests/unit/torrentControls.test.tsx | Reimplemented (#63): SeedingFields.tsx, in the Add dialog; the settings’ default is #30’s | #63, #30 |
| Torrents: details section | TorrentSection.tsx | tests/unit/torrentControls.test.tsx | Reimplemented (#61, #63): TorrentSection.tsx: hashes, version, peers, seeding, the seeding queue, trackers, peers, re-announce, upload limit and file changes | #61, #63 |
| Confirm dialogs and shared UI | confirm.tsx | tests/unit/app.test.tsx | Reimplemented (#29): src/components/ | #29 |
| Tray and notifications | lib.rs | none in V1: Apollo DM adds one | Reimplemented (#29): src-tauri/src/tray.rs and shell.rs; what is notified is velocity_core::notices. Checked by hand per platform | #29 |
| Accessibility: keyboard, focus, labels, reduced motion, themes | DownloadTable.tsx | tests/unit/app.test.tsx | Audited (#66): each behaviour with its test or a manual check in ACCESSIBILITY.md; src/app/Accessibility.test.tsx, src/a11y.test.ts | #66 |
Engine and manager behaviours without their own UI (26)
These behaviours run behind the scenes, so they're easy to drop without anyone noticing (issue risk). V1's integration tests are in engine_integration.rs and torrent_integration.rs.
| Behaviour | V1 code | V1 tests | Apollo DM disposition | Apollo DM issue |
|---|---|---|---|---|
| Finalisation: Completed only when the file exists with the expected length; size-only otherwise | manager.rs:2250 | engine_integration: incomplete_transfer_is_not_reported_complete, engine_integration: checksum_mismatch_fails_verification | Adapt: Apollo DM completion gate (coverage, length, hash; no-clobber rename) | #46, #43 |
Reconcile: re-add a lost download after three missed polls; ENGINE_LOST otherwise | manager.rs:1889, manager.rs:1960 | engine_integration: engine_restart_recovers_unfinished_download | Decided (ADR-0005): follows #38, a helper process (ADR-0012); Apollo DM resumes from checkpoints, no GIDs. A download an engine crash, hang or restart lost resumes on its own once an engine runs (PO decision on #58, ENGINE_CONTRACT.md) | #43, #58, #38 |
| Failure classes (transient, throttled, permanent) and app-level retries; 429/503 wait ≥ 60 s | codes.rs:9 | engine_integration: transient_failures_are_retried_with_bounded_backoff, engine_integration: rate_limited_server_is_reported_as_throttling | Adapt: classify libcurl results and HTTP status; honour Retry-After | #48 |
| Engine supervision: restart back-off (at most five restarts in five minutes) | mod.rs:6 | engine_integration: engine_crash_is_restarted_and_download_resumes | Decided (ADR-0005): follows #38, a helper process (ADR-0012) | #38, #39 |
| Session scrubbing: credentials never kept in the engine session | mod.rs:193 | engine_integration: engine_session_file_never_keeps_credentials | Decided (ADR-0005): no aria2 session; checkpoints never hold secrets | #43, #71 |
| Secrets handling: credentials held in memory, never stored | secret.rs | engine_integration: ftp_login_is_sent_as_ftp_credentials_and_never_stored, engine_integration: ftp_user_and_password_in_the_link_are_used_and_removed_from_it | Reimplement. The FTP part is reimplemented (#51): the login goes out as FTP's USER and PASS (velocity-transfer tests/engine_contract.rs), and a login in an FTP link is split off and never stored (velocity-core's database secret scan). The keychain and the leak scan remain. Since #70: a Secret type wiped when dropped (crates/velocity-core/src/secret.rs), and the system keychain on consent per login (ADR-0015); the leak test in crates/velocity-transfer/tests/secret_leaks.rs | #70, #71 |
| Redaction of URLs, headers and keys in logs and diagnostics | redact.rs:14 | none in V1: Apollo DM adds one | Reimplemented (#28): velocity_core::redact, V1's patterns and tests, plus private keys, proxy logins, libcurl debug lines, JSON passwords and logins with an @ in the password; the child processes' logs use it too | #28 |
| URL validation: scheme allowlist, IDN, credentials out of the URL | validation.rs:42 | engine_integration: invalid_urls_are_rejected_before_the_engine | Reimplement; SFTP allowed in Apollo DM | #67 |
| File naming: typed names, server names, extensions, reserved names | validation.rs:148 | engine_integration: typed_names_keep_the_file_type_of_the_link, engine_integration: links_without_a_file_name_use_the_server_name | Reuse in substance | #31, #40, #72 |
| Destination conflicts never overwritten silently | commands.rs:108 | engine_integration: destination_conflicts_are_never_overwritten_silently | Reimplement | #31, #43 |
| Schedule window | schedule.rs:13 | none in V1: Apollo DM adds one | Reuse in substance | #50 |
| Optimizer: chooses aria2 options per profile; halves connections on throttled retries | optimizer.rs:30 | none in V1: Apollo DM adds one | Decided (ADR-0005): replaced by profile caps and adaptive budgets | #47, #57 |
| Multiple connections on range servers; one connection without ranges | — | engine_integration: range_server_uses_multiple_connections, engine_integration: non_range_server_downloads_with_one_connection | Adapt: bounded probe, leases | #41, #42 |
| HTTPS certificate rules (trusted, untrusted, purpose, host, expiry) | — | engine_integration: https_download_with_trusted_certificate, engine_integration: https_with_untrusted_certificate_fails, engine_integration: https_certificate_not_valid_for_server_auth_is_rejected, engine_integration: https_certificate_for_another_host_is_rejected, engine_integration: https_expired_certificate_is_rejected | Reimplemented per TLS backend: velocity-curl tests/tls.rs and tests/windows_store.rs, velocity-transfer-h2c tests/tls.rs; results per platform in SECURITY.md | #69 |
| FTP downloads | — | engine_integration: ftp_download_completes_with_integrity, engine_integration: ftp_missing_file_fails_with_an_error | Reimplemented on libcurl, with REST resume and explicit and implicit FTPS: velocity-transfer tests/ftp.rs and tests/engine_contract.rs, velocity-curl tests/ftp.rs, velocity-transfer-h2c tests/ftps.rs (ENGINE_INTERNALS.md) | #51 |
| Engine missing is reported | — | engine_integration: engine_missing_is_reported | Decided (ADR-0005): follows #38, a helper process (ADR-0012) | #38 |
| Browser bridge: IPC with a per-launch token; 30 requests per 10 s | ipc.rs:12 | none in V1: Apollo DM adds one | Reimplemented (#65): crates/velocity-core/src/browser/ipc.rs and bridge.rs, with Apollo DM's endpoint and token file | #65 |
| Native-host registration per browser | native_registration.rs:144 | none in V1: Apollo DM adds one | Reimplemented (#65): crates/velocity-core/src/browser/registration.rs, Windows' registry through windows-sys | #65 |
| Single instance | lib.rs:36 | none in V1: Apollo DM adds one | Reimplement with Apollo DM identifier | #27 |
| Torrent duplicates by info hash; corrupt and malicious torrents rejected | — | torrent_integration: duplicates_are_detected_by_info_hash, torrent_integration: corrupt_and_malicious_torrents_are_rejected_before_the_engine | Reimplement on libtorrent | #62 |
| Torrent v1, v2-only, hybrid and magnet downloads | — | torrent_integration: torrent_file_multi_file_downloads_and_verifies, torrent_integration: v2_only_torrents_download_on_libtorrent, torrent_integration: v2_magnet_downloads_on_libtorrent, torrent_integration: hybrid_downloads_on_libtorrent_from_libtorrent_and_knows_both_hashes | Reimplement on libtorrent | #62 |
| Torrent recovery after restart and engine crash | — | torrent_integration: restart_recovers_interrupted_torrent_without_duplicates, torrent_integration: magnet_recovers_after_restart_using_saved_metadata, torrent_integration: engine_crash_recovers_torrent, torrent_integration: a_paused_torrent_stays_paused_after_restart | Reimplement | #62 |
| Seeding policies and the seeding queue | — | torrent_integration: seeding_stops_when_the_share_ratio_is_reached, torrent_integration: the_seeding_queue_limits_active_seeders_and_starts_the_next, torrent_integration: lowering_the_seeding_limit_queues_the_newest_seeders, torrent_integration: seeding_resumes_after_restart_without_redownloading | Reimplement | #62, #63 |
| Torrent deletion: only the torrent's files, never through links | — | torrent_integration: deleting_a_completed_multi_file_torrent_removes_only_its_files, torrent_integration: deleting_an_active_torrent_removes_partial_data_and_control_file, torrent_integration: deletion_never_follows_links_out_of_the_download_folder | Reimplement | #62, #72 |
| Missing peers show as stalled, not failed | — | torrent_integration: missing_peers_show_as_stalled_not_failed | Reimplement | #62 |
| aria2-only torrent behaviours (aria2 torrents, switching torrent engine, aria2 hybrid handling) | — | torrent_integration: switching_the_engine_never_moves_existing_torrents, torrent_integration: reannounce_is_refused_for_aria2_torrents_with_a_reason, torrent_integration: v2_only_torrents_are_refused_on_aria2_with_an_explanation | Decided (ADR-0005): dropped; libtorrent is the only torrent engine | #62 |
Browser extension and native host (11)
| Behaviour | V1 code | V1 tests | Apollo DM disposition | Apollo DM issue |
|---|---|---|---|---|
| Download interception and modes (ask, add directly, off) | background-core.ts:152 | browser-extension/tests/background.test.ts | Reimplemented with Apollo DM identity (#64): browser-extension/src/background-core.ts; V1's tests in browser-extension/tests/background.test.ts | #64 |
| Allow and deny rules | rules.ts:16 | browser-extension/tests/rules.test.ts | Reused (#64): browser-extension/src/shared/rules.ts; V1's tests in browser-extension/tests/rules.test.ts | #64 |
| Context menu ("Download with Velocity") | background.ts:50 | browser-extension/tests/background.test.ts | Reimplemented (#64) as "Download with Apollo Download Manager" (#347): browser-extension/src/background.ts | #64 |
| Popup | popup.ts | none in V1: Apollo DM adds one | Reimplemented (#64): browser-extension/src/popup.ts | #64 |
| Options page | options.ts | none in V1: Apollo DM adds one | Reimplemented (#64): browser-extension/src/options.ts | #64 |
| Confirmation prompt | prompt.ts | none in V1: Apollo DM adds one | Reimplemented (#64): browser-extension/src/prompt.ts | #64 |
| Magnet capture (always through the Add dialog) | magnet.ts:2 | browser-extension/tests/background.test.ts | Reimplemented (#64): browser-extension/src/magnet.ts, off by default | #64, #63 |
| Extension settings storage | settings.ts | none in V1: Apollo DM adds one | Reimplemented (#64): browser-extension/src/shared/settings.ts | #64 |
| Native messaging client and protocol v1 | native.ts, protocol.ts | browser-extension/tests/protocol.test.ts | Reimplemented (#64): protocol v1 unchanged, to Apollo DM's host name; V1's tests in browser-extension/tests/protocol.test.ts. The host side is #65 | #64, #65 |
| Native host: framing, caller allowlist, message filtering, desktop unavailable, wrong token | main.rs | crates/velocity-native-host/tests/host_e2e.rs | Reimplemented (#65): crates/velocity-native-host, the program velocity-native-host-v2; V1's tests in crates/velocity-native-host/tests/host_e2e.rs | #65 |
| Shared message protocol and validation (core side) | protocol.rs:18 | none in V1: Apollo DM adds one | Reimplemented (#65): crates/velocity-core/src/browser/protocol.rs, checked by the host and again by the app | #65, #67 |
Persistence (6)
| Behaviour | V1 code | V1 tests | Apollo DM disposition | Apollo DM issue |
|---|---|---|---|---|
| Initial schema: settings, categories, downloads | 0001_initial.sql:4 | none in V1: Apollo DM adds one | Not reused: Apollo DM has its own schema and migrations (Apollo DM never opens V1's database, ADR-0003) | #26 |
| Torrent columns (info hash, uploaded bytes, seeding start) | 0002_torrents.sql:4 | none in V1: Apollo DM adds one | Apollo DM schema equivalent | #26, #62 |
| Torrent engine columns (v2 info hash, engine) | 0003_torrent_engines.sql:3 | none in V1: Apollo DM adds one | Apollo DM schema: engine holds Apollo DM engines only; unknown values become unavailable | #26 |
| Settings JSON migration on load (older settings get defaults) | settings.rs:11 | Reimplemented (#30): Database::section reads a section merged over its defaults; an unknown field is refused | #30 | |
Backup before migrating (VACUUM INTO); refuse a newer schema unmodified | mod.rs:7 | none in V1: Apollo DM adds one | Reimplement | #26 |
| Importing anything from a V1 installation | — | — | Decided (ADR-0007): no import in the first releases; revisit after parity | #85 |
Apollo Download Manager additions (not V1 features)
Behaviours Apollo Download Manager adds by PO decision. They aren't parity rows, because V1 didn't have them, but they're first-release scope.
| Addition | Decision | Apollo DM issue |
|---|---|---|
| SFTP downloads with strict host-key checks | ADR-0001 (HTTP, HTTPS, FTP and SFTP required); built on russh (ADR-0013): velocity-sftp tests/sftp.rs, velocity-transfer tests/sftp.rs | #52, #53 |
| Proxy settings (HTTP, HTTPS, SOCKS5) for every download, torrents included, with the password in the OS keychain on consent; ambient proxy variables ignored | ADR-0008, PO decisions on #192: velocity_core::proxy, the Settings screen's Proxy section (SETTINGS.md) | #192, #70 |
| Confirmation for browser links to loopback or private addresses | ADR-0006 | #67, #65 |
| Changed-object detection, validator-checked resume and atomic no-clobber completion | ADR-0001, research report section 5 | #43, #45, #46 |
Decided in #23
18 rows changed meaning without aria2. The PO decided each one on 2026-10-22 (ADR-0005); the rows above say Decided (ADR-0005).