Skip to content

feat(studio-cp,oab-mcp): k8s roster observe + runtime_context dispatch - #149

Merged
brettchien merged 1 commit into
mainfrom
orca/k8s-roster-mcp-dispatch
Sep 7, 2026
Merged

feat(studio-cp,oab-mcp): k8s roster observe + runtime_context dispatch#149
brettchien merged 1 commit into
mainfrom
orca/k8s-roster-mcp-dispatch

Conversation

@brettchien

Copy link
Copy Markdown
Contributor

Summary

studio#146 slice 2 of 4 (k8s fleet roster/observe UI). Builds on slice 1 (#148, merged) to add the actual k8s observe path, and wires oab-mcp to dispatch to it for a k8s-runtime fleet.

crates/studio-cp — the k8s counterpart to the existing ECS read-model (observe_services/observe_deployment/build_deployment):

  • observe_k8s_services(context, namespace): lists OAB-managed Deployments (oab/name label present) in a namespace — the k8s counterpart to observe_services.
  • observe_k8s_deployment(context, namespace, service): one Deployment's replica counters + per-Pod phase, listing its Pods by the Deployment's own selector and mapping each through agent_lifecycle::k8s::K8sDriver (from feat(agent-lifecycle): k8s RuntimeDriver — projects Pod state onto the 6-state model #148) — same Deployment/InstancePhase output type ECS produces, no new console-facing shape.
  • Pod → phase mapping reads phase, the Ready condition, whether a readiness probe is declared, and CrashLoopBackOff from container statuses — the native signals K8sDriver::project expects.

crates/oab-mcpdeploy_list/deploy_get/runtime_context now check the named fleet's runtime and branch to the k8s path (the new studio-cp functions, and the already-existing observe_k8s_identity for runtime_context) before falling through to the existing ECS logic. target() itself is untouched and still rejects k8s fleets — every other tool (scale/delete/apply/provision/events) has no k8s dispatch yet and should keep erroring loudly rather than silently acting against an empty cluster string. Tool descriptions updated to mention k8s support where applicable.

A naming wrinkle worth flagging explicitly: a k8s Deployment's own on-cluster resource name is oab-{slug(name)} (k8s_driver.rs's k8s_deployment_name, no namespace embedded — namespace is a first-class k8s field there). But fleets.toml's members array stores oab-{namespace}-{name} (console/src/deploy.ts's service = oab-${namespace}-${name}, written unconditionally for both runtimes). observe_k8s_services/observe_k8s_deployment reconstruct the fleets.toml form from the Deployment's oab/name label (which k8s_driver.rs::build_deployment sets to the original, un-slugified agent name) rather than using the on-cluster resource name, so FleetBinding::includes matches the same way it does for ECS's full-vs-short dual match. Flagging this because it's exactly the kind of naming mismatch that reads as "roster shows nothing" if missed.

runtime_context's k8s response shape: cluster is null, context/namespace are set instead of it — noting this for whoever picks up slice 4 (console read-path wiring), since that's the exact JSON shape it'll need to branch on.

Test plan

  • cargo test -p studio-cp --lib — 35/35 passing (no regressions; the new k8s observe functions need a live cluster so aren't unit-tested here, same as observe_k8s_identity/list_namespaces before them)
  • cargo test -p oab-mcp --lib — 3/3 passing (incl. catalog_advertises_the_named_tools, unaffected by the description-only tool-schema edits)
  • cargo check -p studio-cp -p oab-mcp — clean
  • Full workspace build/test — left to CI per this sandbox's resource constraints (aws-sdk-ec2 OOM'd the sandbox's default test-profile debuginfo; worked around locally with CARGO_PROFILE_TEST_DEBUG=0, not something to carry into the PR itself)
  • Manual: exercise deploy_list/deploy_get/runtime_context against a real k8s-runtime fleet — no live cluster available in this sandbox

🤖 Generated with Claude Code

studio#146 slice 2 of 4. Builds the k8s counterpart to the ECS read-model
(observe_services/observe_deployment/build_deployment), producing the same
runtime-agnostic Deployment/InstancePhase shape via slice 1's K8sDriver
instead of EcsDriver:

- studio-cp: observe_k8s_services (list OAB-managed Deployments in a
  namespace, via the `oab/name` label) and observe_k8s_deployment (one
  Deployment's replica counters + per-Pod phase, mapping a live Pod's
  phase/readiness/CrashLoopBackOff onto the canonical 6-state).
- oab-mcp: deploy_list/deploy_get/runtime_context now dispatch on a named
  fleet's runtime — k8s calls the new studio-cp functions (and the
  already-existing observe_k8s_identity for runtime_context), ecs is
  unchanged. `target()` itself and every other tool (scale/delete/apply/
  provision/events) stay ecs-only on purpose — this only touches the three
  calls that are actually runtime-aware now.

Fleet-membership matching note: a k8s Deployment's own on-cluster resource
name is `oab-{slug(name)}` (k8s_driver.rs, no namespace embedded), but
fleets.toml's `members` stores `oab-{namespace}-{name}` (deploy.ts writes
this uniformly for both runtimes) — observe_k8s_services/
observe_k8s_deployment reconstruct the latter from the Deployment's
`oab/name` label so FleetBinding::includes matches correctly.

🤖 Generated with Claude Code
@brettchien
brettchien merged commit 9233883 into main Sep 7, 2026
2 checks passed
@brettchien
brettchien deleted the orca/k8s-roster-mcp-dispatch branch September 7, 2026 10:20
brettchien added a commit that referenced this pull request Sep 7, 2026
…ge commands (#150)

studio#146 slice 3 of 4. `deploy_list` and `runtime_context` (the two
Tauri bridge commands backing the console's roster/identity reads) gain
an optional `fleet: Option<String>` parameter, forwarded to the
underlying `deploy_list`/`deploy_get`/`runtime_context` MCP tool calls
alongside `cluster`. This is what a k8s-runtime fleet's roster/identity
needs to reach oab-mcp's new k8s dispatch (studio#146 slice 2, PR #149) —
`cluster` alone has no k8s equivalent to resolve against.

No Rust compile-time dependency on slice 2 — the MCP call is JSON/string
dispatched, not typed, so this only needs the tool arg *contract* (stable,
defined in #149) not its merge. `fleet` is a new trailing optional
parameter on two already-registered `#[tauri::command]`s, so no
`generate_handler!` change is needed, and existing callers that omit it
keep their exact current (cluster-only) behavior — nothing in the console
passes `fleet` yet, that's slice 4.

Per studio-tauri-bridge-layer-gotcha: this layer has no local build
signal in this sandbox (`studio-desktop` isn't a workspace member, needs
macOS/GTK system libs this environment doesn't have) — reviewed by hand
against the surrounding bridge functions' existing pattern (the
`params["provider"] = json!(p)` idiom `deploy_provision_agent`'s bridge
already uses), only `bundle-macos` CI verifies it actually builds.

🤖 Generated with Claude Code
brettchien added a commit that referenced this pull request Sep 7, 2026
…151)

studio#146 slice 4 of 4 (final slice) — the console-side fix for the
originally-reported bug: clicking a k8s-runtime fleet in the Fleets panel
now actually opens Fleet detail and loads its roster, instead of
`selectFleet` showing an info toast and refusing to switch.

- `source.ts`: `listDeployments`/`runtimeContext` gain an optional `fleet`
  param, forwarded to the Tauri bridge (#150) alongside `cluster`.
- `main.ts`: `selectFleet` branches on `fleet.runtime` instead of
  declining for k8s — `activeCluster` becomes an unused "" sentinel for a
  k8s fleet (reads now go by `fleet` name, which oab-mcp resolves to the
  bound context/namespace, #149), and the header label shows
  `context/namespace` instead of a cluster. `tick()` now always passes
  `activeFleet` as `fleet` to `listDeployments` (a no-op for the
  no-fleet-selected case, and for an ecs fleet this only makes oab-mcp's
  existing member-side filtering redundant with the console's own
  `filterByMembers` — same result, not a behavior change).
- `types.ts`: `RuntimeContext.cluster` is `string | null` now (`null` for
  a k8s-runtime fleet, with `context`/`namespace` set instead); `FleetBinding`
  gained optional `context`/`namespace` fields alongside the existing
  `profile`/`region`. `runtimeContext()` itself has no console call site
  yet (true before this change too) — this is type-contract parity with
  oab-mcp's tool, not new UI.

**Not in scope** (matches the issue body): start/stop (scale) for a k8s
roster row isn't wired — `scale()` still calls `scaleDeployment` without a
`fleet` arg, so it'll hit the ecs-only `deploy_scale` path and fail with a
clear error toast (not silently) if attempted against a k8s deployment.
That's a separate action-surface follow-up, not blocking this read-only
roster view.

## Test plan

- `npm test`: 107/107 passing (no new tests — this is control-flow
  wiring over already-tested pure functions; `filterByMembers`/
  `serviceName` already have k8s-shaped-data coverage from the delete-fleet/
  member-name-preview work).
- `npm run typecheck`: clean.
- `npm run build`: clean.
- Manual: no live k8s cluster in this sandbox to exercise end-to-end —
  same caveat #149 already carries.

🤖 Generated with Claude Code
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