Files
Frank 8af40c402b chore(agent): commit the machine tier and the doctrine text the checkout was already running
Landing blocker for #61 and #63: the deployment checkout is 12 commits behind and
`merge --ff-only` refuses while tracked paths are dirty. Discard is not available here
(workspace policy: no `checkout`/`stash` on shared state), so the dirt is committed.

- `agent/APPEND_SYSTEM.md`: the point-of-use / one-work-log / PR-link doctrine, already
  byte-identical to `origin/main` (`cmp` against the trunk blob: no difference). No new
  prose — the commit exists so the trunk merge can proceed.
- `agent/config.yml` and `agent/managed-skills/**`: the machine-live tier the heartbeat
  owns (AGENTS.md § Live-sync tiers).

`ALLOW_SECRETS=1` was used, deliberately and after auditing the staged diff by hand: the
only credential-shaped strings in it are two copies of `AKIA0000000000000EXAMPLE` — the
inert all-zero AWS example id — and they appear inside the two skill pages that teach
that exact placeholder shape. This checkout is still running the pre-#61 hook; the merged
gate (value-scoped placeholder filter, `71601f7`) exempts this value, so the refusal
dies with this landing. No `--no-verify` was used: the gate ran and its verdict is
recorded here.

Refs #61
2026-10-21 01:44:51 -07:00

89 lines
4.1 KiB
Markdown

---
name: dotfiles-adoption-audit
description: "Use when deciding which userspace configs on a Dank box belong in the command-center dotfiles tier — separates tracked intent from generated expansion, app state, secrets, and config owned by another repo. Not for deploying one known file."
---
# Dotfiles adoption audit
Decides, with evidence, which files under `~/.config` (and top-level dotfiles) belong in
`dank/command-center`'s `dotfiles/` tier. The repo law being applied: **config in, state
out**, and **one fact, one owner**.
## The four gates (all four must pass)
1. **Installed?** `command -v <prog>` — config for software absent from the box is a prune
candidate, never an adoption target. This eliminates most of the clutter.
2. **Authored, or the expansion of something else?** Generated files must stay out even
though they look like config.
3. **Does another repo or installer already write it?** If a script or package owns the
file, tracking it here creates a second owner that loses every argument.
4. **Could a secret ever live in it?** If yes: template + quarantine, or leave it out.
## Gate 2 is the one people get wrong — prove generation from mtime
A generator walking the app list writes dozens of files in the same sub-second window.
Compare timestamps across unrelated programs:
```bash
cd ~/.config && stat -c '%y %n' */themes/noctalia* */noctalia.* */colors/noctalia* 2>/dev/null | sort
```
Identical seconds + microsecond progression across ten different programs = generated.
Re-check one file days later: a new mtime with no human edit proves regeneration, so the
tracked file must be the *source* (palette/config the generator reads), never its output.
The correct pattern is a tracked config that *imports* the generated path.
## Classify the whole tree per file, not per directory
The tracked unit is the **file symlink**, so a directory can be "real" while containing only
tracked symlinks. Never classify by directory:
```bash
cd ~/.config && for d in */; do d=${d%/}; tr=0; rl=0; rlfiles=""
while IFS= read -r f; do
case "$(readlink -f "$f" 2>/dev/null)" in
*/.omp/dotfiles/*) tr=$((tr+1));;
*) rl=$((rl+1)); rlfiles="$rlfiles ${f#./}";;
esac
done < <(find "$d" -maxdepth 2 \( -type f -o -type l \) 2>/dev/null \
| grep -vE 'Cache|cache|Cookies|storage|sessions|Crashpad|Default|thumbnails|extensions')
[ $((tr+rl)) -eq 0 ] && continue
printf '%-22s tracked=%-3s box-only=%-3s %s\n' "$d" "$tr" "$rl" "$(echo $rlfiles | cut -c1-90)"
done
```
Then read the headers of suspicious files — installers often confess ("Installed by
`infra/setup-dev.sh` into `~/.config/containers/systemd/`") and unit files name another
project's `WorkingDirectory`/`ExecStart`, which means that project owns them.
## Ownership before adoption
```bash
git -C ~/.omp ls-files dotfiles # packages deploy.sh sees
grep -rl "<unit-or-file-name>" ~/Projects/<proj> # does a project repo own it?
systemctl --user is-active <unit> # dead config is not load-bearing
```
## Secret-gap check (do this before adopting anything env-shaped)
The ignore list may cover only `.env`, so `frank-job-hunt.env` / `postgres.env` fall
through, and a token-shape secret gate (`sk-`, `ghp_`, PEM, JWT) does not match a
low-entropy `POSTGRES_PASSWORD=…`. Test both explicitly:
```bash
cd ~/.omp && git check-ignore -v --no-index 'dotfiles/x/postgres.env' || echo "NOT IGNORED"
sed -n 's/^patterns=//p' .githooks/pre-commit
```
Fix = add `*.env` to the quarantine block **and** a password-assignment pattern to the gate,
with a red test that stages a fake `POSTGRES_PASSWORD=` file.
## Land it
Packages are home-relative trees under `dotfiles/<pkg>/`; per-host overrides go in
`zz-$(hostname)` (stowed last). `dconf` is loaded via `dconf load /`, `systemd-user` gets
`daemon-reload` + conditional `enable --now`. Sequence: `scripts/deploy.sh --check` (dry run,
reports divergents) → adopt → `deploy.sh` → confirm live paths are symlinks into the repo →
`bats tests/` (includes the real-bundle `kb-lint`/coverage gate) → branch off `origin/main`,
PR to `main`, never merge without Frank's OK.