Skip to content

feat: add Execution / Worker / Agent / Message Pydantic models (additive)#31

Merged
mikemolinet merged 1 commit into
mainfrom
feat/sdk-additive-model-classes
May 11, 2026
Merged

feat: add Execution / Worker / Agent / Message Pydantic models (additive)#31
mikemolinet merged 1 commit into
mainfrom
feat/sdk-additive-model-classes

Conversation

@mikemolinet
Copy link
Copy Markdown
Collaborator

Summary

Closes the remaining model_drift items in cueapi-python #24's parity manifest. Adds 4 new model files + 11 new exported classes covering response shapes that resource methods currently return as raw dicts.

Additive only — resource methods still return raw dict. No breaking change. Callers opt into typed accessors via Model.model_validate(dict). Promoting resource methods to return typed objects directly is a separate breaking-change PR (would warrant a major version bump).

New models

File Classes Coverage
cueapi/models/execution.py Execution, ExecutionList, OutcomeDetail All 13 fields the manifest flagged as missing on Execution: payload (PR #589), outcome, outcome_state, triggered_by, evidence_*, claimed_by_*, chain_*, last_heartbeat_at
cueapi/models/worker.py Worker, WorkerList heartbeat_status (online / stale / dead), seconds_since_heartbeat, handlers
cueapi/models/agent.py Agent, AgentList Phase 12.1.5 identity surface; webhook_secret field captures one-time-on-create / one-time-on-regenerate contract
cueapi/models/message.py Message, MessageList, FromAgentRef, StateTransitionResponse Phase 12.1.5 message lifecycle; Message.from_agent aliases server's from field

Design notes

  • extra="allow" on every new model so the server can grow response shapes without breaking SDK callers. Same pattern as AlertConfig / VerificationConfig from feat: expand Cue model with 8 missing fields (drift fix-up) #29.

  • Message.from_agent aliases server's from field via Field(alias="from"). from is a reserved keyword in Python so the SDK exposes it as from_agent while still parsing the server's from on the wire. populate_by_name=True lets callers use either name on construction.

  • All 11 new classes exported from cueapi.__init__ for ergonomic access:

    from cueapi import Agent, Execution, Message, Worker
    
    ex = Execution.model_validate(client.executions.get("exec_x"))
    print(ex.outcome.success, ex.outcome_state)  # typed access

Tests

17 new (93 → 110 total unit tests). Coverage:

  • Minimal vs full responses parse cleanly
  • Forward-compat: unknown fields land in model_extra instead of raising (pinned by test_forward_compat_extra_field)
  • fromfrom_agent alias roundtrip
  • webhook_secret one-time-view shape on Agent
  • All new classes are exported from top-level + are BaseModel subclasses

No hosted-PR dependency

All response shapes already shipped on prod. Pure SDK catch-up.

Companion PRs from this session

  • #25mark_verified bug fix + replay()
  • #26WorkersResource + UsageResource
  • #27AgentsResource
  • #28MessagesResource
  • #29 — Cue model 8-field expansion
  • #30fire(send_at=...) (#618 port)

After this lands, the entire model_drift section of cueapi-python #24's manifest is closed.

🤖 Generated with Claude Code

govindkavaturi-art pushed a commit that referenced this pull request May 6, 2026
#32)

* ci(test): spin up cueapi-core locally instead of hitting staging with ARGUS_STAGING_KEY

The `test` job's `pytest tests/` runs the SDK's CRUD tests against a real
CueAPI server. Previously that server was remote staging, authenticated by
`secrets.ARGUS_STAGING_KEY`. Argus was retired 2026-05-02 (cueapi PR #539)
and that key/user is no longer valid — every PR's `test` job has been
red for ~3 days with `AuthenticationError: Invalid API key`, blocking
PRs #30 and #31 (and any future SDK PRs).

This switches the job to the same self-contained pattern that's already
proven by the passing `sdk-integration` job: clone cueapi-core, install,
migrate, boot uvicorn locally, register a fresh test user via
POST /v1/auth/register (gated by ALLOW_REGISTER=true), capture the key,
plumb it through the existing `CUEAPI_STAGING_URL` / `CUEAPI_STAGING_API_KEY`
env vars (no SDK code change needed — `tests/conftest.py` already reads
them from env).

Workflow-only diff. No SDK behavior change.

Note: `notify-merge` still references `secrets.ARGUS_CUEAPI_KEY` for the
post-merge prod telemetry cue. That key is also stale, but the step runs
after auto-merge so it doesn't gate the PR — leaving for a follow-up that
needs a new prod key minted by an operator.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* ci(test): replace alembic with Base.metadata.create_all (matches cueapi-core conftest)

First push failed because OSS migration set ends at 023 while the User
model declares an `api_key_encrypted` column with no migration backing it.
That's a parity drift in cueapi-core (private migration 019 in the hosted
repo includes the column; the OSS port renamed/replaced it with the
alert-webhook bits but kept the column on the model). `alembic upgrade
head` produced a schema missing that column → register endpoint 500'd
on the User SELECT.

Switch the CI bootstrap to model-driven schema init via
`Base.metadata.create_all`, which is the exact pattern cueapi-core's own
`tests/conftest.py` uses (and which is robust to model/migration drift
because the model is the source of truth for tests).

Imports the same model list as conftest so all tables register before
create_all runs.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@govindkavaturi-art govindkavaturi-art enabled auto-merge (squash) May 7, 2026 00:59
…ive)

Closes the remaining `model_drift` items in cueapi-python #24's parity
manifest. Adds 4 new model files + 7 new exported classes covering the
response shapes that resource methods currently return as raw dicts.

**Additive only** — resource methods still return raw `dict` (no breaking
change to return types). Callers opt into typed accessors via
`Model.model_validate(dict)`. Promoting resource methods to return typed
objects is a separate breaking-change PR and would warrant a major version
bump.

New files:

- `cueapi/models/execution.py` — `Execution` + `ExecutionList` +
  `OutcomeDetail` (typed outcome inline). Covers all 13 fields the
  manifest flagged as missing on Execution: `payload` (PR #589),
  `outcome`, `outcome_state`, `triggered_by`, evidence_*, claimed_by_*,
  chain_*, last_heartbeat_at.

- `cueapi/models/worker.py` — `Worker` + `WorkerList`. Captures
  `heartbeat_status` (online / stale / dead),
  `seconds_since_heartbeat`, `handlers` list.

- `cueapi/models/agent.py` — `Agent` + `AgentList` (Phase 12.1.5
  messaging primitive identity surface). Mirrors server's
  `AgentResponse`. `webhook_secret` field captures the one-time-on-create
  / one-time-on-regenerate contract.

- `cueapi/models/message.py` — `Message` + `MessageList` +
  `FromAgentRef` (inline sender reference) + `StateTransitionResponse`
  (read / ack response shape).

Notable design choices:

- All new models use `extra="allow"` so server can grow response shapes
  without breaking SDK callers. Same pattern already in use on
  `AlertConfig` + `VerificationConfig` from #29.

- `Message.from_agent` aliases the server's `from` field via
  `Field(alias="from")` — `from` is a reserved keyword in Python so
  the SDK exposes it as `from_agent` while still parsing server's `from`
  on the wire. `populate_by_name=True` lets callers use either name on
  construction.

- All 11 new classes exported from `cueapi.__init__` for ergonomic
  access (`from cueapi import Agent, Message, ...`).

Tests: 17 new (93 → 110 total). Coverage:
- Minimal vs full responses parse cleanly
- Forward-compat: unknown fields land in `model_extra` instead of
  raising
- `from` → `from_agent` alias roundtrip
- `webhook_secret` one-time-view shape on Agent
- All new classes are exported from top-level + are BaseModel subclasses

🤖 Generated with [Claude Code](https://claude.com/claude-code)
@mikemolinet mikemolinet force-pushed the feat/sdk-additive-model-classes branch from 7414093 to f74ca30 Compare May 11, 2026 23:49
@mikemolinet mikemolinet merged commit bda862b into main May 11, 2026
4 checks passed
@mikemolinet mikemolinet deleted the feat/sdk-additive-model-classes branch May 11, 2026 23:52
mikemolinet added a commit that referenced this pull request May 12, 2026
…dy coverage, bump audit to 2026-05-12 (#43)

Manifest was dated 2026-05-07 and missing the PR-1b event-emit endpoint
coverage (4 endpoints) plus the body-verify Phase 2 + inline_body
extensions that shipped 2026-05-09 → 2026-05-12. Brings the manifest
back in sync with SDK head.

Endpoints added to `endpoints_covered`:
- POST /v1/agents/{ref}/subscriptions (subscriptions_create, PR #38;
  inline_body kwarg in PR #42 / cueapi #791 Item 1)
- GET /v1/agents/{ref}/subscriptions (subscriptions_list, PR #38)
- DELETE /v1/agents/{ref}/subscriptions/{sub_id} (subscriptions_delete,
  PR #38)
- GET /v1/agents/{ref}/events (events_pull, PR #38)

Updates to existing entries:
- POST /v1/messages — added auto_verify body-verify Phase 2 (PR #39 +
  #40, cueapi/cueapi #795 + #798 parity)
- POST /v1/cues/{id}/fire — note that #33 shipped (was "in-flight")
- GET /v1/agents/roster — note that #35 shipped (was "in-flight")
- GET /v1/agents/{ref}/presence — note that #35 shipped (was "in-flight")

Replaced `in_flight_ports_2026_05_07` section with
`ports_shipped_2026_05_08_to_2026_05_12` (now-resolved entries) plus a
near-empty `ports_in_flight_2026_05_12` placeholder for future ports.

Backlog row: cmp1vukmc.

Out of scope:
- `model_drift` section walk-through (Cue/Execution/Worker missing
  fields) — PR #31 just landed the Execution + Worker + Agent +
  Message additive models; a fuller `model_drift` refresh deserves a
  separate audit pass against the now-shipped models to figure out
  what's still drifting.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

1 participant