The self-hosted runner's Nix python env has no `pip` module, so
`python3 -m pip install -r requirements-dev.txt` failed with "No module
named pip" in both firecracker jobs. Neither job needs that install:
- integration-firecracker runs the stdlib `unittest` suite (no deps);
- coverage needs only `coverage`, which the runner's Nix python env
already ships (7.12.0) — verified `coverage run`/`coverage json` work.
pylint/pyright are lint.yml's concern, not test.yml's. The ubuntu-latest
`unit` job keeps its `--break-system-packages` install unchanged.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S1qRZTJC6qgBsUSjNrBdkX
Add separate `integration-docker` and `integration-firecracker` jobs,
each with an explicit BOT_BOTTLE_BACKEND env var, so the backend used
is visible in CI output and skipped backends surface as a distinct job
rather than silent unittest.skip lines.
- integration-docker: ubuntu-latest, BOT_BOTTLE_BACKEND=docker
- integration-firecracker: [self-hosted, kvm], BOT_BOTTLE_BACKEND=firecracker,
same-repo PRs + push + workflow_dispatch only (untrusted fork PRs do
not execute on the privileged KVM runner)
- coverage: same same-repo restriction; refs #414 for the planned
follow-up that moves coverage to ubuntu-latest via artifact combination
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
- Finding 1: set BOT_BOTTLE_BACKEND=firecracker on the coverage step so
the integration suite actually exercises the Firecracker orchestration
paths rather than defaulting to Docker
- Finding 2: restrict the coverage job to push+workflow_dispatch only;
PR-controlled code no longer executes on the privileged KVM runner
automatically — maintainers trigger workflow_dispatch for trusted PRs
- Finding 3: expand path filters to include workflow files, scripts, and
README so changes to CI configuration trigger the workflow itself
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
The self-hosted KVM runner is a persistent machine, so
--break-system-packages is inappropriate. Use --user instead so
coverage (and pyright/pylint for future jobs) land in ~/.local
and survive between runs without touching the system Python.
Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
Re-land the coverage gate deferred from #343. The Firecracker VM/SSH
orchestration (~230 lines) is only exercised by the integration suite,
which needs /dev/kvm + the provisioned TAP/nft pool — a container runner
skips it and those lines read uncovered, so the 90% diff gate can't pass
on ubuntu-latest. Move the `coverage` job to a self-hosted `kvm` runner
with a firecracker-readiness preflight (binary + /dev/kvm + `backend
status`) so the integration test actually runs. Unit/lint stay on
ubuntu-latest. README documents the runner prerequisites.
Depends on a registered self-hosted runner labelled `kvm`; until one is
provisioned this gate will not run. See PRD 0069 / #348.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
The old act_runner engine (v0.2.13 on the delphi-ci runner) mishandles
actions/setup-python's PATH injection: pip installs coverage into the
toolcache interpreter while `python3` in later steps resolves back to the
image's system Python, so unit/coverage jobs failed with "No module named
coverage". Newer runners (TrueNAS's v0.6.1) don't, which is why it only
broke on delphi.
The runner-images/act container already ships Python 3.12, and the job
container is ephemeral, so drop setup-python entirely and install straight
into the system Python with --break-system-packages. Every step now uses
one interpreter consistently, on any runner version. Also removes the
redundant setup-python step from the integration job (stdlib-only).
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UoEZHDjv84ChoZbozQERhJ
Split the self-hosted KVM runner setup out of this branch so main's CI
doesn't require a runner that isn't registered yet. The `coverage` job
runs on ubuntu-latest again and reports combined coverage informationally
— the hard diff-coverage gate (which needs the firecracker integration
suite, hence /dev/kvm + the provisioned pool) is deferred and moves to a
separate PR. The unit-coverage tests added alongside the gate stay.
See PRD 0069 / #348; the runner + gate re-land on the ci-runner branch.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
The Firecracker VM/SSH orchestration (launch/boot/SSH/isolation-probe,
~230 lines) is covered by the integration suite, which needs /dev/kvm +
the provisioned pool — a container runner skips it, so those lines read
uncovered and the 90% diff gate can't pass there (it's been red since the
backend landed). Move the `coverage` job to a self-hosted `kvm` runner
with a firecracker-readiness preflight (binary + /dev/kvm + `backend
status`), so the integration test actually runs and the orchestration is
covered. Unit/lint stay on ubuntu-latest. README documents the runner
prerequisites.
Also close the last unit-coverable gaps (bottle exec/close, bottle_plan
properties, docker status/setup branches, netpool span/conflict) so the
gate clears 90% (90.3%) with the firecracker integration test running.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WBMWTEtQdJ4W5UrWuLHCck
Adopt ADR 0004: stop chasing a single global coverage number and
measure what matters instead.
- Omit the genuinely-interactive `cli/init.py` shell (read_tty_line
prompt loops) alongside the existing `cli/tui.py`, with a rationale
comment in .coveragerc. Subprocess/backend orchestration is NOT
omitted — it stays visible and is scored via the integration suite.
- scripts/coverage.sh runs unit + integration under one coverage
measurement (the policy's yardstick) and can report the critical
security/logic core held to the >=90% target.
- scripts/diff_coverage.py is a stdlib-only gate (no diff-cover dep):
new/changed executable lines must be >=90% covered. This is the
enforced regression guard; the global number is informational.
- CI gains a `coverage` job: combined report + the diff-coverage gate.
- Unit-test `cli/__init__.py` dispatch/exit-code mapping (it's logic,
not I/O, so it earns tests rather than an omit).
Combined unit+integration coverage now reports 83% global / 87% across
the critical modules; per-module ratcheting toward 90% is the ongoing
work this policy frames.
Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01NkwFXLFff9PYPy4wgVBJp9
Replace the hand-maintained INTEGRATION_NAMES classifier (and the
bespoke run_tests.py around it) with a directory-driven split:
tests/unit/ unit tests, always run
tests/integration/ Docker-dependent, skip cleanly without Docker
tests/canaries/ upstream-regression checks, opt-in via
CLAUDE_BOTTLE_RUN_CANARIES=1
The pinned-pipelock-image check moves to the canary suite — it tests
upstream packaging, not our code, so it shouldn't gate every dev push.
A scheduled canaries.yml workflow runs it weekly.
The manifest-runtime tests collapse the four assertRaises cases for
distinct 'runtime' values into one subTest loop and drop the
error-message-wording assertions; the contract is "any value is
rejected", not "the error literally contains 'auto-detect'".
Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>