Files
clawdbot/docs/typebox.md
2025-12-09 15:18:34 +01:00

2.4 KiB
Raw Blame History

TypeBox as Protocol Source of Truth

Last updated: 2025-12-09

We use TypeBox schemas in src/gateway/protocol/schema.ts as the single source of truth for the Gateway control plane (hello/req/res/event frames and payloads). All derived artifacts should be generated from these schemas, not edited by hand.

Current pipeline

  • TypeBox → JSON Schema: pnpm protocol:gen writes dist/protocol.schema.json (draft-07) and runs AJV in the server tests.
  • TypeBox → Swift (quicktype): pnpm protocol:gen currently also generates apps/macos/Sources/ClawdisProtocol/Protocol.swift via quicktype. This produces a single struct with many optionals and is not ideal for strong typing.

Problem

  • Quicktype flattens oneOf/discriminator into an all-optional struct, so Swift loses exhaustiveness and safety for GatewayFrame.

Preferred plan (next step)

  • Add a small, custom Swift generator driven directly by the TypeBox schemas:
    • Emit a sealed enum GatewayFrame: Codable { case hello(Hello), helloOk(HelloOk), helloError(...), req(RequestFrame), res(ResponseFrame), event(EventFrame) }.
    • Emit strongly typed payload structs/enums (Hello, HelloOk, HelloError, RequestFrame, ResponseFrame, EventFrame, PresenceEntry, Snapshot, StateVersion, ErrorShape, AgentEvent, TickEvent, ShutdownEvent, SendParams, AgentParams, ErrorCode, PROTOCOL_VERSION).
    • Custom init(from:) / encode(to:) enforces the type discriminator and can include an unknown case for forward compatibility.
    • Wire a new script (e.g., pnpm protocol:gen:swift) into protocol:check so CI fails if the generated Swift is stale.

Why this path:

  • Single source of truth stays TypeBox; no new IDL to maintain.
  • Predictable, strongly typed Swift (no optional soup).
  • Small deterministic codegen (~150200 LOC script) we control.

Alternative (if we want off-the-shelf codegen)

  • Wrap the existing JSON Schema into an OpenAPI 3.1 doc (auto-generated) and use swift-openapi-generator or openapi-generator swift5. More moving parts, but also yields enums with discriminator support. Keep this as a fallback if we dont want a custom emitter.

Action items

  • Implement protocol:gen:swift that reads the TypeBox schemas and emits the sealed Swift enum + payload structs.
  • Update protocol:check to include the Swift generator output in the diff check.
  • Remove quicktype output once the custom generator is in place (or keep it for docs only).