5 Commits
Author SHA1 Message Date
samiandClaude Sonnet 5 755d85964e gui: M1 DoD harness — scroll-60fps, rss-flat, unhappy-path
gui/docs/pkg-qa-requests-m1.md R3, filed by the previous session: three GUI
M1 DoD items (10k rows at 60 fps, flat RSS over 10 minutes,
--slow/--flaky/--drop-connection recovery) had nowhere to run in CI. This is
the harness — `gui/tests/dod/run.sh <gate> [--json <path>]`, exactly the
path/invocation contract tests/integration/README.md already specified —
plus `gui/tests/dod/dod_harness.cpp`, the Qt/RpcClient-driven binary that
actually runs each gate against a real mockd run.sh starts and tears down
itself.

- scroll-60fps: an eased scripted scroll over the whole loaded table,
  timing each step's synchronous repaint; p99 against a 16.6 ms budget
  (auto-scaled 4x under a sanitized build — ASan/UBSan overhead, not a
  loosened bar, see the harness's isSanitizedBuild()).
- rss-flat: samples this process's own VmRSS at 1 Hz across the run,
  discards a warm-up window, checks post-warm-up growth against a stated
  20 MiB slack.
- unhappy-path: three phases (slow/flaky/drop-connection), each its own
  mockd instance; passes when the client reaches and holds Connected with
  no crash or hang. A watchdog (the harness's own QTimer, backstopped by
  run.sh's external `timeout`) turns a genuine hang into a bounded non-zero
  exit rather than needing the CI caller to timeout(1) around it.

Every gate honours the exit-code and --json contract PKG/QA's pre-drafted
CI job expects unchanged (one addition needed: the build step must also
build the `gui-dod-harness` target, noted in the R3 update). No leaked mockd
processes on any exit path (`trap cleanup EXIT INT TERM`); no writes outside
a tempdir except the caller's own --json path.

Verified live end-to-end (not just unit-level): all three gates run against
a real mockd under the exact `ASAN_OPTIONS=detect_leaks=1:halt_on_error=1`
`.github/workflows/ci.yml`'s sanitizers job already sets, all pass, and
scroll-60fps was forced red once on purpose
(VELOX_DOD_FRAME_BUDGET_MS=1) to prove the fail path and exit code actually
work. Building this is also what surfaced the two RpcClient bugs fixed in
the previous commit, and one real gap in mockd itself — --drop-connection
never worked over the Unix socket transport (only WebSocket) — filed as
gui/docs/proto-requests-m1.md since tools/mockd is PROTO's file.

gui/docs/pkg-qa-requests-m1.md R3 and R4 (an unrelated, non-blocking Qt6::DBus
CMake hygiene note filed while wiring the clipboard global-shortcut path)
are updated with the concrete findings above.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NSCdCWFXBTSBBK3MzWtJiC
2026-09-12 21:28:36 +04:00
samiandClaude Sonnet 5 de83ee3cce gui: Options, Scheduler, Speed Limiter, Batch, Grabber, and the tray icon
Continues the build order past the Add-URL/File-Info/Progress dialog flow.

- OptionsDialog: General/Save To/Connection/Downloads/Proxy/Sounds tabs, every
  control bound to a real settings.* key (contracts/schema/types/Settings.
  schema.json). The spec's File Types and Site Logins tabs have no settings.*
  backing (categories go through category.upsert, credentials through the
  Secret Service) so they don't exist here — a tab either binds to a real key
  or isn't shipped. diffChanged() sends only what actually changed, matching
  settings.set's "changed[] names exactly what took effect" contract.
- SchedulerDialog: per-queue schedule (schedule.get/set) plus maxConcurrent/
  onComplete (queue.upsert), Start Now/Stop. Queue.schema.json already carries
  the schedule so queue.list alone seeds the window.
- SpeedLimiterDialog: the live global limiter (limiter.get/set) — a different
  thing from Options' downloads.speedLimit* default. buildParams() enforces
  the schema's "0 with enabled true must not be offered".
- BatchDialog: clipboard-blob and {start..end}-wildcard tabs sharing one
  category/queue/start-mode footer into download.addBatch.
- GrabberWizard: 4-step QWizard (project label -> start URL/depth/filters ->
  file-type filter -> review), grabber.start feeding a poll+event.grabber.
  progress-driven review page, Finish = grabber.harvest for the checked files.
- TrayIcon: active-count tooltip, Show/Add URL/Pause All/Resume All/Speed
  Limiter submenu/Quit. Quit only closes the GUI — there is no RPC to stop
  veloxd itself, filed as a new gap in daemon-requests-m1.md. MainWindow now
  also hides to tray instead of closing when general.minimizeToTray is set.

Every dialog's non-widget logic (diffChanged, buildSchedule, buildParams,
parseUrlBlob/expandWildcard/buildAddBatchParams, buildFileTypes/
buildStartParams/buildHarvestParams) is a static pure function with its own
test, same shape as FileInfoDialog::buildSpec from the previous round.

Verified end-to-end against a running mockd under ASan+UBSan: all five
surfaces render real data (settings.get values, queue.list's two seeded
queues, limiter.get, a live grabber.start/status crawl returning 3 files) with
no sanitizer reports. gui-check (non-ASan) and dev (ASan+UBSan) presets both
build the whole repo clean; all gui-labeled ctest targets pass.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_01NSCdCWFXBTSBBK3MzWtJiC
2026-09-11 17:17:43 +04:00
samiandClaude Sonnet 5 0af4a5c4fc gui: verify against the real veloxd; file one daemon gap
Pointed the existing RPC client at a real veloxd instance (isolated
HOME/XDG_RUNTIME_DIR/XDG_DATA_HOME — never touched the real ~/Downloads),
added a real download.add against tools/testserver, and watched it render
live end to end with no GUI code changes:

  - handshake, subscribe, category.list/queue.list all match what DAEMON
    reported
  - the full event.task.state sequence and batched event.task.progress
    both applied correctly by DownloadTableModel
  - the written file's SHA-256 matches the server's reference

Filed gui/docs/daemon-requests-m1.md: TaskSummary.sizeBytes is never
populated by this daemon build, even in download.get after the task
completes with the exact byte count already on disk. Not a GUI bug —
ProgressDelegate and the model already do the documented right thing
when size is unknown (fall back to plain text, no bar) — but it means
every task renders without a percentage against the real daemon today.
mockd always supplies sizeBytes so this doesn't block current GUI work;
flagging before the M1 GUI<->daemon integration pass.

mockd stays the primary harness for the unhappy paths
(--slow/--flaky/--drop-connection) that a real daemon won't misbehave on
command for.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016Ne28kx4VreeBWZv82Nksd
2026-09-11 07:55:06 +04:00
samiandClaude Sonnet 5 8844dd616b gui: amend PKG/QA request (R1 escalation, R3 restated) + EXT grep-check note
Review feedback on the filed request:

- R1: gui/CMakeLists.txt is on main now, so root's
  find_package(Qt6 ... Svg REQUIRED) is live — a missing SVG dev package
  is a hard configure failure for the whole project, not a skipped guard.
  Added the reason CI hasn't caught it: ubuntu-latest is 24.04 (where
  libqt6svg6-dev likely resolves), the project targets 26.04 (where it
  does not). Wrong name + runner/target release mismatch = the class of
  bug PKG/QA owns is currently unobservable in CI. That's the argument
  for R2, folded in.
- R3: corrected — CI does build the GUI and runs its three ctests under
  the ci preset. What has no home is the non-unit-test DoD: 10k-row
  60fps, flat RSS over a 10-minute run, and mockd
  --slow/--flaky/--drop-connection recovery. Asked for those specifically.
- gui/docs/ext-requests-m1.md: CLAUDE.md §3 says the no-download-logic
  rule applies to extension/ too; GUI made its half an executable ctest,
  EXT's half is still prose. Suggested the ESLint equivalent for the
  existing extension-lint job.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016Ne28kx4VreeBWZv82Nksd
2026-09-10 13:53:51 +04:00
samiandClaude Sonnet 5 2959b0f707 gui: real RTL + no-download-logic checks; split into velox-gui-lib
Follow-up hardening after a review noted the RTL "check" verified nothing
(a .ts stub that no test loads), matching a session-wide pattern of
checks written against what should be true rather than what would break.

- Split the non-main() code into velox-gui-lib (STATIC) so tests link the
  real widgets/models, not a reimplementation.
- tst_rtl: builds the real MainWindow, flips layoutDirection, asserts the
  direction propagates to the central widget AND that the offline-banner
  QHBoxLayout actually mirrors (label x-position LTR vs RTL differs by
  >100px). Verified it fails when the banner is pinned LtR.
- gui_no_download_logic: a ctest that greps gui/src for curl_*/pwrite/
  sqlite/QSqlDatabase/QNetworkAccessManager and fails on a hit — CLAUDE.md
  §3 as an executable check. Verified it fails when a curl_ token is added.
- Still uncovered (noted, not claimed): that the translation catalogue
  loads and the right context/strings resolve at runtime.

Not covered here because the files are PKG/QA-owned: tools/bootstrap.sh
ships a package name that does not exist on 26.04 (libqt6svg6-dev; the
real one is qt6-svg-dev), and --check validates pkg-config outcomes
rather than the apt names it would install. Both, plus the same name in
AGENT-PKG-QA.md and the README, are written up apply-ready in
gui/docs/pkg-qa-requests-m1.md.

Co-Authored-By: Claude Sonnet 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_016Ne28kx4VreeBWZv82Nksd
2026-09-10 01:12:06 +04:00