Skip to content

✨ feat(runtime): observe CPA readiness with safe health probes - #780

Merged
seakee merged 1 commit into
v2from
feat/v2-runtime-readiness-observation
Sep 15, 2026
Merged

seakee merged 1 commit into
v2from
feat/v2-runtime-readiness-observation

Conversation

@seakee

@seakee seakee commented Sep 15, 2026

Copy link
Copy Markdown
Owner

Summary

Configured Embedded Runtime status previously stayed unknown after lifecycle operations. Compute readiness on demand from the owned CPA child and a bounded local HEAD /healthz probe, fencing each result to the same child instance.

Allow Manager to consume ready without an observed version because the safe health route provides none. This Runtime 10 slice starts exactly at v2@80a49128ef64a8d058bc1acd0d2344cfd8fcd7b8.

Scope

  • Frontend panel
  • Manager Server
  • CPA panel mode
  • Full Docker mode
  • Native packages / release
  • Docs / Wiki
  • CI / build / tooling

Runtime Supervisor implementation and regression tests are included.

Changes

  • Add a process-local child instance ID allocated only after successful spawn, retained through reap, and never reused, persisted, or exposed as protocol authority. Exact StopTarget ownership remains unchanged.
  • Inject a read-only status observer using the same process manager as the existing lifecycle executor. Handshake and unauthorized status requests never probe.
  • Add Supervisor-private CPAMP_RUNTIME_CPA_ADDR, default 127.0.0.1:8317; reject invalid/non-loopback addresses. Use a fixed /healthz path, HTTP 200 only, a 1-second timeout, direct transport with Proxy=nil, fresh connections, and no redirects.
  • Preserve Start/Stop/Restart completion, durable ordering, same-ID replay, cancellation transfer, shutdown admission, RuntimeGeneration, and exact start, stop, restart capabilities.
  • Permit ready with an empty CPAObservedVersion; retain nonempty observations verbatim and the External adapter's existing authenticated version requirement.
  • Amend ADR-0001 and cover health failures, authorization/secret boundaries, concurrent observation, PID reuse, real-child Restart races, and unchanged journal evidence.
Process / probe observation Status
Read-only Supervisor or unconfirmed ownership unknown
Not started or confirmed exited/reaped offline
Running, but probe fails or belongs to a replaced child starting
Same owned child running before/after /healthz HTTP 200 ready

User Impact

Authenticated Embedded status reports local CPA availability. Start and Restart still complete at spawn/ownership publication; callers observe readiness separately through status.

Compatibility / Runtime Notes

  • CPA panel mode: existing behavior is unaffected.
  • Manager Server mode: Embedded accepts ready without a version; External still errors when its authenticated Management observation lacks a version.
  • Full Docker / native packages: no wiring or packaging changes. The new probe target is Supervisor-local and its environment variable is filtered out of the CPA child environment.
  • Provider/model requests, credential/quota health, crash recovery, background polling/cache, expected-version gates, Manager reconcile, and operation recovery remain outside this PR.

Data / Security Notes

CPA Management Key authority stays with Manager encrypted storage. The probe does not receive Management credentials, contact /v0/management/*, trigger intentional Management authentication failures, or forward caller credentials. Supervisor does not read Manager DB/product configuration or data.key.

Status does not write the operation journal. Child instance identity is excluded from JSON and is neither PID authority nor RuntimeGeneration. CPA observed version remains empty rather than using configuration or health-response headers as a substitute.

Historical Asset Review

  • Reviewed: feat/integrated-cpa-runtime local health checks in internal/managedruntime/controller.go, waitComponentRestart/healthyOnce in update.go, and its process restart/backoff mechanics; source authorship was checked.
  • Reusable: owned-process plus local-listener/status conditions and failure/race test scenarios, reimplemented within current Supervisor ownership.
  • Reference only: bounded polling and version/handoff wait scenarios.
  • Rejected: Manager-owned process supervision, automatic restart/backoff, persistent PID authority, provider probes, updater/rollback coupling, and long-lived Management Key delegation.
  • Impact: added exact-child replacement/PID-reuse and late-probe regressions; no historical code was copied and no merge/rebase/cherry-pick was performed.

Risk / Rollback

Risk level: Medium

Concurrency and readiness interpretation are the main risks; real child and journal tests verify replacement fencing and unchanged operation evidence. Roll back by reverting this commit to the prior static Embedded status and Manager validation contract.

Verification

  • Type check
  • Lint
  • Tests
  • Build
  • Manual UI check
  • Docs/link check
  • Not applicable, docs-only

All local checks below passed with Go 1.26.1 on darwin/amd64, using isolated temporary data/cache and fixture loopback listeners. UI/TypeScript checks are not applicable. CI results are tracked by the PR checks.

# apps/runtime-supervisor; GOWORK=off GOFLAGS=-mod=readonly
go test -count=1 ./internal/readiness ./internal/cpaprocess ./internal/protocol ./internal/lifecycle ./cmd/cpamp-runtime-supervisor
go test -count=1 ./...
go test -race -count=1 ./...
go vet ./...
CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -trimpath -o <temp>/supervisor-linux-amd64 ./cmd/cpamp-runtime-supervisor
CGO_ENABLED=0 GOOS=linux GOARCH=arm64 go build -trimpath -o <temp>/supervisor-linux-arm64 ./cmd/cpamp-runtime-supervisor
CGO_ENABLED=0 GOOS=windows GOARCH=amd64 go build -trimpath -o <temp>/supervisor-windows-amd64.exe ./cmd/cpamp-runtime-supervisor

# apps/manager-server; GOWORK=off GOFLAGS=-mod=readonly
go test -count=1 ./internal/model ./internal/service/runtime
go test -count=1 ./...

# repository root; GOWORK=off
go test ./bin/ci/runtime-boundary/main.go ./bin/ci/runtime-boundary/main_test.go
go run ./bin/ci/runtime-boundary/main.go
git diff 80a49128ef64a8d058bc1acd0d2344cfd8fcd7b8 HEAD --check

Local self-review found no blocking defects. Linux amd64/arm64 and Windows amd64 outputs were confirmed as the intended binary formats; these are cross-build checks, not execution on those operating systems.

Screenshots / Recordings

N/A — backend protocol and ADR changes.

Docs

  • README / README_CN updated for user-visible capabilities
  • Matching docs manual and navigation updated
  • Demo fixtures, screenshots, and deep links reviewed
  • Release notes needed
  • Not needed — explanation included below

Docs decision: ADR-0001 is amended for the accepted readiness/version contract. Public manual, demo, and release changes are not needed for this Supervisor-private foundation slice. cpamp-plan remains untouched.

Related

Refs #779 (Runtime 09 baseline), Runtime 10 Observed Readiness + Safe CPA Health Probe, and ADR-0001.

CPA upstream evidence at router-for-me/CLIProxyAPI@7bbfeaf8a7acf2cd5a834dcb0842539fe6aabc2b: safe health routes and Management authentication/IP-ban behavior.

Ready for independent acceptance; no automatic merge or subsequent Runtime task is requested.

Compute Embedded status on demand from the owned CPA child and a bounded
loopback HEAD /healthz probe. Fence results with a process-local child
instance so exit or Restart cannot mark a replacement ready.

Preserve lifecycle, journal and RuntimeGeneration semantics without using
Management credentials. Allow ready observations without a version while
retaining the External adapter's authenticated version requirement.

Verify Supervisor focused/full/race/vet, Manager focused/full, Linux
amd64/arm64 and Windows amd64 builds, and architecture boundaries.
@seakee
seakee merged commit 68ab043 into v2 Sep 15, 2026
11 checks passed
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