GUI's M1 definition of done is "10 000 synthetic rows scroll at 60 fps with flat memory over 10 minutes (mockd --tasks 10000)". This flag was missing from the four unhappy-path flags that did land; the brief's own flag list omitted it, which is corrected here too. --tasks seeds a plausible population rather than N copies of one row: varied state, size (log-uniform 50 KB - 20 GB), category, queue position and description, drawn from the same category.list / queue.list fixtures the rest of mockd already serves so a synthetic task can never name a category or queue those methods don't also return. State distribution is roughly 55% complete / 8% failed / 4% cancelled / 6% paused / 2% retry_wait / 25% queued, using the new TaskErrorCode taxonomy for failures. "Progress advances across the whole set, not a handful of live rows" ruled out the obvious cheap answer. A bounded, rotating pool of concurrently-active downloads (--active-cap, default 24) is fed continuously from each queue's FIFO — with the rest of that queue's queuePosition renumbered on every promotion, as a real scheduler would — and a small fraction of active tasks hit a transient failure and cycle through retry_wait before rejoining, so the pool keeps rotating through new rows for the whole run instead of draining once. Verified over a 10000-task, 60-second run: 61.5 MB RSS flat, and the active pool's membership meaningfully different after 60s. tick() only ever walks the active pool plus due retry-wait entries, never the full task list, so its cost stays flat regardless of --tasks. A manual download.add is still admitted immediately regardless of --active-cap — a human driving the GUI by hand must never wait behind synthetic load. Fixed a latent double-push while building this: any task 'connecting' at the top of a tick was pushed to the progress batch once for the transition and again at the loop's unconditional final push, inflating event.task.progress payloads with a duplicate entry for that taskId. It predates this change (the original tick() had the same shape) but only became visible once several tasks are legitimately 'connecting' in the same tick, which --active-cap's continuous promotion now does routinely. --seed makes a run reproducible, which matters when a GUI bug only shows up at a particular row. Co-Authored-By: Claude Sonnet 5 <[email protected]> Claude-Session: https://claude.ai/code/session_012fgjnqFCS5h5L7gZTZo3rV
57 lines
3.1 KiB
Markdown
57 lines
3.1 KiB
Markdown
# Agent brief — PROTO (contract owner)
|
|
|
|
**Runs first, alone, in M0. Then stays on call as gatekeeper for the whole project.**
|
|
|
|
## You own
|
|
```
|
|
contracts/** tools/mockd/** tests/conformance/**
|
|
```
|
|
|
|
## You may read everything. You may write nowhere else.
|
|
|
|
## Mission
|
|
Make it impossible for the CORE, DAEMON, GUI and EXT lanes to become incompatible without
|
|
CI noticing the same day.
|
|
|
|
## M0 deliverables (in order)
|
|
|
|
1. **`contracts/schema/`** — JSON Schema (draft 2020-12) for every type, method and event
|
|
in `contracts/README.md`. Two templates already exist
|
|
(`types/TaskSummary.schema.json`, `methods/capture.offer.schema.json`) — follow their
|
|
shape, including the `x-privileged` / `x-transports` / `x-deadlineMs` annotations.
|
|
2. **`contracts/openrpc.json`** — generated from the schemas; it is the doc humans read.
|
|
3. **`contracts/codegen/gen_cpp.py`** → emits `core/generated/velox_proto.{hpp,cpp}`:
|
|
plain structs, `to_json`/`from_json` (nlohmann), a `Method` enum, and a
|
|
`dispatch(method, json) -> json` skeleton. No exceptions on the hot path; parse errors
|
|
return a `Result`.
|
|
4. **`contracts/codegen/gen_ts.py`** → emits `extension/src/shared/protocol/`: discriminated
|
|
union types, a typed `call<M>()` signature, event payload types, and runtime validators
|
|
for anything crossing the WS boundary (the daemon is not allowed to trust the wire, and
|
|
neither is the extension).
|
|
5. **`contracts/fixtures/`** — every method gets a success fixture; auth, timeout, and
|
|
not-found cases get error fixtures. Use `$uuid` / `$isoDate` placeholders for values
|
|
that can't be fixed.
|
|
6. **`tools/mockd`** — Node/TS. Serves the fixtures over **both** transports (Unix socket
|
|
NDJSON and loopback WS), fakes plausible progress events at 4 Hz, and has flags for
|
|
`--slow`, `--flaky`, `--drop-connection`, `--refuse-pairing` so GUI and EXT can test
|
|
their unhappy paths before `veloxd` exists. **`--tasks <n>`** seeds a large plausible
|
|
population (varied states/sizes/categories, a rotating active pool) instead of the
|
|
fixture's two rows — GUI's M1 DoD needs `--tasks 10000` for its scroll-performance test,
|
|
so this one lands with M0, not as an afterthought once GUI is already blocked on it.
|
|
7. **`tests/conformance/`** — one suite, two runners: replays each fixture against a live
|
|
`veloxd` (C++ side) and through the generated TS client. Wired into CI as a **required
|
|
check on every lane's PR**.
|
|
|
|
## Definition of done
|
|
- `mockd` answers all fixtures over both transports.
|
|
- Both generated clients round-trip every fixture with no hand-written types anywhere.
|
|
- Conformance is a required CI check.
|
|
- `VERSION` frozen at `1.0.0` and the freeze announced to all lanes.
|
|
|
|
## Standing duties after M0
|
|
- You are the **only** committer to `contracts/`. Other lanes file requests; you implement,
|
|
bump `VERSION`, regenerate, update fixtures, and notify the lanes in one PR.
|
|
- Reject "just add a field locally" every single time. That request is the M2 integration
|
|
disaster arriving early enough to stop.
|
|
- Optional field or new method → minor. Rename/remove/retype → major + an ADR.
|