Files
2026-09-02 13:50:03 +00:00

3.9 KiB

id, title, status, wave, depends_on, plan, spec
id title status wave depends_on plan spec
02-lifecycle Kubernetes lifecycle and recovery completed 2
01-backend-foundation
plan.md ../../specs/kubernetes-executor/spec.md

Task 02: Kubernetes lifecycle and recovery

  • Acceptance: CreateInstance provisions/verifies storage and Pod, injects agentctl/config/auth through exec, binds a loopback forward, and completes the existing health/nonce handshake.
  • Acceptance: restart/reconnect verifies exact Pod/PVC UIDs and ownership labels, rotates local forwarding, and surfaces deterministic remote status. Exact-Pod reconnect is independent of the current profile row; replacement of a missing retained Pod uses the recorded non-secret workload snapshot, even after that profile is edited or deleted.
  • Acceptance: ordinary stop preserves resources; terminal/forced cleanup is idempotent, fails closed, and never deletes an existing claim.
  • Verification: cd apps/backend && go test -race ./internal/agent/runtime/lifecycle/...
  • Files likely touched: apps/backend/internal/agent/runtime/lifecycle/executor_kubernetes*.go, executor_backend.go, manager_launch.go, liveness/status files, and adjacent tests.
  • Dependencies: Task 01.
  • Parallelism: parallel-safe with Tasks 03 and 04 after Task 01; owns lifecycle files and metadata allowlists.
  • Inputs: spec State machine/Persistence/Failure modes; ADR exact identity rules; Docker and SSH reconnect/cleanup patterns.

Results

Implemented the real Kubernetes lifecycle behind the existing ExecutorBackend boundary. Fresh launch now composes and admission-validates the exact Pod/PVC, persists provisional and admitted identities before later fallible phases, materializes agentctl/auth/config/skills through exec, starts a loopback-only WebSocket-primary/SPDY-fallback forward, and completes the managed health/nonce handshake. The streaming transport honors Kubernetes REST auth, proxy, TLS, and custom dial hooks; cancellation closes a blocked handshake without coupling a successful upgraded stream to its deadline.

The live Kind launch also established the secret-store boundary used by that bootstrap: lifecycle internals receive the raw store so private kandev-runtime:* nonce/auth records can be materialized, while SSH, Sprites, credential providers, and other user-visible consumers receive the filtered UserVisibleStore. This keeps runtime-only records inaccessible through public secret surfaces without blocking managed Kubernetes startup.

Reconnect, restart, active refresh, model switch, and sibling-session reuse use the recorded six-field resource identity and immutable workload snapshot. Exact UID/resource-version and complete ownership-label checks guard deletion. Ordinary stop retains resources, terminal cleanup preserves durable inventory when teardown fails, existing claims are never deleted, and restart recovery can reconstruct cleanup without an in-memory execution.

Verification completed on 2026-08-24:

  • cd apps/backend && golangci-lint run ./... passed with 0 issues after behavior-preserving complexity, duplication, nesting, constant, and test-file refactors.
  • cd apps/backend && go test -race ./internal/agent/runtime/lifecycle/... passed (lifecycle 43.627 seconds; lifecycle/skill 1.052 seconds).
  • cd apps/backend && go test -race ./internal/agent/kubernetes -count=1 passed, including blocked WebSocket/SPDY cancellation, post-upgrade survival, REST custom-dial preservation, and cancellation/detach race coverage.
  • cd apps/backend && go test ./internal/orchestrator/... and go test ./internal/backendapp -run 'Test.*Kubernetes' -count=1 passed.
  • cd apps/backend && go test ./internal/backendapp -run 'TestLifecycleSecretStoresSeparateRuntimeAndUserCredentialSecrets|Test.*Kubernetes' -count=1 passed after the live Kind secret-store correction.
  • gofmt, go mod tidy -diff, go mod verify, and git diff --check passed.