Skip to content

feat(client): typed shapes for the remaining agent tools; error.data at the boundary - #413

Merged
jrosskopf merged 1 commit into
mainfrom
client/typed-tool-shapes
Aug 15, 2026
Merged

jrosskopf merged 1 commit into
mainfrom
client/typed-tool-shapes

Conversation

@jrosskopf

Copy link
Copy Markdown
Contributor

Summary

Contract-parity fixes (2/2, stacked on #409): the advertised agent-scope tools that had no typed request/response structs or client methods, and the error.data envelope the transport dropped.

  • Typed shapes + client methods for fetch_blob, list_snapshots, list_op_authors, write_instance (wire key ref), and the HTTP session trio open_session/apply_op/close_session. Each struct matches the server handler's wire keys exactly; apply_op.op is the base64 Loro blob as the wire carries it; CloseSessionRequest::default() carries the wire default commit: true (a defaulted close commits, never a silent discard). ExpandResponse also decodes backend_projection so a write_instance caller can do a typed read-after-write.
  • error.data at the client boundary: Error::JsonRpc gains data_code: Option<String> / retryable: Option<bool>, parsed from the gateway's error.data = {code, retryable} — the STABLE app-level refusal code the docs tell callers to branch on (several refusals share -32000). Both None when the envelope carries no data; existing { code, .. } matches are unaffected. (Note: per docs/spec/dx.md the error enum is a breaking-change surface — downstream struct-literal constructions of JsonRpc need the two new fields; the one in-workspace site, escurel-runner-core's reconciler test, is updated here.)

Skill sync in the same PR: references/05-consume-from-rust.md documents the new typed methods + error fields (replacing the stale "the live-CRDT trio and admin methods are added as … the types catch up" deferral); skill CHANGELOG 0.6.28 + VERSION.

Test plan

Real boundary, no mocks (crates/escurel-client/tests/typed_tools.rs, new):

  • session_trio_round_trips_typed — real DuckDB CRDT backend sharing the indexer's DuckDB instance via try_clone (the production single-file wiring): open → apply (real Loro op) → list_op_authors shows the verified principal → close(commit) → expand observes the write-through body.
  • list_snapshots_round_trips_typed — a whole-page write's snapshot is listed through the typed method.
  • fetch_blob_round_trips_typed — real /ingest/upload of a born-digital text document into a document-backend skill, bytes fetched back verbatim (base64, declared content type); a non-document page decodes blob: None.
  • write_instance_round_trips_typed — a real stateful loopback axum CRM as the openapi upstream; endpoint registered + overlay materialised via call_raw (admin plumbing), typed write_instance PATCHes it, live backend_projection read-after-write proves the upstream mutated.
  • error_data_code_and_retryable_surface_typed — a reader_mode gateway refuses update_page and the typed error carries data_code: Some("read_only_replica"), retryable: Some(true).
  • error_without_data_decodes_none — plain protocol error keeps both additions None.
  • crates/escurel-types/tests/wire_contract.rs: write_instance_request_ref_rename, fetch_blob_response_wire_shape, list_op_authors_response_wire_shape, roundtrip_session_tools, close_session_commit_defaults_true.

Gate (un-piped exit codes): cargo fmt --check = 0, cargo clippy --workspace --all-targets -- -D warnings = 0, cargo test --workspace --all-targets = 0 (102 suites ok), cargo build --workspace --release = 0.

🤖 Generated with Claude Code


View with [code]smith Autofix with [code]smith
Need help on this PR? Tag @codesmith-bot with what you need. Autofix is disabled.

…ors/write_instance + session trio; error.data at the boundary

Contract-parity fixes, part 2: the advertised agent-scope tools that
had no typed request/response structs or client methods (tests used
call_raw), and the error.data envelope the transport dropped.

- New escurel-types shapes matching the wire exactly: FetchBlob*
  (blob: Option<BlobInfo> — null = absent/hidden/non-document, one
  indistinguishable answer), ListSnapshots* (taken_at strings, oldest
  first), ListOpAuthors* (OpAuthor{op_id, hlc, applied_at?, principal?}),
  WriteInstance* (wire key `ref`; {ok, source, fields}), and the HTTP
  session trio OpenSession*/ApplyOp*/CloseSession* (apply_op carries the
  base64 Loro blob as the wire does; CloseSessionRequest::default() has
  commit: true, the wire default — a defaulted close commits, never a
  silent discard).
- escurel-client methods for all seven tools. ExpandResponse also
  decodes backend_projection (the sql_view/remote live overlay), so a
  typed write_instance caller can do a read-after-write.
- Error::JsonRpc gains data_code/retryable, parsed from the gateway's
  error.data {code, retryable} — the STABLE app-level refusal code docs
  tell callers to branch on (several refusals share -32000). None when
  the envelope has no data; existing `{ code, .. }` matches unaffected.

Skill sync (same PR): references/05-consume-from-rust.md documents the
new typed methods + error fields (replacing the stale "the live-CRDT
trio ... as the types catch up" deferral); skill CHANGELOG 0.6.28 +
VERSION.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@jrosskopf
jrosskopf merged commit 8171a96 into main Aug 15, 2026
4 checks passed
@jrosskopf
jrosskopf deleted the client/typed-tool-shapes branch August 15, 2026 09:23
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