Spec Overview¶
Steerable follows a single schema, multi-language workflow:
- Define JSON Schemas in
spec/ - Generate TypeScript interfaces (+ Zod) and Python Pydantic models
- Verify behavior parity with conformance tests against
tests/conformance/cases/
Schema folders¶
| Folder | Models |
|---|---|
spec/events/ |
SSEEvent (streaming envelope, see Events) |
spec/tools/ |
ToolCall, ToolResult (see Tools) |
spec/chat/ |
ChatMessage, ChatAgent (see Chat) |
spec/safety/ |
CommandSafetyPattern (see Safety) |
spec/runtime/ |
AgentSession, HarnessTrace, TraceSpan, TraceEvent (Runtime) |
spec/sidecar/ |
SidecarRequest, SidecarResponse, SidecarNotification, SidecarHealth, SidecarError (Sidecar) |
Generation pipeline¶
pnpm gen # writes packages/agent-protocol/ts/src/generated/*.ts
uv run python scripts/generate_py.py
# writes packages/agent-protocol/py/src/steerable_agent_protocol/generated.py
Drift detection¶
pnpm check:drift # TypeScript: re-runs generator and diffs output
uv run python scripts/check_drift.py # Python equivalent
Both run in CI on every PR; a hand-edited generated file fails the build.
Lock-step versioning¶
All 7 publishable packages (TS protocol/harness/ui + Py protocol/harness/runtime/sidecar) are kept at the same version by scripts/check_lockstep_versions.py (CI-enforced on every tag push). Releases are operator-driven: ./scripts/release/bump_to.sh X.Y.Z is the only writer of versions, and the lockstep gate in .github/workflows/release.yml refuses any tag whose source tree disagrees with the tag.
This is stricter than the original protocol-only lockstep — TS and Py implementations of agent-protocol (the codegen pair) plus the four other packages all move together. The cost is a few extra registry versions on no-op packages per release; the benefit is partial-bump corruption is structurally impossible.
Forward / backward compatibility rules¶
- Adding a new optional field is non-breaking — bump patch.
- Adding a new value to a typed enum (e.g. a new
SSEEvent.type) is minor — old consumers should ignore unknown variants. - Removing a field, or making an optional field required, is breaking — major bump only.
- New schemas (entire new model files) are minor.
The TypeScript codegen treats every event payload as having
additionalProperties: true so future event types in the wire stream
won't crash a stale consumer.