Files
bot-bottle/docs/prds/prd-new-ci-artifact-coverage.md
T
didericis-claude 5940b75bb7
test / integration-docker (pull_request) Successful in 12s
tracker-policy-pr / check-pr (pull_request) Successful in 8s
test / unit (pull_request) Successful in 33s
test / integration-firecracker (pull_request) Successful in 3m13s
test / coverage (pull_request) Failing after 1m45s
test / publish-infra (pull_request) Has been skipped
ci: artifact-based coverage and local Firecracker candidate flow
Each test job now runs once under coverage and uploads a small .coverage.*
artifact. The coverage job combines them on ubuntu-latest — no test reruns,
no KVM dependency. The infra candidate is built directly on the KVM runner,
eliminating the build-infra job and the ~70 s upload + ~83 s combined
download. For PRs, no rootfs artifact is transferred at all. Main-branch
pushes upload the tested rootfs and matching dropbear so publish-infra
publishes the byte-identical artifact. relative_files = True in .coveragerc
lets coverage files from different runners combine without path remapping.

Closes #446
2026-07-21 04:04:30 +00:00

4.2 KiB

PRD prd-new: CI artifact-based coverage and local Firecracker candidate flow

  • Status: Active
  • Author: Claude
  • Created: 2026-07-21
  • Issue: #446

Summary

Restructure the CI test pipeline to run each test suite exactly once, upload small .coverage.* artifacts, and combine them in a lightweight aggregation job. Move the infra build onto the KVM runner so the ~194 MB rootfs never crosses the network for PRs. On main-branch pushes, publish the byte-identical rootfs that was tested.

Motivation

The prior pipeline had two redundant costs:

  1. Duplicate artifact transfers. build-infra (ubuntu-latest) built and uploaded the ~194 MB rootfs; integration-firecracker downloaded it; the coverage job downloaded it a second time. Combined download overhead: ~83 seconds per run, plus the ~70-second upload.

  2. Duplicate test execution. integration-firecracker ran the Firecracker integration suite; coverage ran the entire unit + integration suite again on the same KVM runner to collect coverage data. Every line of Firecracker code was tested twice per CI run.

Goals

  • Each test suite (unit, integration-docker, integration-firecracker) executes exactly once per workflow run.
  • PRs incur no large artifact transfers — the rootfs stays on the KVM runner.
  • Main-branch pushes publish a byte-for-byte identical rootfs to the one that passed the integration tests.
  • Concurrent workflow runs cannot cross-publish candidates (naturally enforced by Gitea Actions' per-run artifact scoping).
  • Failed or cancelled runs block publication (enforced by the needs: chain on publish-infra).

Non-goals

  • Changing test semantics or the coverage policy (ADR 0004).
  • Removing the KVM runner guard on integration-firecracker and coverage.
  • Changing how publish_infra.py builds or uploads the rootfs.

Design

Job graph

unit ──────────────────────────────────┐
integration-docker ────────────────────┤──► coverage ──► publish-infra (main only)
integration-firecracker (KVM) ─────────┘

unit

Unchanged except: coverage run writes --data-file=.coverage.unit; the file is uploaded as the coverage-unit artifact.

integration-docker

Adds a coverage install step. coverage run writes --data-file=.coverage.docker; the file is uploaded as coverage-docker.

integration-firecracker (KVM runner)

Replaces the old stage-firecracker-inputsbuild-infra → download chain:

  1. Builds the infra candidate locally with BOT_BOTTLE_FC_DROPBEAR=/var/cache/bot-bottle-fc/dropbear.
  2. Boots the candidate and runs integration tests with coverage, writing .coverage.firecracker.
  3. Uploads the small coverage-firecracker artifact unconditionally.
  4. On main-branch pushes only, uploads the rootfs as infra-candidate and the dropbear as firecracker-inputs so publish-infra can verify and publish the byte-identical artifact.

coverage

Moves from a KVM runner to ubuntu-latest. No tests are re-executed:

  1. Downloads coverage-unit, coverage-docker, and coverage-firecracker.
  2. Runs scripts/coverage.sh aggregate critical, which calls coverage combine then coverage report.
  3. Runs the diff-coverage gate (scripts/diff_coverage.py).

Coverage files use relative_files = True (.coveragerc) so they combine cleanly across runners with different absolute workspace paths.

publish-infra

Depends on all four predecessor jobs (unchanged gate). Downloads infra-candidate and firecracker-inputs that were uploaded by integration-firecracker on main — the same byte sequence that passed the integration tests.

Eliminated jobs

  • stage-firecracker-inputs: existed only to copy the dropbear to ubuntu-latest for build-infra. No longer needed.
  • build-infra: the infra candidate is now built on the KVM runner in integration-firecracker.

Script changes

scripts/coverage.sh gains an aggregate mode (coverage.sh aggregate [critical]) that combines pre-existing .coverage.* files instead of re-running tests. The existing run mode (coverage.sh [critical]) is preserved for local dev.