proto: freeze the wire contract at 1.0.0
Schemas for the whole v1 surface: 38 methods, 9 events, 25 named types and the
JSON-RPC envelope, with x-privileged / x-transports / x-deadlineMs / x-errors
annotations that both generators emit as data rather than prose.
Four generators over one IR (contracts/codegen/schema_ir.py), so the C++ structs,
the TypeScript types and the OpenRPC document cannot disagree about what the
contract says:
gen_cpp.py -> core/generated/velox_proto.{hpp,cpp}
gen_ts.py -> extension/src/shared/protocol/
gen_openrpc.py -> contracts/openrpc.json
gen_cpp_conformance.py -> tests/conformance/cpp/fixture_dispatcher.hpp
Inbound parsing never throws: parse<T>() returns std::expected<T, ParseError> and
nlohmann's throwing ADL from_json is deliberately not emitted. Schema constraints
(minimum, maxLength, pattern, ...) become real runtime checks in both languages —
the daemon does not trust the extension and the extension does not trust the
daemon.
59 golden fixtures: a success case per method, 12 error cases, 9 events. Replayed
by tests/conformance/ against both the generated C++ and a live server over both
transports. tools/mockd serves the same fixtures with unhappy-path flags so the
GUI and EXT lanes never wait for veloxd.
run.sh also proves capture.offer fails open: with a daemon answering slower than
750 ms the client gives up and lets Firefox take the download.
core/generated/ is libveloxproto, a separate target from libveloxcore, which
still never sees JSON — see docs/adr/0009.
Co-Authored-By: Claude Opus 5 <[email protected]>
Claude-Session: https://claude.ai/code/session_012fgjnqFCS5h5L7gZTZo3rV
This commit is contained in:
+45
-4
@@ -3,21 +3,45 @@
|
||||
**This directory is the interface between every lane.** Owner: agent **PROTO**.
|
||||
Nobody else commits here. Everybody else *generates from* here.
|
||||
|
||||
> ## Status: **v1.0.0 — FROZEN** (2026-09-09)
|
||||
>
|
||||
> The surface below is complete and generated from: 38 methods, 9 events, 25 named types,
|
||||
> 59 fixtures. See `docs/adr/0005-protocol-1.0.0-freeze.md` for the versioning rule.
|
||||
>
|
||||
> **What each lane can rely on, starting now:**
|
||||
>
|
||||
> | You need | It is here |
|
||||
> |---|---|
|
||||
> | C++ types, parsing, dispatch | `core/generated/velox_proto.{hpp,cpp}` (target `libveloxproto`) |
|
||||
> | TypeScript types, typed client, runtime validators | `extension/src/shared/protocol/` |
|
||||
> | The API document to read | `contracts/openrpc.json` |
|
||||
> | A daemon to build against today | `tools/mockd` — both transports, 4 Hz progress, unhappy-path flags |
|
||||
> | Proof you have not drifted | `./tests/conformance/run.sh` |
|
||||
>
|
||||
> **Changing this is a PR to `contracts/` alone.** Optional field or new method → minor.
|
||||
> Rename, remove or retype → major, plus an ADR. File a request; do not add a field locally.
|
||||
|
||||
```
|
||||
contracts/
|
||||
├── VERSION # protocol semver, e.g. 1.0.0
|
||||
├── VERSION # protocol semver — frozen at 1.0.0
|
||||
├── openrpc.json # human-readable API doc (generated from schema/)
|
||||
├── schema/
|
||||
│ ├── envelope.schema.json # JSON-RPC 2.0 envelope + our error codes
|
||||
│ ├── types/ # Task, Segment, Category, Queue, Settings, CaptureOffer…
|
||||
│ ├── types/ # Task, Segment, Category, Queue, Settings, CaptureRules…
|
||||
│ ├── methods/ # one file per method: params + result
|
||||
│ └── events/ # one file per server→client notification
|
||||
├── fixtures/ # golden request/response pairs, replayed by conformance
|
||||
└── codegen/
|
||||
├── gen_cpp.py # → core/generated/ (structs + to_json/from_json)
|
||||
└── gen_ts.py # → extension/src/shared/protocol/ (types + client)
|
||||
├── schema_ir.py # the one loader/IR all generators share
|
||||
├── gen_cpp.py # → core/generated/ (structs + to_json + parse + dispatch)
|
||||
├── gen_ts.py # → extension/src/shared/protocol/ (types, client, validators)
|
||||
├── gen_openrpc.py # → contracts/openrpc.json
|
||||
└── gen_cpp_conformance.py # → tests/conformance/cpp/fixture_dispatcher.hpp
|
||||
```
|
||||
|
||||
Each subdirectory has its own README: `codegen/` documents the supported JSON Schema
|
||||
subset, `fixtures/` documents the fixture shape and the placeholder rules.
|
||||
|
||||
## Rules
|
||||
|
||||
1. **Generated code is committed.** No lane may be blocked because it can't run Python.
|
||||
@@ -32,6 +56,23 @@ contracts/
|
||||
regenerated code + `VERSION` bump. Lanes rebase onto it. This is the only synchronization
|
||||
point in the whole project — keep it cheap and frequent rather than big and rare.
|
||||
|
||||
## Per-method annotations
|
||||
|
||||
Every method schema carries these, and both generators emit them as data the code can act
|
||||
on rather than as prose a reader has to honour:
|
||||
|
||||
| Key | Meaning |
|
||||
|---|---|
|
||||
| `x-privileged` | refused over the WebSocket transport with `-32003` |
|
||||
| `x-transports` | which listeners serve it (`uds`, `ws`) |
|
||||
| `x-deadlineMs` | how long a client waits before giving up |
|
||||
| `x-errors` | the error codes this method is documented to return |
|
||||
| `x-wsRestrictions` | extra limits when the call arrives from the extension |
|
||||
|
||||
19 of the 38 methods are privileged: everything that reconfigures the daemon, destroys user
|
||||
data, or names an arbitrary destination path. The extension may *request* a download; it
|
||||
may not choose where the bytes land.
|
||||
|
||||
## Transport framing
|
||||
|
||||
| Client | Transport | Framing |
|
||||
|
||||
Reference in New Issue
Block a user