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 |
|
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 with0 issuesafter behavior-preserving complexity, duplication, nesting, constant, and test-file refactors.cd apps/backend && go test -race ./internal/agent/runtime/lifecycle/...passed (lifecycle43.627 seconds;lifecycle/skill1.052 seconds).cd apps/backend && go test -race ./internal/agent/kubernetes -count=1passed, including blocked WebSocket/SPDY cancellation, post-upgrade survival, REST custom-dial preservation, and cancellation/detach race coverage.cd apps/backend && go test ./internal/orchestrator/...andgo test ./internal/backendapp -run 'Test.*Kubernetes' -count=1passed.cd apps/backend && go test ./internal/backendapp -run 'TestLifecycleSecretStoresSeparateRuntimeAndUserCredentialSecrets|Test.*Kubernetes' -count=1passed after the live Kind secret-store correction.gofmt,go mod tidy -diff,go mod verify, andgit diff --checkpassed.