Skip to content

feat(api): reserve HealthStatus enum + health field on ContainerSnapshot#1504

Draft
chrisgeo wants to merge 1 commit intoapple:mainfrom
full-chaos:feat/chaos-1319-health-status
Draft

feat(api): reserve HealthStatus enum + health field on ContainerSnapshot#1504
chrisgeo wants to merge 1 commit intoapple:mainfrom
full-chaos:feat/chaos-1319-health-status

Conversation

@chrisgeo
Copy link
Copy Markdown

@chrisgeo chrisgeo commented May 2, 2026

Companion issue: #1502

Type of Change

  • Bug fix
  • New feature
  • Breaking change
  • Documentation update

Motivation and Context

External orchestrators that drive the API server (the canonical use case is a Compose-spec orchestrator implementing depends_on: condition: service_healthy) need to know whether a container is up AND healthy, not just up. Today ContainerSnapshot exposes .running for any started container, so consumers fall back to treating liveness as health. Real workloads (databases that take seconds to accept connections, queue brokers warming up in-memory state) hit this regularly.

This PR is data-shape only: it adds a public HealthStatus enum + an optional health: HealthStatus? field on ContainerSnapshot. It does NOT wire a healthcheck observer into the daemon — at runtime the field is always nil. The on-the-wire behavior is unchanged modulo one new Codable key on ContainerSnapshot.

See #1502 for the full motivation, the rationale for shipping a nil-only field today, and the open design questions for the eventual observer.

What this PR changes

  • Sources/ContainerResource/Container/HealthStatus.swift (new): the enum, with each case documented.
  • Sources/ContainerResource/Container/ContainerSnapshot.swift: new optional health: HealthStatus? field with a defaulted init parameter; existing construction sites compile unchanged. Inline doc on the field flags the "always nil today" caveat so consumers know not to wait on it.

Total: 2 files (1 new + 1 modified), +45/-1.

Why ship a nil-only field?

A container-level healthcheck observer is a non-trivial design discussion (where the healthcheck spec lives, who runs probes, sandbox-boundary semantics, cadence/retry/start_period, etc.). We would rather have that discussion separately, against a concrete companion design issue, than couple it to the SDK shape PR.

Reserving the SDK shape now lets downstream tools start coding against the field with the documented nil-today guarantee inline; flipping the implementation on later does not require another SDK-shape PR.

If maintainers prefer the opposite path (block the SDK shape until an observer design lands), we will park this PR and open a design discussion issue instead — please indicate preference on #1502.

Wire compatibility

ContainerSnapshot is marshaled as Codable JSON over XPC. Adding an optional field is forward-compatible:

  • Older clients against a newer server: ignore the new key.
  • Newer clients against an older server: decode health as nil.

Testing

  • Tested locally (full swift build clean on macOS 26 / Apple silicon, including downstream targets that consume ContainerSnapshot).
  • Added/updated tests — no behavior to exercise (field is always nil); a Codable round-trip test for the new optional key is straightforward to add if maintainers want it.
  • Added/updated docs — public API doc comments added on the new enum and the new field, including the "always nil today" caveat; no separate docs page to update.

Status

Draft. Filed alongside #1502. Marked draft until a maintainer signals direction on the SDK-shape-vs-defer-until-observer decision.

Adds a new public enum HealthStatus { none, starting, healthy, unhealthy }
and a new optional 'health: HealthStatus?' field on ContainerSnapshot,
defaulting to nil at all construction sites.

Motivation
----------

External orchestrators that drive the API server (the canonical use
case is the compose-spec depends_on: condition: service_healthy gate)
need to know whether a container is up AND healthy, not just up. Today
ContainerSnapshot exposes .running for any started container, so
consumers have to fall back to .running and treat liveness == health.
Real workloads (databases that take seconds to accept connections,
queue brokers that warm up an in-memory state) hit this regularly and
end up either waiting too long or proceeding too early.

Scope of this PR (deliberately minimal)
---------------------------------------

This PR is data-shape only. It adds the enum and the field to the SDK.
It does NOT wire a healthcheck observer into the daemon: at runtime
the field is always nil, so the on-the-wire behavior is unchanged
modulo one new Codable key on ContainerSnapshot.

Why ship a nil-only field?
~~~~~~~~~~~~~~~~~~~~~~~~~~

A container-level healthcheck observer is a non-trivial design
discussion (where does the spec live? does the API server exec into
the container, or does the runtime drive it? does it leak into the
sandbox boundary?) and we'd rather have that discussion separately,
referencing a concrete companion issue. Reserving the SDK shape now
lets downstream tools start coding against the field with the
'always nil today' guarantee documented inline; flipping the
implementation on later does not require another SDK-shape PR.

Wire compatibility
------------------

ContainerSnapshot is marshaled as Codable JSON over XPC. Adding an
optional field is forward-compatible:

  - Older clients reading from a newer server: ignore the new key.
  - Newer clients reading from an older server: decode health as nil.

Files
-----

- Sources/ContainerResource/Container/HealthStatus.swift (new):
  the enum, with cases documented and a note on the daemon-side
  observer caveat.
- Sources/ContainerResource/Container/ContainerSnapshot.swift:
  new optional field + init parameter (default nil).

Companion issue
---------------

Filed at apple/container with the design proposal for the eventual
healthcheck observer; this PR is deliberately the smaller surface so
the data shape can land independently of that discussion.
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.

2 participants