Files
ansible-arch/docs/superpowers/specs/2026-07-28-ansible-reconciliation-design.md

4.6 KiB

Ansible-arch ↔ machine reconciliation

Date: 2026-07-28 Status: Approved

Context

The ansible-arch repo had an in-flight uncommitted diff (hyprland.conf tweaks, new packages across several roles, new dotfiles stow packages) plus untracked files. Separately, ansible.cfg sets become = True globally, which caused the dotfiles role's stow task to run as root — the hypr, zellij, and starship.toml symlinks in ~/.config ended up owned by root instead of the invoking user. A full audit of installed packages (pacman -Qeq) against what the roles declare also turned up drift in both directions.

Resolution order: prefer encoding drift into ansible roles and re-running the playbook over hand-editing the machine directly. Direct machine changes are only for things ansible can't (or already will, as a side effect) fix.

Scope

One reconciliation pass, no new tooling:

  1. Fix the become default so privilege escalation is opt-in per task, not global.
  2. Commit the existing in-flight diff and untracked files as-is — they're already the intended state.
  3. Fold audited package/service drift into the appropriate roles.

No repeatable drift-check tool is being added (explicitly out of scope — one-time fix, matches existing YAGNI preference).

1. Privilege escalation fix

  • Remove become = True from ansible.cfg [privilege_escalation] (default becomes False).
  • Add explicit become: true to every task that needs root:
    • all pacman: module tasks (every role)
    • systemd: service-enable tasks (hyprland role)
    • user: group-membership task (hyprland role, docker group)
    • chsh / getent tasks and the sudoers-setup step (common role)
  • No change needed for kewlfft.aur.aur tasks or the paru makepkg shell task — they never had explicit become set, so they now correctly inherit become: false. (They were arguably broken before: makepkg refuses to run as root. This went unnoticed because the packages were already installed, so the tasks were no-ops.)
  • No change needed for dotfiles-role stow tasks or any blockinfile/.zshrc/ .profile tasks — same reasoning, they now correctly run unprivileged.
  • Net effect on ~/.config symlink ownership: no manual chown/rm step is needed. The dotfiles role's "Remove existing config files before stowing" task unconditionally removes the hypr/zellij/starship.toml/kanshi targets before every stow run, so simply re-running site.yml after this fix deletes the root-owned symlinks and recreates them owned by the invoking user.

2. Formalize the in-flight diff

Commit as-is:

  • dotfiles/hypr/.config/hypr/hyprland.conf (keyboard layout, mouse/scroll behavior, hyprlock keybind, monitor scaling, dwindle pseudotile changes)
  • roles/ai, roles/common, roles/devops, roles/dotfiles, roles/hyprland, roles/multimedia package/task additions already in the working tree

git add untracked files that are already part of an existing stow package:

  • dotfiles/hypr/.config/hypr/hyprlock.conf
  • dotfiles/hypr/.config/hypr/monitors.sh
  • dotfiles/kanshi/
  • dotfiles/starship/

3. Package/service audit — role placement

Cross-referenced pacman -Qeq (explicitly installed) against every roles/*/tasks/*.yml package list.

Add to ctf role:

  • gitleaks
  • nuclei-bin (AUR)
  • picocom
  • python-pyserial

Add to common role:

  • nano
  • vim
  • sshpass
  • smartmontools
  • cargo-audit

Explicitly out of scope (base install / firmware / bootloader — provisioning concerns this repo doesn't own): base, linux, linux-firmware, efibootmgr, lvm2, sudo, btrfs-progs, amd-ucode, intel-media-driver, libva-intel-driver, vulkan-intel, sof-firmware, xorg-server, xorg-xinit, zram-generator, snapper.

Flagged, no role change: dunst — installed but superseded by swaync (already declared in the hyprland role). Left as-is; not added to any role, not uninstalled.

No action needed (false positives from the audit): paru, paru-debug (installed by the common role's own bootstrap task, not a plain pacman list item), ttf-joypixels (already declared, just via a single-value name: key the audit grep didn't match).

Minor cleanup: remove the duplicate docker package entry from the cs_student role — devops already declares it.

Verification

  • ansible-playbook site.yml --check --diff runs clean (no unexpected changes) after the become fix and role edits.
  • ansible-playbook site.yml run confirms ~/.config/hypr, ~/.config/zellij, ~/.config/starship.toml, ~/.config/kanshi are owned by the invoking user, not root.
  • git status clean after committing.