Skip to content

refactor: #203 follow-up — agentkeys-protocol wire crate (wasm-safe) + ts-rs frontend bindings#215

Open
hanwencheng wants to merge 1 commit into
mainfrom
claude/charming-shockley-d51949
Open

refactor: #203 follow-up — agentkeys-protocol wire crate (wasm-safe) + ts-rs frontend bindings#215
hanwencheng wants to merge 1 commit into
mainfrom
claude/charming-shockley-d51949

Conversation

@hanwencheng

Copy link
Copy Markdown
Member

Summary

B1 + B2 of the frontend-testability / CLI↔web-parity plan (docs/plan/frontend-testability-cli-web-parity.md), implemented after a Codex adversarial review. Both push the broker/worker wire contract down the parity ladder (CLAUDE.md "Parity/wiring checks evolve down a ladder"): from hand-mirrored copies that drift silently → to a single shared definition where drift is a compile error / CI-red.

Builds on #213 (which removed the in-memory MCP backend + collapsed the Backend wrapper) — that was B0.

B1 — agentkeys-protocol: one wasm-safe wire-type crate

  • Extracted the broker/worker wire types (protocol.rs) into a standalone agentkeys-protocol crate — pure serde, no transport, compiles to wasm32.
  • agentkeys-backend-client (native, + STS via the provisioner) re-exports them as ::protocol (back-compat — every existing agentkeys_backend_client::protocol::* path still resolves).
  • agentkeys-web-core (browser/wasm) now shares the same crate instead of its own copy — killing a live cap-mint drift: ttl_seconds was a required u64 in backend-client but Option<u64> in web-core. The shared on-wire BrokerCapRequest uses Option<u64> + skip (faithful to the broker's #[serde(default = "default_ttl_seconds")]); the native caller-side CapMintRequest stays u64zero ripple to mcp-server/daemon, and the wire stays byte-identical (it always sends Some(..)).
  • Why not depend web-core on agentkeys-backend-client directly? (the Codex finding) — that crate pulls aws-sdk-sts + tokio + native reqwest via the provisioner and breaks the wasm build. Sharing only the pure-serde protocol crate avoids that.
  • New CI gate (harness-ci.yml rust-checks): cargo check --target wasm32-unknown-unknown -p agentkeys-web-core (default + --features wasm) — fails if a native dep ever leaks into the browser build.

B2 — ts-rs: generate the frontend Api* wire types

  • The 12 ui_bridge.rs Api* structs derive ts_rs::TS and export to apps/parent-control/lib/generated/*.ts.
  • apps/parent-control/lib/client/daemon.ts imports the generated types and drops the 5 hand-declared interfaces (−49 lines). A daemon-side field rename is now a frontend compile error instead of silent drift (rung 2 → rung 3).
  • Type fidelity: u64number (#[ts(type = "number")]; ts-rs defaults to bigint, which the JSON wire + mappers don't use); skip-serialize Options → optional ?: (#[ts(optional)]).
  • New CI gate: cargo test regenerates the bindings, then git diff --exit-code apps/parent-control/lib/generated/ fails if a struct changed without committing the regenerated .ts (same discipline as the backend-protocol fixtures).

Verification

  • cargo build --workspace ✓ · cargo test (protocol + backend-client + web-core + daemon export_bindings) ✓ · cargo clippy -D warnings (native + daemon) ✓
  • cargo check --target wasm32-unknown-unknown -p agentkeys-web-core (default + --features wasm) ✓ — confirmed no aws-sdk-sts/tokio in the wasm dep tree.
  • npm run typecheck ✓ — clean except 2 pre-existing core.ts errors (the lib/wasm/ wasm-pack output isn't built in a fresh checkout; unrelated to this PR).
  • Backend-protocol fixture --check ✓ — cap_mint_request.json byte-unchanged.

What did NOT land (rest of the plan)

  • B3 (cap-token unification) — assessed no-code: CapToken is opaque-by-design (no drift); pairing isn't duplicated. Documented in the plan doc.
  • B4 (one fixture set across Rust+TS) — blocked on Part A (the Vitest harness).
  • B5 (CLI onto the shared client) — audited: the CLI doesn't re-type wire bodies.
  • Part A (frontend Vitest + FakeBackend + Playwright) — not started.
  • Follow-up: the Onboarding config bootstrap + classifier-driven auto-distribution (cred + memory, R1–R4) #207 ApiProposedScope + classify/credentials wire types are still hand-declared in daemon.ts — next ts-rs codegen batch.

🤖 Generated with Claude Code

…+ ts-rs frontend bindings

B1 + B2 of docs/plan/frontend-testability-cli-web-parity.md (after the Codex adversarial review).

B1: extract the broker/worker wire types into a standalone wasm-safe agentkeys-protocol crate (pure serde). agentkeys-backend-client re-exports them as ::protocol (back-compat); agentkeys-web-core (wasm) now shares them instead of its own copy, killing the cap-mint ttl_seconds drift (u64 vs Option<u64>). The shared on-wire BrokerCapRequest uses Option<u64> + skip (faithful to the broker's serde default); the native caller-side CapMintRequest is unchanged (no ripple to mcp/daemon). New CI gate: cargo check --target wasm32-unknown-unknown -p agentkeys-web-core proves no native deps (aws-sdk-sts/tokio) leak into the browser build.

B2: generate the frontend Api* wire types from the daemon's ui_bridge.rs structs via ts-rs (apps/parent-control/lib/generated/); daemon.ts imports them and drops the 5 hand-declared interfaces (-49 lines). u64 -> number, skip-serialize Options -> optional. New CI gate: cargo test regenerates + git diff --exit-code on the generated dir, so a daemon struct rename is a frontend compile error.

Verified: cargo build/test/clippy -D warnings (native + wasm32), npm run typecheck (clean except pre-existing core.ts wasm-artifact errors), fixture --check unchanged.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant