Drop the transitional mkDefault from the dev VM forge SSH entry #84

Merged
vnprc merged 2 commits from agent/drop-forge-identityfile-default into master 2026-09-20 05:16:36 +01:00
Member

The dev VM's forge host block now has one author and no tie-breaker: the framework module that renders it from the VM's own identity record defines IdentityFile at normal priority instead of under lib.mkDefault. On every running machine nothing changes: each template machine evaluates to the same system derivation as before, because the profile copies that the default used to yield to are gone from allod/profiles (allod/profiles#10) and from the deployments' private profiles, and the default value and the plain value render the same bytes. What changes is what happens if a copy ever comes back: it is a module-system conflict at evaluation, never a silent winner, and the check now pins that with a fixture that must fail to evaluate. If it is wrong, revert this PR; the only machine-visible consequence of a wrong revert is the old tie-breaker returning. This is allod/archetypes#82, the follow-up allod/archetypes#54 and allod/strategy#56 name, and it closes the arc.

What changes

  • modules/dev-ssh-forge.nix: IdentityFile = "~/.ssh/${sshKeyName}", no lib.mkDefault; the header explains that a second definition of the block is now an evaluation conflict.
  • checks/ssh-settings.nix and its fixtures: the "dev VM beside an unmigrated profile copy" fixture, which asserted the copy's key won, is inverted into a negative witness asserting that the two modules together fail to evaluate. The hypervisor, dev, localForwards, wiring and sabotage fixtures are unchanged.

Risk

R2. Localized to one value and one fixture; the render is pinned byte-for-byte below. The integration uncertainty is a deployment whose private profiles still define the block: its next lock bump fails loudly at evaluation with a conflicting-definition error naming programs.ssh.settings.<forge host>.IdentityFile, which is the behaviour the issue asks for. Rollback is a revert.

Validation

Run by the integrator at 680442d:

  • nix build .#checks.x86_64-linux.ssh-settings --rebuild exits 0; the worker showed the new negative assertion failing with lib.mkDefault restored, then reverted.
  • nix flake check --no-build exits 0 (worker run, alone).
  • allod-dev's rendered ~/.ssh/config from this flake's own machines is byte-identical between master and this branch.
  • Deploy template (allod/deploy master, whose lock already pins allod/profiles#10 and allod/secrets#33) with --override-input archetypes <this branch>: allod-dev, nexus and privacy-1 evaluate to the same toplevel derivation paths as the current lock, so every template machine is unchanged byte for byte; allod-dev's rendered config is byte-identical to the lock's render.

Cold cross-family review: gpt-5.6-terra via codex exec, reasoning effort high, read the diff at 447213e (a Claude-family worker authored it). Three findings. Marked blocker: the module comment claimed any second definition of the block is a conflict, but Home Manager merges an identical migrated copy silently; true, and harmless, since two identical definitions render one identical block and nothing substitutes another key. Comment corrected in 680442d; no code change. Should-fix, accepted: the negative fixture's tryEval records only that rendering threw, so a positive twin now asserts the legacy copy renders on its own and the rejection is pinned to the combination, in 680442d. Should-fix, discarded: a fixture comment "names a real machine and a private path" — it cites the public allod/profiles file path and the public template machine in prose, not as a fixture value. The reviewer traced and cleared: forcing the rendered config text reaches the converted block and the failure is Home Manager's conflicting-option-types error between the legacy list and this module's string; the rendered bytes with no profile copy are unchanged; mkDevVm is the only production consumer and no in-tree fixture relied on the former default priority. No second reading follows the fix round; the fixes are settled by the rebuilt check and the sabotage of the new positive assertion.

Closes allod/archetypes#82
Refs allod/archetypes#54
Closes allod/strategy#56

The dev VM's forge host block now has one author and no tie-breaker: the framework module that renders it from the VM's own identity record defines `IdentityFile` at normal priority instead of under `lib.mkDefault`. On every running machine nothing changes: each template machine evaluates to the same system derivation as before, because the profile copies that the default used to yield to are gone from allod/profiles (allod/profiles#10) and from the deployments' private profiles, and the default value and the plain value render the same bytes. What changes is what happens if a copy ever comes back: it is a module-system conflict at evaluation, never a silent winner, and the check now pins that with a fixture that must fail to evaluate. If it is wrong, revert this PR; the only machine-visible consequence of a wrong revert is the old tie-breaker returning. This is allod/archetypes#82, the follow-up allod/archetypes#54 and allod/strategy#56 name, and it closes the arc. ## What changes - `modules/dev-ssh-forge.nix`: `IdentityFile = "~/.ssh/${sshKeyName}"`, no `lib.mkDefault`; the header explains that a second definition of the block is now an evaluation conflict. - `checks/ssh-settings.nix` and its fixtures: the "dev VM beside an unmigrated profile copy" fixture, which asserted the copy's key won, is inverted into a negative witness asserting that the two modules together fail to evaluate. The hypervisor, dev, localForwards, wiring and sabotage fixtures are unchanged. ## Risk R2. Localized to one value and one fixture; the render is pinned byte-for-byte below. The integration uncertainty is a deployment whose private profiles still define the block: its next lock bump fails loudly at evaluation with a conflicting-definition error naming `programs.ssh.settings.<forge host>.IdentityFile`, which is the behaviour the issue asks for. Rollback is a revert. ## Validation Run by the integrator at 680442d: - `nix build .#checks.x86_64-linux.ssh-settings --rebuild` exits 0; the worker showed the new negative assertion failing with `lib.mkDefault` restored, then reverted. - `nix flake check --no-build` exits 0 (worker run, alone). - `allod-dev`'s rendered `~/.ssh/config` from this flake's own machines is byte-identical between master and this branch. - Deploy template (`allod/deploy` master, whose lock already pins allod/profiles#10 and allod/secrets#33) with `--override-input archetypes <this branch>`: `allod-dev`, `nexus` and `privacy-1` evaluate to the same toplevel derivation paths as the current lock, so every template machine is unchanged byte for byte; `allod-dev`'s rendered config is byte-identical to the lock's render. Cold cross-family review: `gpt-5.6-terra` via `codex exec`, reasoning effort `high`, read the diff at 447213e (a Claude-family worker authored it). Three findings. Marked blocker: the module comment claimed any second definition of the block is a conflict, but Home Manager merges an identical migrated copy silently; true, and harmless, since two identical definitions render one identical block and nothing substitutes another key. Comment corrected in 680442d; no code change. Should-fix, accepted: the negative fixture's `tryEval` records only that rendering threw, so a positive twin now asserts the legacy copy renders on its own and the rejection is pinned to the combination, in 680442d. Should-fix, discarded: a fixture comment "names a real machine and a private path" — it cites the public allod/profiles file path and the public template machine in prose, not as a fixture value. The reviewer traced and cleared: forcing the rendered config text reaches the converted block and the failure is Home Manager's conflicting-option-types error between the legacy list and this module's string; the rendered bytes with no profile copy are unchanged; `mkDevVm` is the only production consumer and no in-tree fixture relied on the former default priority. No second reading follows the fix round; the fixes are settled by the rebuilt check and the sabotage of the new positive assertion. Closes allod/archetypes#82 Refs allod/archetypes#54 Closes allod/strategy#56
The trigger fired: the public deploy lock pins profiles past the copy's
deletion and deployments' private profiles have merged theirs. This module
is now the forge host block's only author, so a stray profile copy is a
module-system conflict at evaluation instead of a silent winner.

Invert the check's legacy-copy fixture into a negative witness: composing
dev-ssh-forge.nix beside a profile that still defines the block must now
fail evaluation, asserted via tryEval on the rendered config text rather
than resolving the profile's key with ssh -G.

Refs allod/strategy#56, allod/archetypes#54
The dev-ssh-forge.nix header overstated the case: Home Manager's freeform
settings merge two identical scalar values, so a profile copy with the same
IdentityFile renders the same block instead of clashing. Narrow the claim to
a different IdentityFile, and say plainly that an identical copy is a
harmless duplicate to delete, not a silent substitution.

tryEval on the combined fixture only shows that rendering threw, not why.
Add legacyCopyAloneRenders, a positive twin asserting the legacy profile
copy renders cleanly by itself, so the rejection beside dev-ssh-forge.nix is
pinned to the clash between the two authors rather than to something wrong
with the legacy fixture on its own.
vnprc approved these changes 2026-09-20 05:16:30 +01:00
vnprc merged commit 680442df11 into master 2026-09-20 05:16:36 +01:00
vnprc deleted branch agent/drop-forge-identityfile-default 2026-09-20 05:16:37 +01:00
Sign in to join this conversation.
No description provided.