Add integration tests based on testcontainers

This commit is contained in:
2026-10-25 19:47:25 +02:00
parent 2ccd67d64c
commit f4a6540df9
24 changed files with 8693 additions and 68 deletions
+368 -7
View File
@@ -94,13 +94,13 @@ remaining limitations, next concrete action.>
<Coordinator acceptance decision and timestamp, or exact blocker.>
```
Use ISO 8601 timestamps with offsets for progress. Keep evidence concise and reproducible, with redacted excerpts or artifact paths rather than large logs. Distinguish planned commands from executed commands and unavailable checks from passing checks. Keep the plan self-contained by summarizing discoveries that affect the next task, not just linking to an agent conversation.
Use ISO 8601 timestamps with offsets for progress. Keep evidence concise and reproducible, following [execution records](docs/EXECUTION_RECORDS.md); diagnostic attempts belong outside this plan. Use redacted excerpts or artifact paths rather than large logs. Distinguish planned commands from executed commands and unavailable checks from passing checks. Keep the plan self-contained by summarizing discoveries that affect the next task, not just linking to an agent conversation.
## Validation gates
For Rust milestones, use the relevant focused tests and the formatting, Clippy, default tests, and release-build commands in AGENTS.md. Add exact commands when targets exist; do not invent passing outputs. Unit and fixture checks run without root. Use standard-library test adapters rather than an additional mock framework.
Container suites are opt-in and use pinned/cached Alpine images, fresh server/client containers, synthetic keys, isolated dual-stack networking, and scoped `NET_ADMIN`. The real runtime adapter executes inside the server container. Missing Docker/kernel/IPv6 prerequisites are incomplete validation, not success. Alpine fixture application is not a production systemd test.
Container suites are opt-in and use pinned/cached Alpine images, fresh server/client containers, synthetic keys, isolated networking, and scoped `NET_ADMIN`. The real runtime adapter executes inside the server container. Missing prerequisites required by a suite are incomplete validation, not success. Milestone 8 validates IPv4 networking only by user direction; IPv6 capability and traffic remain unverified while the production dual-stack requirement remains in force. Alpine fixture application is not a production systemd test.
Actual systemd/D-Bus acceptance runs in a disposable VM. Never use production to fill gaps in test coverage. Final release acceptance requires relevant unit, Alpine WireGuard, and VM systemd results reported separately. Document blockers without silently weakening the gate.
@@ -117,7 +117,7 @@ Each row is a delivery milestone, not a completion claim. Expand its record befo
| 5 | Runtime observation and systemd restart adapter | 4 | accepted |
| 6 | Client configuration delivery and reissue lifecycle | 5 | accepted |
| 7 | Complete terminal management workflow | 6 | accepted |
| 8 | Alpine Testcontainers integration | 7; suitable disposable Docker environment | pending |
| 8 | Alpine Testcontainers integration | 7; suitable disposable Docker environment | accepted |
| 9 | Real systemd acceptance and release readiness | 8; disposable systemd VM | pending |
### Milestone 1 — Rust foundation, settings, and domain states
@@ -2623,9 +2623,361 @@ all planned coordinator gates. M8/M9 retain real WireGuard/systemd release gates
### Milestone 8 — Alpine Testcontainers integration
Implement opt-in Rust container tests and a minimal pinned Alpine image with `wireguard-tools` and `iproute2`. Cache images, use fresh isolated resources, build an Alpine-compatible test executable (prefer musl), and exercise the real runtime adapter within the server container. Avoid new helpers outside approved dependencies without agreement.
Status: accepted (2026-10-25T20:43:00+03:00; M8.1–M8.7 complete, independent
review closed, scoped IPv4 Alpine gates passed).
Prerequisites: milestones 1–7 accepted; pinned Rust toolchain; an explicitly
selected disposable Docker-compatible Linux environment with kernel WireGuard,
scoped NET_ADMIN and IPv4 support; an Alpine-compatible test executable.
Missing prerequisites are blockers/incomplete evidence, never passing tests.
Acceptance: prerequisite checks and cleanup work; real dual-stack tunnels produce handshake/counter changes; creation/Commit leaves runtime unchanged until explicit test-only application; deletion retains access/address reservation until application; restored saved state does not implicitly apply. No host network, host D-Bus, production mounts, privileged default, or automatic module loading. Systemd coverage is explicitly excluded.
#### Outcome and scope
Add an opt-in Rust Testcontainers suite using small Alpine server/client images.
Exercise the actual application, encrypted vault/persistence/delivery APIs, and
M5's real rustix Generic Netlink reader inside the server container against real
kernel WireGuard interfaces. Establish real tunnels, generate IPv4 traffic,
and prove that saved changes remain separate from runtime application.
The user explicitly excluded IPv6 capability and traffic tests on 2026-10-22.
Production dual-stack behavior in SPEC.md is unchanged; real IPv6 behavior is
therefore unverified by this milestone.
Read SPEC.md Operations and states, Connection delivery, Backups and restoration,
Client screen and statistics, and acceptance criteria; ADRs 0001–0003; TESTING.md,
ENVIRONMENT.md, RUNTIME.md, DELIVERY.md, VAULT_FORMAT.md, PERSISTENCE.md,
BACKUP_FORMAT.md, DEPENDENCIES.md and current application/worker code.
This is real-WireGuard integration, not real-systemd acceptance. Alpine fixtures
have no systemd; a test-only apply operation may configure the interface explicitly.
Actual zbus service job behavior, production restart and VM acceptance remain M9.
M7's synthetic TUI/PTY evidence remains separate; M8 does not require live terminal
screens, phone QR decoding, public Internet access, external DNS, production
configuration, new product commands, or a production apply mode.
#### Existing code and integration constraints
- M1–M7 are accepted. The normal executable now starts the management TUI; do not
invoke it with host/production settings as a shortcut for a test driver.
- Application has open-from-store, setup/import, creation, Commit, delivery/reissue,
deletion, backup/list/restore and confirmed restart flows. Use these real APIs,
not a parallel shell-only client manager that would leave application behavior
untested. Public-key identity and source preservation remain authoritative.
- Real observation is `runtime::transport::observe_interface`, currently crate-
private, with the validated decoder and bounded rustix transport. Do not use
only `wg show` output or a fake observation as evidence that this reader works.
- `Application::restart` always invokes the actual zbus path, while injected
restart functions are private. Plan an in-crate test seam so a test-only fixture
service operation can apply peers and the real observation/persistence path can
verify them. Preserve backup ordering and per-attempt authority from M5.
- Prefer cfg(test) in-container test scenarios/driver code, compiled for musl and
selected explicitly, to reach private adapters without exposing an unauthenticated
public apply API. If another arrangement is needed, document its compile-time
isolation before implementation. No environment variable may switch production
restart to a fake service or bypass password/backup/root checks.
- The TUI ApplicationWorker already has synthetic workflow tests. An optional
in-container worker scenario may test its real statistics reader, but network
acceptance must not depend on automating every terminal key sequence again.
- No Testcontainers crate, container image recipe or integration driver is present.
Add approved `testcontainers` as a dev-dependency with compatible pinned version
and minimal features. Prefer its supported synchronous facade if it avoids a
direct executor dependency. Other direct helpers, Docker client crates or
Tokio still require prior agreement; transitive dependencies are not new direct
approvals. Use Cargo.lock and inspect official APIs for the selected version.
- Native tools use system Rust 1.97.1; musl readiness was previously unverified.
Recheck rather than assuming rust-toolchain.toml installed targets or a linker.
#### Context and implementation steps
Use GPT-6-Sol/medium coordination and GPT-6-Luna/high implementation, fresh review
and repairs. Execute bounded tasks sequentially with one writer and explicit file
ownership. The coordinator alone updates milestone acceptance and evidence.
| Task | Owner | Files and deliverable |
| --- | --- | --- |
| M8.1 | implementer | `docs/TESTING.md`/ENVIRONMENT additions and test support: exact opt-in architecture, prerequisites, image/build and driver contracts |
| M8.2 | implementer | `tests/containers/alpine/Dockerfile`, fixture-only setup scripts, musl build recipe: small reproducible cached image and executable compatibility checks |
| M8.3 | implementer | `tests/testcontainers_wireguard.rs`, `tests/support/` modules and Cargo files: host orchestration, private network/container ownership, prerequisite checks, bounded execution and cleanup |
| M8.4 | implementer | in-crate cfg(test) scenarios/private adapters, test driver coordination: real netlink reads, fixture apply and application state verification inside server |
| M8.5 | implementer | container scenarios and TESTING/README documentation: required lifecycle, restore, delivery/import/failure assertions and executed evidence |
| M8.6 | reviewer, repairer | Independent isolation, real-adapter coverage and negative-test review; bounded fixes and real-suite re-review |
| M8.7 | coordinator | Default and explicit container gates, redacted results, prerequisites/limitations, acceptance and M9 handoff |
M8.1 defines two execution locations: host harness uses Testcontainers to manage
containers; server-side code uses the real application and kernel reader in that
container's namespaces. Document how driver sessions persist across staged create,
Commit, first delivery and apply phases, and how the client is configured from the
actual delivered file. Do not lose the creation session between Commit and first
issue or reconstruct password-free authority on reopen.
Choose a named ignored integration target/scenarios or another explicit opt-in
mechanism that default cargo test never starts. The host suite must also opt into
all in-container scenario processes. Ensure --ignored does not select unrelated
privileged tests. Fail an explicitly requested suite nonzero on missing required
prerequisites; never return early with a successful test result labelled skipped.
M8.2 pins an Alpine base release/digest and records the resolved image identity
and package versions. Install only wireguard-tools, iproute2 and minimal fixture
requirements. Use BusyBox capabilities when sufficient; justify any extra package
in the test recipe. Runtime images contain no Cargo registry/compiler or systemd.
Build/cache image layers and compatible driver artifacts before repeated tests;
use fresh mutable containers/volumes for each scenario, not reusable dirty servers.
Keep build context small with explicit copies/ignore rules, no .git, host Backup,
credentials or private configuration. Never bake generated keys into image layers.
Build server/client driver code for x86_64-unknown-linux-musl or in a pinned
Alpine builder matching the toolchain. Inspect dynamic requirements and actually
execute a harmless readiness command inside Alpine. Do not rely on a glibc host
binary, target name or successful host compilation as runtime compatibility proof.
Only build required test targets; avoid pulling compiler/debug layers into the
small runtime fixture. No global package installation or toolchain update is
implicitly authorized by a missing cross-build prerequisite.
M8.3 checks Docker API access and explicit disposable-environment selection.
Do not use a configured remote daemon without establishing that it is the selected
non-production test environment. Probe WireGuard interface creation inside
a disposable probe container, never by creating host interfaces or loading modules.
Container tools alone do not supply the host kernel module. Refuse to repair
prerequisites via SYS_MODULE, privileged mode or host sysctl changes automatically.
Create a dedicated uniquely named IPv4 network with a test subnet that does
not overlap the tunnel ranges. Run server/client as container root with scoped
NET_ADMIN in their own network/PID namespaces; no host networking, namespace joins
to host, host D-Bus, host /etc/wireguard, Docker socket mount, or production binds.
No host UDP publication is needed for container-to-container endpoints. Use
container-internal reachable endpoint/port values, not localhost mapped ports.
All settings/server/vault/output paths are private fixture directories created
with the application's required permissions.
Use Testcontainers copy/exec APIs or an explicitly safe bounded transport for
fixture data. Secret-bearing configs/passwords never enter command arguments,
image metadata, public environment variables or raw stdout/stderr logs. Any
necessary copied plaintext exists only in private temporary/container storage.
Generate keys per scenario. Reports carry public keys, phase/status and redacted
categories, not full configurations, QR matrices or `wg show all dump` output.
Bound image/start/driver/readiness/traffic/teardown operations. Poll explicit
conditions with monotonic deadlines, not fixed sleeps. Use unique ownership and
RAII/explicit cleanup for containers, networks and volumes on failures as well as
success; cleanup must not mask the original failure or delete unrelated resources.
Do not default to retain-failed-containers with secrets. Report leftover owned
resources if daemon failure prevents cleanup.
M8.4 keeps fixture `ip`/`wg` calls solely in setup or test-only apply code. It may
use wg-quick strip or equivalent explicit fixture setup, but must not execute
arbitrary imported hooks. Handle optional DNS/MTU fields in delivered configs via
a documented fixture setup, without mutating the delivered payload just to pass.
Avoid an unneeded resolvconf dependency by configuring networking explicitly when
appropriate. No subprocess apply path is added to production.
Configure endpoint reachability outside the tunnel before full-tunnel policy so
an encrypted UDP endpoint is not recursively routed into its own VPN. Set full
IPv4 routes within the client's isolated namespace only. Exercise traffic
to server tunnel addresses; an external Internet gateway/DNS is not required and
successful tunnel reachability does not claim public Internet egress works.
Ensure test success cannot come from the Docker underlay: target tunnel-only
addresses, use an explicit interface/route assertion, and verify public counters
through the actual rustix reader. Test-only service apply reports must be labelled
fixture results, never actual systemd job evidence.
If fixture restart orchestration verifies and releases application reservations,
use the existing private adapter seam with a real backup, fixture apply, actual
netlink read/comparison and journaled verification. Do not simply call
mark_applied or clear reservations to satisfy assertions. A direct fixture apply
outside that orchestration proves only kernel state change; it cannot by itself
prove the production successful-restart state transition.
M8.5 implements these required scenarios, splitting tests where isolation helps:
1. **Baseline and reader interoperability.** Bring up a server plus an existing
client with synthetic configuration, establish real IPv4 tunnel traffic,
and observe the configured interface's complete peer set, routes, nonzero fresh
handshake and increasing counters through the actual Rust reader. Capture
before/after counters, not exact packet-byte totals. Empty/not-yet-handshaken
observations must not be misreported as a reader failure or online heuristic.
2. **Create, Commit, deliver, then apply.** Use Application to prepare a new client,
Commit and save the actual first-delivery file (QR optional). Assert saved
server/vault change, mandatory pre-Commit archive, unchanged runtime peers and
explicit activation warning. Configure a fresh client from delivered bytes;
prove it cannot reach tunnel-only targets before apply. Apply through the
test-only boundary, then require real handshake and IPv4 traffic.
3. **Saved deletion versus live access.** Delete and Commit an active client;
assert current vault record removal, existing backup retention, and unchanged
runtime peer/access before apply. Reopen saved state to verify the IP remains
reserved. After fixture restart plus real verification/persistence, assert
peer removal, failed fresh tunnel traffic and address release. Use a finite
negative-check window and establish a working baseline so unrelated setup
failure cannot masquerade as successful revocation.
4. **Backup/restore separation.** Save a known client set, mutate/Commit, preview
and restore the archive without providing a vault password. Assert restored
settings/server/vault/state and precautionary backup, plus unchanged kernel
state until explicit fixture apply. Then verify the restored peer set and
renewed tunnel traffic. Restoration alone never releases pending addresses.
5. **Import, reissue and file safety.** Import a valid generated existing client
plus invalid/conflicting candidates from private source files. Assert original
bytes unchanged, anomalies visible and conflicts blocked. Reopen and reissue
a matched client through password-authorized output; verify wrong password
fails and a valid delivered file can configure a real tunnel. Preserve first-
issue/password boundaries and do not add plaintext source fallback.
6. **Failure prevents application.** Produce a deterministic backup failure in the
container (not chmod-only when root can bypass it) and assert Commit/fixture
restart does not install/apply prepared changes or release addresses. Cover
default reader deadline/missing-interface behavior inside its namespace without
changing host state. Keep already exhaustive malformed/archive/failure-unit
cases in ordinary tests rather than duplicating them in every costly container.
For all negative reachability assertions, stop/restart client traffic as needed
and use new requests; stale timestamps/counters or buffered packets cannot prove
continued/revoked access. Use a fixture endpoint/traffic generator with its own
bounded error classification. Record that IPv6 traffic is intentionally untested.
#### Acceptance and validation
- [x] M8-A1: documented opt-in commands run an actual Testcontainers suite with
pinned/cached Alpine runtime images and compatible in-container driver; default
tests/help/version do not start Docker, require root or query host interfaces.
- [x] M8-A2: explicit prerequisite checks reject unavailable daemon/kernel/capability
or incompatible artifacts as incomplete/failure; no production or automatic
privileged/module/sysctl workaround is used.
- [x] M8-A3: isolated real peers establish IPv4 traffic, and the actual
rustix reader reports complete public peer/routes, fresh handshake and traffic
increments without relying solely on wg text or fake decoded observations.
- [x] M8-A4: new client creation/Commit/file delivery use real Application and vault
APIs; the delivered configuration works only after explicit fixture application,
and Commit itself never changes runtime state.
- [x] M8-A5: saved deletion removes only current secret, keeps archives and retains
reservation/live peer until explicit apply; actual observation and journaled
verification precede reservation release. Fresh traffic is revoked afterward.
- [x] M8-A6: password-free restore replaces the saved set but does not apply it;
fixture apply restores real tunnel behavior. Source files remain unchanged and
imported conflict/wrong-password outputs remain blocked.
- [x] M8-A7: deterministic mandatory-backup failure prevents protected write/apply;
negative traffic and missing-interface results cannot pass due to unrelated
broken setup or a silent empty/partial observation.
- [x] M8-A8: fixture adapter cannot be selected in production or bypass root/password/
backup controls; no public alternate restart API or runtime environment bypass
is introduced. Alpine results explicitly exclude actual zbus/systemd coverage.
- [x] M8-A9: secret-free diagnostics, private copied files, limits/deadlines and
cleanup work on both success and failure; only test-owned resources are removed.
- [x] M8-A10: integrated default and real-container gates pass; a clean repeated run
reuses image layers while starting fresh mutable scenarios and produces the same
behavioral outcomes. Independent review closes actionable isolation/coverage issues.
Executed integrated host and selected-daemon commands, from repository root;
the exact Docker environment, image/driver commands, counts and durations are
in [the M8.7 evidence](docs/evidence/milestone-8.md):
```sh
cargo fmt --all -- --check
cargo clippy --locked --all-targets -- -D warnings
cargo test --locked
cargo build --locked --release
cargo test --locked --test testcontainers_wireguard -- --ignored --test-threads=1
./target/release/wireguard-admin --help
./target/release/wireguard-admin --version
git diff --check
```
The exact pinned image-build and in-container driver-build commands,
prerequisites, and output artifact path are in [TESTING.md](docs/TESTING.md).
Both were executed; the test-only musl driver compiled and ran in Alpine.
Build setup and cached dependency access are distinct from the locked host
test execution. No unapproved direct dependency was added.
Image identities, kernel/runtime/toolchain versions, target architecture,
scenario names, outcomes, durations and counts are recorded in the evidence
report. All required scenarios executed and passed; the ignored-test output
explicitly reports 1/1 passing rather than treating a skipped test as success.
Real systemd validation remains M9.
#### Progress
Current snapshot: 2026-10-25. This replaces the chronological execution notes;
[the historical record](docs/history/milestone-8-before-workflow-cleanup.md)
preserves previous attempts and their reported evidence.
| Task | State | Result / next action |
| ------------------- | ----------- | --------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------- |
| M8.1 | complete | Reviewed host/container contract. |
| M8.2 | complete | Pinned Alpine recipe, package closure and readiness verified. |
| M8.3 | complete | Reviewed IPv4 prerequisite probe passed on the selected daemon. |
| M8.4 | complete | Reviewed in-container Application driver and real Rustix smoke passed. |
| M8.5 traffic | complete | Copied first-delivery file drives real IPv4 traffic; exact IPv4 routes, handshakes and counter deltas verified; independent findings closed. |
| M8.5 deletion | complete | Saved deletion preserves access until explicit fixture Apply; subsequent deleted-client traffic fails and retained-client traffic succeeds; independent finding closed. |
| M8.5 restore | complete | Two password-free restores, unchanged runtime before Apply, renewed IPv4 traffic and pending-address release after verified Apply passed on the selected daemon. Independent code review found no implementation defect; the plan-status finding closed on re-review. |
| M8.5 import/reissue | complete | M8.5-IMP-01 closed on independent re-review; focused tests and fingerprinted pinned musl build passed. Selected-daemon suite passed 1/1 in 82.06s with cleanup, including reissued-file IPv4 traffic. |
| M8.5 backup failure | complete | M8.5-BF-R1 closed on independent re-review. Typed backup errors, unchanged saved/runtime state, no fixture apply, retained reservation and reader errors passed on a known-source selected-daemon run (1/1, 81.45s). |
| M8.6 | complete | R1 documentation, R2 IPv6 plan-gate and R3 stale milestone summary findings each closed on independent re-review. No code defect was found. |
| M8.7 | complete | Cached image/readiness/packages, pinned musl rebuild, host gates, two fresh selected-daemon full runs and the documented unfiltered opt-in command passed on stable source inputs. Final independent review closed all findings; coordinator accepted M8-A1–A10 for the scoped IPv4 Alpine work. |
#### Review and repair
| Finding / work item | State | Disposition / next evidence |
| --- | --- | --- |
| M8.1-R1; M8.2-R1/R2 | closed | Independent re-review closed documentation/image findings. |
| M8.3-R1–R7 | closed | Timeout/transport/error/name fixes reviewed; revised IPv4 probe passed. |
| M8.4-R1–R4 | closed | Driver protocol, path confinement, host transport and cleanup findings independently closed; real smoke passed. |
| M8.5-R1/R2 | closed | Delivered bytes consumed in client namespace; exact IPv4 route assertions; independent re-review closed both. |
| M8.5-R3 | closed | Negative ping requires completed transmitted/zero-received evidence; Docker execution errors cannot pass; independent re-review closed it. |
| M8.5 restore phase failure | closed | Fixture expected only the removed client's IPv4 reservation; Application correctly retained both saved addresses. Exact-set expectation now derives all removed-client addresses; focused regression and selected-daemon pass. [Diagnostic record](docs/diagnostics/milestone-8.md). |
| M8.5-RESTORE-DOC-1 | closed | Independent reviewer found stale restore status/handoff; coordinator updated the snapshot, and independent re-review closed it. No code defect found. |
| M8.5-IMP-01 | closed | P2: the Alpine driver had omitted invalid-file byte preservation. Repair checks all three sources before claiming unchanged originals; focused regression and fingerprinted selected-daemon rerun passed, and independent re-review closed it. |
| M8.5 failure phase | closed | Fixture misread `delete_client`'s bool as peer deletion; it actually reports vault-record removal and the legacy baseline has no record. Repair checks draft peer absence; focused regression and known-source selected-daemon run passed; independent review found no further issue with this check. |
| M8.5-BF-R1 | closed | P2: Commit-failure assertion formerly accepted any error. Repair requires `Err(AppError::Backup(_))`; focused regression and known-source selected-daemon run passed, and independent re-review closed it. |
| M8.6-R1 | closed | P2: TESTING.md and evidence report had stale M8.5 pending claims. Repairer recorded completed first-delivery transfer and fingerprinted backup-failure evidence; independent re-review closed it. |
| M8.6-R2 | closed | P2: General plan gate required IPv6 prerequisites despite M8's explicit IPv4-only scope. Coordinator scoped prerequisite language to the suite and retained production dual-stack requirement as unverified; independent re-review closed it. |
| M8.6-R3 | closed | P2: Milestone status summary still reported active M8.5 restore repair. Coordinator updated it to M8.1–M8.5 complete with M8.6/M8.7 open; independent re-review closed it. |
| M8.7-R1 | closed | P2: Reviewer saw a prior plan snapshot while final evidence was being entered. Task/handoff rows and outcome now record passing repeated runs and R3 closure; independent re-review closed it. |
| M8.7-R2 | closed | P3: Validation passage still described implemented and executed commands as planned, and snapshot date was stale. Coordinator corrected the passage and date; independent re-review closed it. |
Detailed closed-finding rationale and superseded attempts are in the historical
record. New diagnostic attempts belong in `docs/diagnostics/`, not this table.
#### Evidence and handoff
Previously reported evidence, carried forward without rerunning during this
2026-10-24 documentation cleanup:
| Scope | Result and limit |
| --- | --- |
| IPv4 prerequisite | Coordinator probe passed 1/1 (1.46s); isolated resources cleaned. |
| M8.4 Application smoke | Coordinator musl rebuild and selected-daemon smoke passed 1/1 (4.05s); named resources absent afterward. |
| M8.5 delivered-file traffic | Repaired real run passed 1/1 (14.23s); host support tests passed 21 with privileged test ignored; independent re-review closed R1/R2. |
| M8.5 deletion | Repaired coordinator real run passed 1/1 (42.96s); host support tests passed 23 with privileged test ignored; cleanup verified; independent re-review closed R3. |
| M8.5 restore | Worker reported fmt, focused/full locked tests, all-target Clippy, release build and diff check passing before the final reservation-helper edit; focused helper regression and pinned musl rebuild passed afterward. Coordinator exact selected-daemon run passed 1/1 (72.66s), with named resources absent. Independent review found no code issue; later integrated gates are recorded in the M8.7 row. |
| M8.5 import/reissue | Worker reported fmt, Clippy and focused import tests after its final assertion edit; full locked tests and release build passed before it. After M8.5-IMP-01 repair, focused regressions and Clippy passed. Coordinator pinned musl build and selected-daemon run passed 1/1 (82.06s), with stable source fingerprint and named resources absent. [Reproducible evidence](docs/evidence/milestone-8.md). |
| M8.5 backup failure | Worker reported fmt, Clippy, full locked tests/release before the final typed-error repair; focused regression, fmt and Clippy passed afterward. Coordinator pinned musl build and selected-daemon run passed 1/1 (81.45s), source-input SHA256 `b28dc6e4cb92aa6edf384d966ba5952a8d6d0efe4c22d6a804b67fb2c0297da2` stable before build/after run, driver SHA256 `ad45dc3f6b0987b075ac522e171c82c6651124c3d01c65c14df90595fe552815`, named resources absent. Independent re-review closed BF-R1. |
| M8.7 integrated gates | On the same source fingerprint, `cargo fmt --all -- --check`, `cargo clippy --locked --all-targets -- -D warnings`, `cargo test --locked`, `cargo build --locked --release`, release help/version, and `git diff --check` exited 0. Cached Alpine image ID `sha256:f41cd6b848d8280d4d86bcaceea962e1b3e5a73e9496c6f9d7929470d0e874d5` passed readiness; 33 constrained packages recorded. Pinned musl rebuild produced the same driver SHA256. Exact selected-daemon scenario passed 1/1 twice (80.77s each); documented unfiltered `--ignored` command passed 1/1 (81.87s). Named containers/networks absent after each. [Reproducible evidence](docs/evidence/milestone-8.md); prerequisite failures in [diagnostics](docs/diagnostics/milestone-8.md). Final independent review closed M8.7-R1/R2 and found M8-A1–A10 supported. |
Exact earlier commands, environment/build identities and intermediate results
remain in the historical record. For new evidence use the report format in
[execution records](docs/EXECUTION_RECORDS.md), including the tested revision or
working-tree identity. Earlier runs lack a complete source fingerprint; do not
infer that they validate subsequent edits. Final gates must run on the integrated
revision. Do not rerun unrelated gates merely to maintain this document.
Next action: Milestone 8 is accepted. Stop here; Milestone 9 remains pending and
requires a disposable systemd VM before its real D-Bus acceptance work.
#### Decisions and discoveries
- The user excluded IPv6 capability and traffic testing on 2026-10-22;
production dual-stack requirements are unchanged. Real IPv6 remains unverified.
- Use the real bounded Rustix reader and test-only fixture Apply. Alpine cannot
validate the private zbus/systemd adapter; real systemd acceptance remains M9.
- Build the musl test driver in the pinned Alpine builder; the host lacks the
musl prerequisites. Cached image/build layers may be reused; secret-bearing
fixture state must be fresh. Build identities are preserved in the history.
- Full fixture `wg setconf` resets learned peer state. Traffic assertions reconnect
retained clients and require fresh observations; peer presence alone is not
continuity evidence.
#### Outcome
M8 accepted for its user-scoped IPv4 Alpine Testcontainers work after independent
review closed all actionable findings and the host and repeated real-container
gates passed on one source fingerprint. M8-A1–A10 are satisfied within that
scope. IPv6 capability and traffic were excluded by user direction; real
systemd/D-Bus behavior remains unverified and belongs to M9.
Milestone 9 remains pending.
### Milestone 9 — Real systemd acceptance and release readiness
@@ -2635,7 +2987,7 @@ Acceptance: real D-Bus job handling, separate restart verification, address rele
## Current progress and resume point
- Milestones 1–7 are accepted; milestones 8–9 remain pending.
- Milestones 1–8 are accepted; milestone 9 remains pending.
- The approved dependency list is in [DEPENDENCIES.md](docs/DEPENDENCIES.md); container testing methodology is in [TESTING.md](docs/TESTING.md).
- Milestone 1 is expanded and the Rust environment is pinned.
- Milestone 2 delivered a lossless in-memory WireGuard configuration editor, versioned metadata, and behavioral preservation tests.
@@ -2646,8 +2998,17 @@ Acceptance: real D-Bus job handling, separate restart verification, address rele
- M1–M6 integrated gates and independent reviews passed. No Alpine or VM results are claimed.
- Milestone 7 delivered the native terminal workflow, safe startup/session projection, bounded single-owner worker, setup/import/draft/Commit/restart/delivery/backup/restore forms, synthetic PTY and workflow tests, and documentation. Independent R2 closed its list-status finding; all planned coordinator gates passed. Real WireGuard and systemd remain M8/M9.
- Milestone 8 delivered reviewed opt-in Alpine Testcontainers coverage for real IPv4 WireGuard peers, application lifecycle, saved/runtime separation, restoration, import/reissue, backup failures and the real Rustix reader. Host gates and three fresh real-container runs passed on one stable source fingerprint; M8-A1–A10 accepted. Real IPv6 behavior is unverified by user-authorized scope.
- Next action: stop after M8. Milestone 9 remains pending for disposable-VM systemd/D-Bus acceptance; no production access, commit or deployment is authorized by M8 acceptance.
## Plan maintenance
Update progress, findings, decisions, blockers, and the next action after each meaningful execution/review cycle and before handing off or ending a session. Preserve acceptance evidence when summarizing old iterations. Append scope changes with their reason; update the specification only when the user actually authorizes a requirement change.
Follow [Execution records and diagnostic workflow](docs/EXECUTION_RECORDS.md).
Update the current snapshot in place after a task result, review disposition,
blocker/scope change, acceptance, or handoff. Do not append command-by-command
attempts or worker transcripts. Keep register, milestone outcome and resume point
consistent. Preserve final evidence and scope-change reasons; archive superseded
history with a link before removing it. Change the specification only with actual
user authorization.
Planning reference: [OpenAI — Using PLANS.md for multi-hour problem solving](https://developers.openai.com/cookbook/articles/codex_exec_plans). The specific model roles, milestone numbering, and review policy above are project decisions requested by the user.