feat: studio control-plane — agent-lifecycle + vendored oabctl + MCP (slice-0) - #2
Merged
Conversation
Implements docs/adr/agent-lifecycle.md: - AgentState (6), Discriminator (4 axes incl. latching identity_verified), classify() with the Starting/Unhealthy split the latch resolves. - IdentityLatch: CP-owned monotonic identity_verified bit. - RuntimeDriver trait: observe -> project -> classify. - EcsDriver: ECS lastStatus/desiredStatus/healthStatus/lease/cordon projection (DescribeTasks wiring deferred to a later slice). - Unit tests incl. F1 (latch), superseded=>Paused, Unknown=>Unhealthy, Stopping. NOT compile-verified locally (no cargo toolchain on the authoring runtime) — needs cargo test in CI / on Brett's box. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Vendor the oabctl crate (ECS provisioner CLI+lib) from openabdev/openab operator/ @ d64c678 (MIT, attributed in crates/oabctl/VENDORED.md) so Studio can build control-plane actions + an MCP surface on it in one repo instead of across repos. Wire it into the workspace alongside agent-lifecycle. Add GitHub Actions CI (fmt/clippy on our crates; build+test the whole workspace) — the authoring runtime has no cargo toolchain, so CI is the compile/test verification for both the vendored crate and agent-lifecycle. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
No rustfmt on the authoring runtime, so style is informational for now; the hard gate is that the whole workspace (vendored oabctl + agent-lifecycle) compiles and tests pass. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
How oabctl exposes to Studio (PR #2's real integration point): - oabctl: add public library status API crates/oabctl/src/status.rs (ServiceStatus struct + service_status()) — the data half of `oabctl get`, returning structs instead of printing a table. Additive + upstream-shaped. - studio-cp: new downstream crate depending on oabctl + agent-lifecycle; observe_services() consumes oabctl::service_status. This is the seam where the 6-state mapping and a future MCP surface attach. oabctl stays clean (additive pub API only); Studio-specific logic lives downstream in studio-cp — keeping the vendored crate upstream-contributable. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
- oabctl: add InstanceStatus + instance_status() (ListTasks + DescribeTasks) — the per-Task granularity ADR-2 Claim 3 requires (DescribeServices is service-level only). Additive, upstream-shaped. - studio-cp: instance_phase() maps an ECS InstanceStatus onto AgentState via agent-lifecycle's EcsDriver (last_status/health_status → discriminators → classify). Admission/lease are CP-level (not ECS-observable) and default here. - Tests for the mapping (ACTIVATING->Starting, RUNNING+healthy->Running, RUNNING+unhealthy(verified)->Unhealthy, desired-stopped->Stopping). Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ADR-2 §4 read-model slice-2, in studio-cp:
- Deployment { name, namespace, desired, current, ready, instances:[InstancePhase] }
— Deployment-level counters (NOT an AgentState) + per-Instance phase.
- build_deployment(ServiceStatus, [InstanceStatus]) — pure aggregation; ready =
count of Running instances.
- observe_deployment() wires service_status + instance_status end-to-end.
- latched_verified(): one-shot approximation of the identity_verified latch
from current lastStatus (real latch needs CP-persisted history).
- Test for the aggregation (ready count + per-instance phases).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
ADR-2 read-tools front-end prototype: - studio-cp bin: 'list' -> Deployments + counters (deploy_list); 'get <service>' -> one Deployment's counters + per-Instance phase (deploy_get). Read-only; writes wait on ADR-3 authz. Cluster from $OAB_CLUSTER (default oab). - tokio dep for the async runtime. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Authoring runtime now has cargo+rustfmt; format ecs.rs so the CI fmt step is clean. Whitespace-only; 10 unit tests still pass, clippy -D warnings clean.
Adds the ADR-2 write model as thin passthroughs to oabctl, mirroring the read-side observe_* seam: - oabctl: new additive `studio_api` module (parse_manifests / scale / delete) — a structured, non-interactive surface over the CLI-oriented internals so downstream calls a lib API instead of shelling out. No upstream behaviour changed; recorded in VENDORED.md. - studio-cp: apply_deployment (parse YAML -> programmatic apply -> ApplyReport), scale_deployment, delete_deployment. Next: MCP server (crates/oab-mcp) exposing read+write as tools. Compile-blind locally (aws-sdk-ec2 exceeds this box's RAM); relying on CI.
… MCP) Two things, together so CI compiles them as one unit: 1. Fix wiring the prior commit missed (studio_api.rs was committed but its `pub mod studio_api;` + the studio-cp write fns + VENDORED.md note were not, so the write seam never actually compiled). Now included: - oabctl: pub mod studio_api; (parse_manifests/scale/delete) - studio-cp: apply_deployment / scale_deployment / delete_deployment - VENDORED.md: record the additive studio_api module 2. New crate crates/oab-mcp — a hand-rolled rmcp 1.7 ServerHandler (stdio) exposing the read+write model as six MCP tools: deploy_list, deploy_get, get_agent_states, deploy_apply, deploy_scale, deploy_delete. Thin dispatch into studio-cp; owns only the JSON wire shape. Added to the workspace. Still compile-blind locally (aws-sdk-ec2 > this box's RAM); CI is the gate.
…ist) Compiling isn't running. Add a CI step that starts the built binary and drives a real MCP stdio handshake (initialize -> initialized -> tools/list), asserting all six tools are advertised. Needs no AWS creds — the catalog is static; AWS is resolved lazily on real tool calls.
Addresses PR#2 review items 1-5,7: 1. Config-free scale/delete: studio_api.scale now drives ECS UpdateService via ecsctl directly (cluster/namespace explicit, size 0|1 enforced); studio_api.delete resolves the control-plane bucket from env/account and reuses delete::run_with_bucket (widened to pub(crate)). Neither reads ~/.oabctl/config.toml anymore. Seam + MCP deploy_scale (name/cluster/ namespace, size 0|1) updated to match. 2. Drop RuntimeDriver::observe + the state() convenience (only called by each other, never externally) and remove EcsDriver's unimplemented! stub — the trait is projection-only this slice, no shipped panic. Verified locally: agent-lifecycle 10 tests + clippy green. 3. oab-mcp unit tests: tool catalog (6 named) + deployment_json wire shape. 4. apply_deployment error carries the structured ApplyErrorKind. 7. oab-mcp README (tools, run, env, mcp.json, 0/1 scale semantics). VENDORED.md updated for the studio_api rewrite + run_with_bucket visibility. oabctl/studio-cp/oab-mcp remain compile-blind locally; CI is the gate.
So a runtime that can't compile oab-mcp locally (aws-sdk-ec2 > small-box RAM) can fetch the CI-built binary and drive it as an MCP client.
This was referenced Aug 9, 2026
brettchien
marked this pull request as ready for review
August 9, 2026 15:22
This was referenced Aug 28, 2026
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Realizes the plan: Studio becomes the control-plane, built on a vendored
oabctl, with an MCP surface so an agent operates it first-class. One repo, four crates:In this PR — read + write + MCP (ADR-2 slice-0)
crates/agent-lifecycle— 6-state model, 4-axis discriminator (incl. latchingidentity_verified),RuntimeDriver(projection-only this slice), ECS projection, tests.crates/oabctl— vendored from openabdev/openaboperator/@ d64c678 (MIT, attributed inVENDORED.md). Studio-local additions are additive and listed there: a config-freestudio_api(parse/scale/delete) + one visibility widening.crates/studio-cp— the read/write seam:observe_services/observe_deployment; per-instance ECS status → canonical 6-state; the genericDeploymentread-model (counters + per-instance phase); read-onlystudio-cpCLI.apply_deployment(YAML → programmatic apply →ApplyReport),scale_deployment(0/1),delete_deployment. Config-free: cluster/namespace explicit, control-plane bucket from env/account — no~/.oabctl/config.toml.crates/oab-mcp— hand-rolledrmcp1.7ServerHandlerover stdio, six tools:deploy_list,deploy_get,get_agent_states,deploy_apply,deploy_scale,deploy_delete. See itsREADME.md.oab-mcpand drives a real MCPinitialize→tools/listover stdio.Next slices
DescribeTasks-basedobserve+ thestate()convenience (deferred out of the projection-only trait here).ecs-control-plane.mdPhase 3), identity/lease/epoch.Verification —
crates/agent-lifecycle(zero deps) is built + tested + linted locally:cargo test10 pass,cargo clippy -D warningsclean,cargo fmt --checkclean. Theoabctl/studio-cp/oab-mcpcrates pull inaws-sdk-ec2, whose single-craterustcpeak exceeds the authoring box's RAM (3.7 GiB, no swap), so those are CI-verified (ubuntu-latest): whole-workspace build + test and the MCP stdio smoke test are green.