Make the runtime-selection, vm-facts drift, and dev-forge-opt-out checks fork-safe #36

Closed
opened 2026-08-02 23:09:22 +01:00 by vnprc-agent · 4 comments

Bring runtime-module-selection, vm-facts-negative, and dev-forge-opt-out up to the same fork-safety standard allod/archetypes#11 asks of pi-integration: all three fail against a deployment fork that supplies its own profiles/secrets/inventory inputs through the documented follows redirects, so a fork consuming the check suite has to exclude them and loses their coverage.

Primary goals:

  • Fixtures must not assume the public data layer — a check that drives a builder or captures expected values from public master data breaks on any fork whose identities, profile data, or inventory legitimately differ.
  • Negative fixtures must not depend on an unused enum member — a drift fixture that needs a valid runtime literal no real machine uses becomes unsatisfiable the day a fleet uses every member, which is exactly the state the microvm arc drives fleets toward.
  • Keep the coverage — each check pins a real property; the fix is fork-safe fixtures (synthetic machines/identities, data-derived expectations, or presence-gating), not deletion.

Per-check evidence, measured against a fork at archetypes 8aa0fce

runtime-module-selection builds its selection fixtures from whichever dev machine inventory declares first, driving mkDevVm without the profileData layer that machineConfigurations composes. On a fork whose secrets identity assigns that machine a different username than the public secrets do, the fixture merges two home.username definitions and dies before asserting anything:

error: The option `home-manager.users.<user>.home.username' has conflicting definition values:
- In `<fork profiles>/flake.nix': "<fork username>"
- In `<public secrets>/nixos/common.nix': "<public username>"

It also hard-requires the inventory input to expose checks.<system>.runtime-fact-mutations, which only the public inventory carries.

vm-facts-negative (as of the runtime-axis extension) requires a valid runtime literal that differs from every runtime in the composed vmFacts. With a two-value libvirt/microvm enum and a fleet that has machines on both, no such literal exists and the check refuses by its own design:

error: vm-facts-negative: fixture runtime "microvm" is in use by a real machine; it must differ from every runtime in vmFacts, or a factFor returning that literal would satisfy both this check and vm-facts-coherence's inventory diff

dev-forge-opt-out asserts the composed dev machine generates exactly the two age secrets captured from public master, byte-for-byte in file/path/owner/group/mode. A fork that uses the framework's own token-registry opt-out (the mechanism allod/archetypes#17 added) to withhold one credential fails the capture comparison:

error: dev-forge-opt-out: with-access allod-dev no longer generates today's two secrets with today's file/path/owner/group/mode

Scope

In scope: making these three checks pass, or self-skip with a named reason, against a fork supplying divergent profiles/secrets/inventory data. Out of scope: the fork's own exclusion list (each fork carries that until this lands) and pi-integration, which allod/archetypes#11 already tracks.

Found during the microvm runtime adoption arc (allod/strategy#20), where a fork excluding these checks is what makes its composed check set green.

Bring `runtime-module-selection`, `vm-facts-negative`, and `dev-forge-opt-out` up to the same fork-safety standard allod/archetypes#11 asks of `pi-integration`: all three fail against a deployment fork that supplies its own `profiles`/`secrets`/`inventory` inputs through the documented follows redirects, so a fork consuming the check suite has to exclude them and loses their coverage. Primary goals: - **Fixtures must not assume the public data layer** — a check that drives a builder or captures expected values from public `master` data breaks on any fork whose identities, profile data, or inventory legitimately differ. - **Negative fixtures must not depend on an unused enum member** — a drift fixture that needs a valid runtime literal no real machine uses becomes unsatisfiable the day a fleet uses every member, which is exactly the state the microvm arc drives fleets toward. - **Keep the coverage** — each check pins a real property; the fix is fork-safe fixtures (synthetic machines/identities, data-derived expectations, or presence-gating), not deletion. ### Per-check evidence, measured against a fork at archetypes `8aa0fce` `runtime-module-selection` builds its selection fixtures from whichever dev machine inventory declares first, driving `mkDevVm` without the `profileData` layer that `machineConfigurations` composes. On a fork whose secrets identity assigns that machine a different username than the public secrets do, the fixture merges two `home.username` definitions and dies before asserting anything: ``` error: The option `home-manager.users.<user>.home.username' has conflicting definition values: - In `<fork profiles>/flake.nix': "<fork username>" - In `<public secrets>/nixos/common.nix': "<public username>" ``` It also hard-requires the inventory input to expose `checks.<system>.runtime-fact-mutations`, which only the public inventory carries. `vm-facts-negative` (as of the runtime-axis extension) requires a valid runtime literal that differs from every runtime in the composed `vmFacts`. With a two-value `libvirt`/`microvm` enum and a fleet that has machines on both, no such literal exists and the check refuses by its own design: ``` error: vm-facts-negative: fixture runtime "microvm" is in use by a real machine; it must differ from every runtime in vmFacts, or a factFor returning that literal would satisfy both this check and vm-facts-coherence's inventory diff ``` `dev-forge-opt-out` asserts the composed dev machine generates exactly the two age secrets captured from public `master`, byte-for-byte in file/path/owner/group/mode. A fork that uses the framework's own token-registry opt-out (the mechanism allod/archetypes#17 added) to withhold one credential fails the capture comparison: ``` error: dev-forge-opt-out: with-access allod-dev no longer generates today's two secrets with today's file/path/owner/group/mode ``` ### Scope In scope: making these three checks pass, or self-skip with a named reason, against a fork supplying divergent `profiles`/`secrets`/`inventory` data. Out of scope: the fork's own exclusion list (each fork carries that until this lands) and pi-integration, which allod/archetypes#11 already tracks. Found during the microvm runtime adoption arc (allod/strategy#20), where a fork excluding these checks is what makes its composed check set green.
Author

A fourth check now has the same defect: pi-provider-lifecycle, added with the managed Pi provider arc.

Its fixture hard-codes machineName = "allod-dev" and drives mkDevVm directly, without the profileData layer machineConfigurations composes. That is the same shape as runtime-module-selection: on a fork whose profileData supplies that machine a different runtime identity, the fixture merges two home.username definitions and dies before asserting anything.

… while evaluating the option `home-manager.users.<user>.home.activation':
… while evaluating the option `home-manager.users.<user>.home.username':
error: The option `home-manager.users.<user>.home.username' has conflicting definition values:
- In `<fork profiles>/flake.nix': "<fork username>"
- In `<public secrets>/nixos/common.nix': "<public username>"

Measured against archetypes c41a294 with a fork consuming the check suite through the documented profiles/secrets/inventory follows redirects. Every other framework check in that fork's re-exported suite passes at this pin except the ones already listed here and microvm-host-join, which is the same fixture class again.

The cost is higher for this one than for the three above, because the fork has no fork-safe sibling and no real machine to fall back on: a deployment that manages no Pi provider yet has nothing that exercises the reconciler, so excluding the check drops the coverage entirely rather than relocating it. The fixture builds a machine only to reach a home-activation script and an age secret, so composing the fixture identity through the same layer production uses — or building a synthetic dev machine rather than reusing a real machine name — would keep the witness and cost nothing.

A fourth check now has the same defect: `pi-provider-lifecycle`, added with the managed Pi provider arc. Its fixture hard-codes `machineName = "allod-dev"` and drives `mkDevVm` directly, without the `profileData` layer `machineConfigurations` composes. That is the same shape as `runtime-module-selection`: on a fork whose `profileData` supplies that machine a different runtime identity, the fixture merges two `home.username` definitions and dies before asserting anything. ``` … while evaluating the option `home-manager.users.<user>.home.activation': … while evaluating the option `home-manager.users.<user>.home.username': error: The option `home-manager.users.<user>.home.username' has conflicting definition values: - In `<fork profiles>/flake.nix': "<fork username>" - In `<public secrets>/nixos/common.nix': "<public username>" ``` Measured against archetypes `c41a294` with a fork consuming the check suite through the documented `profiles`/`secrets`/`inventory` follows redirects. Every other framework check in that fork's re-exported suite passes at this pin except the ones already listed here and `microvm-host-join`, which is the same fixture class again. The cost is higher for this one than for the three above, because the fork has no fork-safe sibling and no real machine to fall back on: a deployment that manages no Pi provider yet has nothing that exercises the reconciler, so excluding the check drops the coverage entirely rather than relocating it. The fixture builds a machine only to reach a home-activation script and an age secret, so composing the fixture identity through the same layer production uses — or building a synthetic dev machine rather than reusing a real machine name — would keep the witness and cost nothing.
Author

Subsumed by allod/archetypes#88, which states the general rule (a check reads composed machines or its own fixtures, never data-input contents by name), widens the mkDevVm seam so fixtures need no real machine name, and adds a standing fork-safety witness. The vm-facts-negative enum point in this issue is a separate defect and stays here.

Subsumed by allod/archetypes#88, which states the general rule (a check reads composed machines or its own fixtures, never data-input contents by name), widens the `mkDevVm` seam so fixtures need no real machine name, and adds a standing fork-safety witness. The `vm-facts-negative` enum point in this issue is a separate defect and stays here.
Member

The fork-safety half is done by allod/archetypes#91, which closed allod/archetypes#88. Leaving this open for the residual named below.

What landed for the three checks this issue names:

  • runtime-module-selection — the dependency on inventory.checks.<system>.runtime-fact-mutations is gone. Enum validation for the runtime fact stays inventory's to own; the framework no longer reaches for another repo's check output to prove it.
  • vm-facts-coherence — the same dependency on inventory.checks.<system>.vm-specs-json is gone, and the freshness property it was buying is now derived in-check from inventory.lib.vmSpecsJson, a data surface rather than a check name. Its drift fixture selects its machine dynamically instead of naming allod-dev.
  • dev-forge-opt-out — all four fixtures build through the widened builder seam with a synthetic name, identity, definition, platform and runtime, and committed ciphertext under checks/fixtures/. The hand-copied literal snapshot of allod-dev's generated secrets is replaced by shape assertions keyed off the fixture's own username, plus a sweep over the composed dev machines pinning that nothing merged in after the builder changes the path, owner, group or mode of a framework-owned credential.

All three are green under ./fork-safety.sh, which runs the suite against a synthetic data trio under checks/fork-fixture/ sharing no name with the template's.

Residual, still open here: the enum problem this issue also records for vm-facts-negative. That is a separate defect from the fork-safety class and allod/archetypes#88 explicitly left it here rather than subsuming it.

The fork-safety half is done by allod/archetypes#91, which closed allod/archetypes#88. Leaving this open for the residual named below. What landed for the three checks this issue names: - `runtime-module-selection` — the dependency on `inventory.checks.<system>.runtime-fact-mutations` is gone. Enum validation for the `runtime` fact stays inventory's to own; the framework no longer reaches for another repo's check output to prove it. - `vm-facts-coherence` — the same dependency on `inventory.checks.<system>.vm-specs-json` is gone, and the freshness property it was buying is now derived in-check from `inventory.lib.vmSpecsJson`, a data surface rather than a check name. Its drift fixture selects its machine dynamically instead of naming `allod-dev`. - `dev-forge-opt-out` — all four fixtures build through the widened builder seam with a synthetic name, identity, definition, platform and runtime, and committed ciphertext under `checks/fixtures/`. The hand-copied literal snapshot of `allod-dev`'s generated secrets is replaced by shape assertions keyed off the fixture's own username, plus a sweep over the composed dev machines pinning that nothing merged in after the builder changes the path, owner, group or mode of a framework-owned credential. All three are green under `./fork-safety.sh`, which runs the suite against a synthetic data trio under `checks/fork-fixture/` sharing no name with the template's. Residual, still open here: the enum problem this issue also records for `vm-facts-negative`. That is a separate defect from the fork-safety class and allod/archetypes#88 explicitly left it here rather than subsuming it.
Author

Verified complete, including the residual enum defect this issue's own 2026-09-21 comment left open. The fork-safety half for the three named checks was already confirmed landed via allod/archetypes#91 (closing allod/archetypes#88), per that comment. The remaining vm-facts-negative enum problem (a sabotage literal needing to differ from every runtime in vmFacts becomes unsatisfiable once a fleet uses every enum member) is independently resolved by commit f6aba2d ('Remove the microvm guest runtime from the framework', 2026-09-08, part of allod/strategy#46 which permanently withdrew the microvm runtime: 'If the runtime is revisited, it will be redesigned rather than resumed'). That commit rewrote vm-facts-negative's and vm-facts-coherence's fixtures to sabotage with a literal outside inventory's runtime enum ('fixture-runtime') instead of an enum member, so the fixture no longer depends on an unused enum member at all, satisfying this issue's own stated goal verbatim; inventory's runtime enum itself was also reduced to one member (libvirt). This is still the design on current master (checks/vm-facts-negative.nix). Verified today by building both checks against the real composed fleet: 'nix build .#checks.x86_64-linux.vm-facts-negative' and '.#checks.x86_64-linux.vm-facts-coherence' both succeed (exit 0). No other open issue references this defect.

Verified complete, including the residual enum defect this issue's own 2026-09-21 comment left open. The fork-safety half for the three named checks was already confirmed landed via allod/archetypes#91 (closing allod/archetypes#88), per that comment. The remaining vm-facts-negative enum problem (a sabotage literal needing to differ from every runtime in vmFacts becomes unsatisfiable once a fleet uses every enum member) is independently resolved by commit f6aba2d ('Remove the microvm guest runtime from the framework', 2026-09-08, part of allod/strategy#46 which permanently withdrew the microvm runtime: 'If the runtime is revisited, it will be redesigned rather than resumed'). That commit rewrote vm-facts-negative's and vm-facts-coherence's fixtures to sabotage with a literal outside inventory's runtime enum ('fixture-runtime') instead of an enum member, so the fixture no longer depends on an unused enum member at all, satisfying this issue's own stated goal verbatim; inventory's runtime enum itself was also reduced to one member (libvirt). This is still the design on current master (checks/vm-facts-negative.nix). Verified today by building both checks against the real composed fleet: 'nix build .#checks.x86_64-linux.vm-facts-negative' and '.#checks.x86_64-linux.vm-facts-coherence' both succeed (exit 0). No other open issue references this defect.
Sign in to join this conversation.
No milestone
No project
No assignees
2 participants
Notifications
Due date
The due date is invalid or out of range. Please use the format "yyyy-mm-dd".

No due date set.

Dependencies

No dependencies set

Reference
allod/archetypes#36
No description provided.