Skip to content

test(runtime): shared ContainerRuntime contract suite across both backends (RIG-2493) - #720

Open
rigel-mintaka wants to merge 1 commit into
compass-runner/rig-2493-u4-microvm-lifecyclefrom
compass-runner/rig-2493-u5-contract-suite
Open

test(runtime): shared ContainerRuntime contract suite across both backends (RIG-2493)#720
rigel-mintaka wants to merge 1 commit into
compass-runner/rig-2493-u4-microvm-lifecyclefrom
compass-runner/rig-2493-u5-contract-suite

Conversation

@rigel-mintaka

@rigel-mintaka rigel-mintaka commented Aug 28, 2026

Copy link
Copy Markdown
Contributor

This PR is part of a stack containing 5 PRs:

  1. main
  2. feat(guest_control): grow GuestControl with the V2b exec surface (RIG-2493) #696
  3. feat(guestd): replace the Health-only stub with the V2b in-guest supervisor (RIG-2493) #701
  4. feat(runtime): host GuestExec layer + portable exit-signal error type (RIG-2493) #702
  5. feat(runtime): fill MicroVMRuntime lifecycle methods (RIG-2493) #719
  6. "test(runtime): shared ContainerRuntime contract suite across both backends (RIG-2493)" (this PR)

U5 of the frozen microVM Runner V2b plan (docs/designs/infra/runtime/compass-elastic-session-runtime/microvm-v2b-guest-supervisor-exec.md, §Plan U5) — the V2b acceptance gate. One table-driven contract suite proves MicroVMRuntime and PodmanCLI behave identically through the runtime.ContainerRuntime interface, run against BOTH backends, with the 6 conceded divergences (record 580-593) encoded as a per-backend capability matrix so a divergence that silently widens fails a row.

What

  • contract_suite_test.go (UNTAGGED, package runtime): the shared body. backendCaps capability descriptor (factory + the 6 divergence flags + typed-error closures) and runContractSuite, a thin dispatcher whose rows are one helper each. It references ONLY untagged production symbols plus backendCaps — every KVM/podman-only symbol (microvmtest, podmanUsable, buildImage, *DuplicateNameError) is reached through a caps closure, so the file compiles on every platform. A var _ = runContractSuite roots the graph for the untagged unused lint pass (both real callers are tag-gated).
  • contract_podman_test.go (//go:build podman): TestContractSuite_Podman — skips unless podmanUsable(), builds the agent image once, containers run sleep infinity as uid 1000; all microVM divergence flags OFF so those rows self-skip; the deliberate-kill row asserts the byte-identical *exec.ExitError path (podman byte-path unregressed).
  • contract_microvm_test.go (//go:build microvm && unix): TestContractSuite_MicroVMmicrovmtest.Require(t), sessions with a /workspace share + uid 1000; all 6 divergence flags ON so every divergence row runs and a silent widening fails. Plus TestMicroVMQBudget, informational boot-latency + per-process PSS via t.Logf (record §(g)), NOT a boot gate.
  • Fold, not duplicate: TestMicroVMLifecycleEndToEnd (Create→Start→Exec→Stop→Remove) and TestMicroVMExecStreamingKillSignalsExit (ExecStreaming-kill), previously standalone in microvm_lifecycle_microvm_test.go, are now shared contract rows and were folded out of that file. e2eConfig and TestMicroVMStartFailureLeavesNoState (a microVM-backend-only negative with no podman analog) stay.

Divergence 6 (deliberate-kill error shape)

The shared row body proves the SAME behavioral contract — a deliberate Kill+Wait surfaces as a signalled exit isDeliberateKill accepts — in the two shapes the frozen code matches: microVM's portable *ExitStatusError (Signal != 0) and podman's byte-identical *exec.ExitError (ExitCode() == -1). The error SHAPE is capability-gated (portableKillError) rather than forced to one type, because asserting *ExitStatusError on podman would require a forbidden production change to PodmanCLI and contradict record 590-591. This honors the frozen record.

Verification

gofmt -l clean; go build/go vet clean untagged, under -tags 'microvm unix', and under -tags podman; go test -race -count=1 ./internal/runtime/... ok; golangci-lint run 0 issues (untagged, and under each tag for the touched files). Live: go test -tags podman -run TestContractSuite_Podman PASS (all shared rows; microVM-gated rows correctly absent). Live on KVM: go test -tags 'microvm unix' -run TestContractSuite_MicroVM — all 15 rows PASS (the exec rows exercise the U2 guest PATH resolution and the whole suite the U4 socket-budget fix).

Ledger-impact: none

Spec-impact: none. Refs RIG-2493

Co-authored-by: Matt Wilkinson matt@rigel.build

@linear-code

linear-code Bot commented Aug 28, 2026

Copy link
Copy Markdown

RIG-2493

@github-actions

github-actions Bot commented Aug 28, 2026

Copy link
Copy Markdown

Compass engineering docs preview: https://compass-runner-rig-2493-u5-c.compass-eng-docs.pages.dev

Deployed from compass-runner/rig-2493-u5-contract-suite at e664f7e.

…kends (RIG-2493)

U5 of the frozen microVM Runner V2b plan (`docs/designs/infra/runtime/compass-elastic-session-runtime/microvm-v2b-guest-supervisor-exec.md`, §Plan U5) — the V2b acceptance gate. One table-driven contract suite proves `MicroVMRuntime` and `PodmanCLI` behave identically through the `runtime.ContainerRuntime` interface, run against BOTH backends, with the 6 conceded divergences (record 580-593) encoded as a per-backend capability matrix so a divergence that silently *widens* fails a row.

### What

- **`contract_suite_test.go`** (UNTAGGED, package `runtime`): the shared body. `backendCaps` capability descriptor (factory + the 6 divergence flags + typed-error closures) and `runContractSuite`, a thin dispatcher whose rows are one helper each. It references ONLY untagged production symbols plus `backendCaps` — every KVM/podman-only symbol (`microvmtest`, `podmanUsable`, `buildImage`, `*DuplicateNameError`) is reached through a caps closure, so the file compiles on every platform. A `var _ = runContractSuite` roots the graph for the untagged `unused` lint pass (both real callers are tag-gated).
- **`contract_podman_test.go`** (`//go:build podman`): `TestContractSuite_Podman` — skips unless `podmanUsable()`, builds the agent image once, containers run `sleep infinity` as uid 1000; all microVM divergence flags OFF so those rows self-skip; the deliberate-kill row asserts the byte-identical `*exec.ExitError` path (podman byte-path unregressed).
- **`contract_microvm_test.go`** (`//go:build microvm && unix`): `TestContractSuite_MicroVM` — `microvmtest.Require(t)`, sessions with a `/workspace` share + uid 1000; all 6 divergence flags ON so every divergence row runs and a silent widening fails. Plus `TestMicroVMQBudget`, informational boot-latency + per-process PSS via `t.Logf` (record §(g)), NOT a boot gate.
- **Fold, not duplicate**: `TestMicroVMLifecycleEndToEnd` (Create→Start→Exec→Stop→Remove) and `TestMicroVMExecStreamingKillSignalsExit` (ExecStreaming-kill), previously standalone in `microvm_lifecycle_microvm_test.go`, are now shared contract rows and were folded out of that file. `e2eConfig` and `TestMicroVMStartFailureLeavesNoState` (a microVM-backend-only negative with no podman analog) stay.

### Divergence 6 (deliberate-kill error shape)

The shared row body proves the SAME behavioral contract — a deliberate Kill+Wait surfaces as a signalled exit `isDeliberateKill` accepts — in the two shapes the frozen code matches: microVM's portable `*ExitStatusError` (`Signal != 0`) and podman's byte-identical `*exec.ExitError` (`ExitCode() == -1`). The error SHAPE is capability-gated (`portableKillError`) rather than forced to one type, because asserting `*ExitStatusError` on podman would require a forbidden production change to `PodmanCLI` and contradict record 590-591. This honors the frozen record.

### Verification

`gofmt -l` clean; `go build`/`go vet` clean untagged, under `-tags 'microvm unix'`, and under `-tags podman`; `go test -race -count=1 ./internal/runtime/...` ok; `golangci-lint run` 0 issues (untagged, and under each tag for the touched files). Live: `go test -tags podman -run TestContractSuite_Podman` PASS (all shared rows; microVM-gated rows correctly absent). Live on KVM: `go test -tags 'microvm unix' -run TestContractSuite_MicroVM` — all 15 rows PASS (the exec rows exercise the U2 guest PATH resolution and the whole suite the U4 socket-budget fix).

Ledger-impact: none

Spec-impact: none. Refs RIG-2493

Co-authored-by: Matt Wilkinson <matt@rigel.build>
@rigel-mintaka
rigel-mintaka force-pushed the compass-runner/rig-2493-u4-microvm-lifecycle branch from b95a89a to 9f07047 Compare August 28, 2026 11:11
@rigel-mintaka
rigel-mintaka force-pushed the compass-runner/rig-2493-u5-contract-suite branch from 79f96e3 to e664f7e Compare August 28, 2026 11:12
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