diff --git a/home/.agents/skills/find-inspiration/decisions.jsonl b/home/.agents/skills/find-inspiration/decisions.jsonl index 74ee9ac..e41fd11 100644 --- a/home/.agents/skills/find-inspiration/decisions.jsonl +++ b/home/.agents/skills/find-inspiration/decisions.jsonl @@ -16,3 +16,24 @@ {"id": "converge-setup", "decision": "spike", "title": "One idempotent converge command replacing the README's manual sequence", "kind": "foundation", "rationale": "", "source_repo": "samandmoore/dotfiles", "run_date": "2026-08-19", "issue_url": "https://github.com/nonrational/dotfiles/issues/28", "logged_at": "2026-08-19T17:49:47+00:00"} {"id": "rcm-deploy", "decision": "reject", "title": "Replace bespoke manifest+deploy.sh with rcm (rcup/lsrc, tags)", "kind": "foundation", "rationale": "rcm's tag model is strictly weaker than the manifest's condition column (cannot express tool=, host=, or the bin.Darwin -> ~/bin rename), and manifest-deploy already shipped.", "source_repo": "samandmoore/dotfiles", "run_date": "2026-08-19", "issue_url": null, "logged_at": "2026-08-19T17:49:47+00:00"} {"id": "fish-migration", "decision": "spike", "title": "fish as the interactive shell (sibling to the zsh-migration spike)", "kind": "foundation", "rationale": "", "source_repo": "samandmoore/dotfiles", "run_date": "2026-08-19", "issue_url": "https://github.com/nonrational/dotfiles/issues/29", "logged_at": "2026-08-19T17:49:48+00:00"} +{"id": "ecosystem-manifests", "decision": "adopt", "title": "Per-ecosystem install manifests beyond Brewfile (VS Code extensions, npm globals)", "kind": "leaf", "rationale": "", "source_repo": "webpro/dotfiles", "run_date": "2026-08-22", "issue_url": "https://github.com/nonrational/dotfiles/issues/33", "logged_at": "2026-08-22T17:51:47+00:00"} +{"id": "host-os-predicates", "decision": "adopt", "title": "Shared OS/host predicate helpers instead of re-derived uname expressions", "kind": "leaf", "rationale": "", "source_repo": "webpro/dotfiles", "run_date": "2026-08-22", "issue_url": "https://github.com/nonrational/dotfiles/issues/34", "logged_at": "2026-08-22T17:51:48+00:00"} +{"id": "shell-function-tests", "decision": "adopt", "title": "Assert shell helpers behave, not just that they are defined", "kind": "leaf", "rationale": "", "source_repo": "webpro/dotfiles", "run_date": "2026-08-22", "issue_url": "https://github.com/nonrational/dotfiles/issues/35", "logged_at": "2026-08-22T17:51:50+00:00"} +{"id": "bats-harness", "decision": "reject", "title": "bats-core instead of the bespoke pass/fail test harness", "kind": "leaf", "rationale": "The bespoke harness already works and is ~80 lines; sandboxing and assertions stay ours either way, so the only gain is less scaffolding, paid for with a bats-core install on both CI runners.", "source_repo": "webpro/dotfiles", "run_date": "2026-08-22", "issue_url": null, "logged_at": "2026-08-22T17:51:50+00:00"} +{"id": "ci-real-install", "decision": "adopt", "title": "CI that installs onto a bare machine instead of smoke-testing symlinks", "kind": "leaf", "rationale": "", "source_repo": "webpro/dotfiles", "run_date": "2026-08-22", "issue_url": "https://github.com/nonrational/dotfiles/issues/36", "logged_at": "2026-08-22T17:51:51+00:00"} +{"id": "duti-declarative", "decision": "adopt", "title": "File associations as a data file instead of an imperative script", "kind": "leaf", "rationale": "", "source_repo": "webpro/dotfiles", "run_date": "2026-08-22", "issue_url": "https://github.com/nonrational/dotfiles/issues/37", "logged_at": "2026-08-22T17:51:52+00:00"} +{"id": "editorconfig", "decision": "adopt", "title": "Root .editorconfig so whitespace conventions are declared, not inferred", "kind": "leaf", "rationale": "", "source_repo": "webpro/dotfiles", "run_date": "2026-08-22", "issue_url": "https://github.com/nonrational/dotfiles/issues/38", "logged_at": "2026-08-22T17:51:53+00:00"} +{"id": "update-everything", "decision": "reject", "title": "One command to update every package manager on the machine", "kind": "leaf", "rationale": "Subsumed by the open converge-setup spike (#28), which asks a strictly larger version of the same question; adopting topgrade now would pre-commit that spike's answer.", "source_repo": "webpro/dotfiles", "run_date": "2026-08-22", "issue_url": null, "logged_at": "2026-08-22T17:51:53+00:00"} +{"id": "xdg-config-home", "decision": "reject", "title": "Move deployed config under $XDG_CONFIG_HOME", "kind": "foundation", "rationale": "Benefit is home-directory tidiness only, which does not clear the foundation bar. The repo already runs a mixed layout (kitty and opencode under ~/.config, everything else at home root) and it works; ~/.claude, ~/.agents and ~/.copilot do not honor XDG and would stay at home root regardless, so a migration converts one mixed layout into another at the cost of auditing every ~/.x literal.", "source_repo": "webpro/dotfiles", "run_date": "2026-08-22", "issue_url": null, "logged_at": "2026-08-22T17:51:53+00:00"} +{"id": "stow-deploy", "decision": "reject", "title": "GNU stow instead of manifest + deploy.sh", "kind": "foundation", "rationale": "Same defect as the already-rejected rcm-deploy: stow has no condition model, so os=, host=, tool= and the bin.Darwin -> ~/bin rename are inexpressible. Strictly less expressive than the manifest that already shipped with tests.", "source_repo": "webpro/dotfiles", "run_date": "2026-08-22", "issue_url": null, "logged_at": "2026-08-22T17:51:53+00:00"} +{"id": "skill-script-tests", "decision": "adopt", "title": "Test the executable code that lives inside skills", "kind": "leaf", "rationale": "", "source_repo": "paulirish/dotfiles", "run_date": "2026-08-23", "issue_url": "https://github.com/nonrational/dotfiles/issues/39", "logged_at": "2026-08-23T14:26:10+00:00"} +{"id": "preflight-gate", "decision": "adopt", "title": "One preflight command, so CI and the developer share a definition of \"checked\"", "kind": "leaf", "rationale": "", "source_repo": "paulirish/dotfiles", "run_date": "2026-08-23", "issue_url": "https://github.com/nonrational/dotfiles/issues/40", "logged_at": "2026-08-23T14:26:11+00:00"} +{"id": "skill-index", "decision": "adopt", "title": "Generated index of available skills", "kind": "leaf", "rationale": "", "source_repo": "paulirish/dotfiles", "run_date": "2026-08-23", "issue_url": "https://github.com/nonrational/dotfiles/issues/41", "logged_at": "2026-08-23T14:26:12+00:00"} +{"id": "frontmatter-required-keys", "decision": "adopt", "title": "Validate frontmatter content, not just that it parses", "kind": "leaf", "rationale": "", "source_repo": "paulirish/dotfiles", "run_date": "2026-08-23", "issue_url": "https://github.com/nonrational/dotfiles/issues/42", "logged_at": "2026-08-23T14:26:13+00:00"} +{"id": "scheduled-automation", "decision": "reject", "title": "Checked-in cron for recurring local automation", "kind": "leaf", "rationale": "No demonstrated need for scheduled local automation here, and their implementation concedes in-comment that the tracked crontab drifts from the real one by hand -- adopting it would import the drift and then require building the audit to fix it.", "source_repo": "paulirish/dotfiles", "run_date": "2026-08-23", "issue_url": null, "logged_at": "2026-08-23T14:26:13+00:00"} +{"id": "skill-distribution", "decision": "spike", "title": "Publish skills as an installable package, not just a symlink source", "kind": "foundation", "rationale": "", "source_repo": "paulirish/dotfiles", "run_date": "2026-08-23", "issue_url": "https://github.com/nonrational/dotfiles/issues/43", "logged_at": "2026-08-23T14:26:15+00:00"} +{"id": "shellcheck-ci", "decision": "adopt", "title": "Shellcheck every shell file, discovered by file(1) rather than by extension", "kind": "leaf", "rationale": "", "source_repo": "jessfraz/dotfiles", "run_date": "2026-08-23", "issue_url": "https://github.com/nonrational/dotfiles/issues/45", "logged_at": "2026-08-23T14:41:17+00:00"} +{"id": "makefile-help", "decision": "adopt", "title": "Self-documenting Makefile with a help target", "kind": "leaf", "rationale": "", "source_repo": "jessfraz/dotfiles", "run_date": "2026-08-23", "issue_url": "https://github.com/nonrational/dotfiles/issues/46", "logged_at": "2026-08-23T14:41:18+00:00"} +{"id": "dependabot-actions", "decision": "adopt", "title": "Dependabot on GitHub Actions versions", "kind": "leaf", "rationale": "", "source_repo": "jessfraz/dotfiles", "run_date": "2026-08-23", "issue_url": "https://github.com/nonrational/dotfiles/issues/47", "logged_at": "2026-08-23T14:41:19+00:00"} +{"id": "codex-harness-support", "decision": "reject", "title": "Codex CLI as an additional agent harness fed from home/.agents/rules", "kind": "leaf", "rationale": "No demonstrated Codex use here, and the concatenation machinery it depends on is unlanded (PR #32 builds it for Pi); adding a fifth harness shim before the fourth has merged compounds unproven work. Reconsider if Codex enters the toolchain.", "source_repo": "jessfraz/dotfiles", "run_date": "2026-08-23", "issue_url": null, "logged_at": "2026-08-23T14:41:19+00:00"} +{"id": "nix-home-manager", "decision": "spike", "title": "Nix flake exposing a home-manager module, additive to the existing deploy", "kind": "foundation", "rationale": "", "source_repo": "jessfraz/dotfiles", "run_date": "2026-08-23", "issue_url": "https://github.com/nonrational/dotfiles/issues/48", "logged_at": "2026-08-23T14:41:20+00:00"} diff --git a/home/.agents/skills/find-inspiration/runs/2026-08-22-webpro.json b/home/.agents/skills/find-inspiration/runs/2026-08-22-webpro.json new file mode 100644 index 0000000..c4138d5 --- /dev/null +++ b/home/.agents/skills/find-inspiration/runs/2026-08-22-webpro.json @@ -0,0 +1,166 @@ +{ + "source_repo": "webpro/dotfiles", + "run_date": "2026-08-22", + "items": [ + { + "id": "ecosystem-manifests", + "title": "Per-ecosystem install manifests beyond Brewfile (VS Code extensions, npm globals)", + "kind": "leaf", + "problem": "A rebuilt machine restores everything Homebrew knows about and nothing else.", + "you": "`Brewfile` (97 lines: brew, cask, mas) is the only package manifest, and `home/bin.Darwin/brewfile-dump` only re-dumps that. VS Code extensions, global npm packages and cargo binaries are recorded nowhere, so they are silently lost on a fresh machine. `home/.tool-versions` pins five runtimes -- `bun 1.3.14`, `deno 2.8.0`, `nodejs 25.8.1`, `python 3.14.4t`, `rust 1.92.0` -- so there are five ecosystems whose globally-installed packages have no manifest.", + "them": "`install/` holds one flat manifest per ecosystem \u2014 `Brewfile`, `Caskfile`, `Codefile` (VS Code extensions), `npmfile`, `Rustfile`, `pacmanfile` \u2014 each wired to a Makefile target that pipes it into the right installer (`code --install-extension`, `npm install --location global`, `cargo install`).", + "difference": "Where the reproducibility boundary sits. Ours stops at Homebrew; theirs covers every installer that puts a binary on PATH. This is not hypothetical here: `.tool-versions` pins node, rust, deno and bun, and `Brewfile` installs `visual-studio-code` -- every one of those installs global packages that nothing in this repo records.", + "effort": "S", + "risk": "low", + "translation": "Add `Codefile` and `npmfile` at repo root beside `Brewfile`, plus `make code-extensions` / `make npm-globals` targets that read them. A `Rustfile` is worth considering too now that rust is pinned. Skip `Caskfile` and `pacmanfile`: `brew bundle` already handles the `cask`/`mas` lines in one file, and there is no Arch machine. Add a dump counterpart next to `brewfile-dump` so the lists can be refreshed from a live machine rather than hand-maintained.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "host-os-predicates", + "title": "Shared OS/host predicate helpers instead of re-derived uname expressions", + "kind": "leaf", + "problem": "The manifest's `host=` condition and the shell's per-host rc must agree on what this machine is called, but every consumer re-derives it independently.", + "you": "`uname -n | sed -e 's/\\.lan$//g' -e 's/\\.local$//g'` is written out in `deploy.sh:74`, `home/.bashrc:3` and `test/test_deploy.sh:206`, and `home/.zshrc:66` has already drifted (it strips only `.local`). A `host=nyx` manifest row and a `.bashrc.nyx` lookup can disagree with nothing to catch it. OS detection is the same story: bare `uname` in four places, no shared predicate.", + "them": "`bin/is-macos`, `is-ubuntu`, `is-arch`, `is-arm64`, `is-supported`, `is-executable` \u2014 one-line executables on PATH, called from the Makefile, from `system/.path`, and asserted directly in `test/bin.bats`.", + "difference": "One definition with tests versus four copies that drift. The drift is already present in `.zshrc`, and it is the kind that fails silently \u2014 you get the default rc, not an error.", + "effort": "S", + "risk": "low", + "translation": "`deploy.sh` runs before anything is symlinked, so the helper cannot live in `~/bin`. Put it in the repo (e.g. `scripts/host-id.sh`, sourced by `deploy.sh` and by the rc files through their deployed path) or, cheaper, leave the expressions in place and add a test asserting all four agree. Either way `.zshrc` gets fixed and the agreement becomes a test rather than a convention.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "shell-function-tests", + "title": "Assert shell helpers behave, not just that they are defined", + "kind": "leaf", + "problem": "A shell function can be defined and still be wrong; nothing here would notice.", + "you": "`test/test_shell.sh` sources each entry point and runs `type NAME` on a handful of sentinels (`source_if_exists`, `prpg`, `ll`). That catches a syntax error or a rename and nothing else. The 22 scripts in `home/bin.Darwin/` have no tests at all.", + "them": "`test/function.bats` `load`s `system/.function` and `system/.function_text` directly and asserts on real output against a fixture \u2014 `calc 1+2` is 3, `line 2` is `bar`, `duplines`/`uniqlines` return the expected sets.", + "difference": "Definition checks catch renames; behavioral checks catch logic regressions. The functions here are edited by hand and by agents, and the current suite would pass a function whose body was replaced with `true`.", + "effort": "M", + "risk": "low", + "translation": "Extend `test/test_shell.sh` with a behavioral section using the existing fake-`$HOME` sandbox: source the function-bearing rc files in a subshell and assert output. Start with the pure helpers that need no network, no macOS app and no repo checkout, then widen to `bin.Darwin/` scripts that are safe to run (`calc`, `whohas`).", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "bats-harness", + "title": "bats-core instead of the bespoke pass/fail test harness", + "kind": "leaf", + "problem": "Roughly 80 lines of the test suite are harness, not tests, and they are ours to maintain.", + "you": "`test/test_deploy.sh` (394 lines) and `test/test_shell.sh` (75) hand-roll `ok`/`bad` counters, sandbox creation, the final tally and the exit code. Every new test file would re-implement them or grow a shared include.", + "them": "`make test` is `bats test`. Each `@test` is an isolated case with TAP output, `load` sources the file under test, and the runner owns counting and reporting.", + "difference": "A dependency versus a maintained harness. bats replaces the scaffolding only \u2014 the sandboxing and the assertions stay ours either way \u2014 and it has to be installed on both CI runners (`brew install bats-core`, `apt install bats`), which adds a bootstrap step to a workflow that currently needs nothing but the checkout.", + "effort": "M", + "risk": "low", + "translation": "Add `bats-core` to `Brewfile`, convert `test/test_shell.sh` first (it is 75 lines and has the least sandbox machinery), keep `make test` as the entry point. Convert `test_deploy.sh` only if the first conversion actually reads better.", + "pilot": "", + "spike_question": "", + "decision": "reject", + "rationale": "The bespoke harness already works and is ~80 lines; sandboxing and assertions stay ours either way, so the only gain is less scaffolding, paid for with a bats-core install on both CI runners." + }, + { + "id": "ci-real-install", + "title": "CI that installs onto a bare machine instead of smoke-testing symlinks", + "kind": "leaf", + "problem": "Nothing verifies that a new machine can actually be brought up from this repo.", + "you": "`.github/workflows/ci.yml` runs `make test`, then `./deploy.sh apply && ./deploy.sh audit` with `HOME=$(mktemp -d)`, on ubuntu-latest and macos-latest, on push and PR. Homebrew installation, `brew bundle`, asdf plugin setup and submodule bootstrap are never exercised \u2014 the parts most likely to rot are the parts CI does not touch.", + "them": "`dotfiles-installation.yml` uninstalls every brew formula and then Homebrew itself, runs `make` end to end on macos-14, macos-15, macos-26 and ubuntu-latest, sources `$HOME/.bash_profile` to prove the shell loads afterward, and runs on a weekly cron so upstream breakage surfaces without anyone pushing.", + "difference": "Ours proves the symlinks are correct. Theirs proves the machine comes up. The already-filed `bootstrap-one-liner` (#25) ships exactly the code path we currently have no way to test, and the weekly cron is what turns CI from a diff-gate into a rot detector.", + "effort": "M", + "risk": "med", + "translation": "Add a second workflow (`install.yml`) on a weekly `schedule` plus `workflow_dispatch`, separate from `ci.yml` so a flaky real install never blocks a PR. Start macOS-only against the current `Brewfile`, `continue-on-error` at first, and pin the runner images explicitly rather than tracking `macos-latest`. Sequence after bootstrap-one-liner (#25): the real-install workflow exists to exercise that script's code path, so building it first would test a path that does not exist yet.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "duti-declarative", + "title": "File associations as a data file instead of an imperative script", + "kind": "leaf", + "problem": "Adding a file association means editing bash.", + "you": "`scripts/set-file-associations.sh` is imperative: it hardcodes the Sublime bundle id, hard-fails if Sublime is not installed, and sets exactly two associations (`public.plain-text` and `.md`).", + "them": "`install/duti` is a plain three-column data file \u2014 bundle id, extension, role \u2014 with about 90 rows across VS Code and VLC, applied in one shot with `duti -v install/duti`.", + "difference": "Same tool underneath; the difference is whether the association list is data or code. Data means one line per new extension, diffs that read as a list, and no bash to review.", + "effort": "S", + "risk": "low", + "translation": "Replace the body of `scripts/set-file-associations.sh` with a `duti -v etc/duti` call and move the association list into `etc/duti`. Keep the existing preflight checks for `duti` and the Sublime bundle; `make set-file-associations` stays the entry point.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "editorconfig", + "title": "Root .editorconfig so whitespace conventions are declared, not inferred", + "kind": "leaf", + "problem": "Every editor and agent guesses this repo's indentation, and they guess differently.", + "you": "No `.editorconfig`. `deploy.sh` is 4-space, the `Makefile` is tabs, JSON and YAML are 2-space, and shell rc files are mixed. Nothing declares any of it.", + "them": "Root `.editorconfig` with `root = true`: utf-8, lf endings, final newline, trimmed trailing whitespace, 2-space indent.", + "difference": "Cheapest possible way to stop whitespace-only diffs, and it is machine-readable by the agent harnesses this repo exists to configure.", + "effort": "S", + "risk": "low", + "translation": "Copy the shape but not the values: a blanket `indent_size = 2` would fight `deploy.sh`'s 4-space bodies and break the `Makefile`. Scope per glob \u2014 `[*]` for charset/eol/final-newline/trailing-whitespace only, `[*.{json,yml,yaml,md}]` 2-space, `[*.sh]` 4-space, `[Makefile]` tabs.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "update-everything", + "title": "One command to update every package manager on the machine", + "kind": "leaf", + "problem": "There is an install path and no update path.", + "you": "`make brew-bundle` installs what the `Brewfile` declares. Nothing updates brew formulae, casks, asdf plugins, gems and global npm packages together, so updating is a remembered sequence of commands.", + "them": "`dot update` shells out to `topgrade`, with `config/topgrade.toml` narrowing what it is allowed to touch (`disable = [\"containers\", \"git_repos\", \"pnpm\", \"yarn\", \"node\"]`, `assume_yes`, `cleanup`).", + "difference": "A dedicated tool that already knows every package manager, constrained by a config file, versus a sequence you have to remember. The config is the interesting half \u2014 an unconstrained `topgrade` would happily `git pull` every repo it finds.", + "effort": "S", + "risk": "low", + "translation": "Add `topgrade` to `Brewfile`, a `topgrade.toml` in the repo deployed via `manifest`, and `make update`. Overlaps the already-filed `converge-setup` spike (#28) \u2014 that one is about converging a machine to the declared state, this one is about moving the declared state forward. Fold into #28 if the spike concludes they are the same command.", + "pilot": "", + "spike_question": "", + "decision": "reject", + "rationale": "Subsumed by the open converge-setup spike (#28), which asks a strictly larger version of the same question; adopting topgrade now would pre-commit that spike's answer." + }, + { + "id": "xdg-config-home", + "title": "Move deployed config under $XDG_CONFIG_HOME", + "kind": "foundation", + "problem": "Every tool's config is a dotfile at the root of the home directory.", + "you": "The `manifest` has 33 rows, and the layout is already mixed rather than uniformly home-root: `home/.config/kitty` and `home/.config/opencode/opencode.jsonc` already target `~/.config/`, and `fonts` targets `~/Library/Fonts/dotfiles`. Everything else is a `~/.something` dotfile, including `~/.claude`, `~/.agents`, `~/.gemini` and `~/.copilot`. No XDG variables are exported by the rc files.", + "them": "`runcom/` stows to `$HOME` (only the rc files bash insists on finding there) and `config/` stows to `$XDG_CONFIG_HOME`. `system/.env` exports the full XDG set, including an `XDG_RUNTIME_DIR` created 0700 because macOS has no session-lifetime directory.", + "difference": "Home-directory hygiene, and tools that honor XDG stop scattering. But the honest read is that this repo has already arrived at the end state a migration would produce: two XDG-honoring tools live under `~/.config`, everything else does not, and it works. The agent harnesses this repo mainly exists to configure (`~/.claude`, `~/.agents`, `~/.copilot`) do not honor XDG and would stay at home root permanently, so a full migration converts a working mixed layout into a differently-shaped mixed layout.", + "effort": "L", + "risk": "med", + "translation": "`deploy.sh` already writes arbitrary targets, so `~/.config/tmux/tmux.conf` needs only a manifest edit plus parent-directory creation. The work is auditing every `~/.x` literal in the rc files, `.vim`, and `test/test_shell.sh`'s `RC_FILES` list.", + "pilot": "Largely already answered: `home/.config/opencode/opencode.jsonc` proves `deploy.sh` handles a nested target two levels deep, so the mechanical question a pilot would ask has a known answer. What remains is auditing every `~/.x` literal in the rc files, `.vim`, and `test/test_shell.sh`'s `RC_FILES` list -- which is the actual cost and is not reduced by piloting.", + "spike_question": "", + "decision": "reject", + "rationale": "Benefit is home-directory tidiness only, which does not clear the foundation bar. The repo already runs a mixed layout (kitty and opencode under ~/.config, everything else at home root) and it works; ~/.claude, ~/.agents and ~/.copilot do not honor XDG and would stay at home root regardless, so a migration converts one mixed layout into another at the cost of auditing every ~/.x literal." + }, + { + "id": "stow-deploy", + "title": "GNU stow instead of manifest + deploy.sh", + "kind": "foundation", + "problem": "The deploy engine is bespoke code this repo has to own and test.", + "you": "A 30-row `manifest`, `deploy.sh` with `apply`/`audit`/`--dry-run` and `os=` / `host=` / `tool=` conditions, and a 394-line test suite covering it.", + "them": "`stow -t \"$HOME\" runcom` and `stow -t \"$XDG_CONFIG_HOME\" config`, with a `.bak` shuffle for pre-existing real files and per-OS differences pushed into separate Makefile targets.", + "difference": "Stow has no condition model. There is no way to express `os=Darwin`, `host=nyx`, `tool=git-town`, or the `bin.Darwin` to `~/bin` rename \u2014 those become directory layout and Makefile branching instead. It is directory-granular by design where the manifest is entry-granular. Same shape as the already-rejected `rcm-deploy`: a third-party tool that is strictly less expressive than what shipped.", + "effort": "L", + "risk": "high", + "translation": "Would mean restructuring `home/` into per-target stow packages and reproducing the condition column as Makefile targets, then deleting `deploy.sh` and its tests.", + "pilot": "", + "spike_question": "", + "decision": "reject", + "rationale": "Same defect as the already-rejected rcm-deploy: stow has no condition model, so os=, host=, tool= and the bin.Darwin -> ~/bin rename are inexpressible. Strictly less expressive than the manifest that already shipped with tests." + } + ] +} \ No newline at end of file diff --git a/home/.agents/skills/find-inspiration/runs/2026-08-23-jessfraz.json b/home/.agents/skills/find-inspiration/runs/2026-08-23-jessfraz.json new file mode 100644 index 0000000..b9b7d4b --- /dev/null +++ b/home/.agents/skills/find-inspiration/runs/2026-08-23-jessfraz.json @@ -0,0 +1,86 @@ +{ + "source_repo": "jessfraz/dotfiles", + "run_date": "2026-08-23", + "items": [ + { + "id": "shellcheck-ci", + "title": "Shellcheck every shell file, discovered by file(1) rather than by extension", + "kind": "leaf", + "problem": "No shell script in this repo is linted, and the scripts most likely to break are the ones a `*.sh` glob would never find.", + "you": "Nothing runs shellcheck. `make test` executes two behavior suites and the `check-*` targets verify symlinks and frontmatter; no static analysis runs anywhere. Concretely: `file(1)` identifies **31 tracked shell files** in this repo, and **only 10 end in `.sh`** -- the other 21 are the extensionless executables in `home/bin.Darwin/` plus the rc files (`.bashrc`, `.bash_profile`, `.zshrc`, `.githelpers`), which is exactly the set that breaks a login shell when it goes wrong.", + "them": "`test.sh` walks every tracked file, asks `file \"$f\" | grep --quiet shell` whether it is a shell script, and runs shellcheck on each match \u2014 collecting failures into an array so one bad file does not mask the rest. `make shellcheck` runs it inside the `jess/shellcheck` container, so contributors and CI need no local install, and `make test` is just `shellcheck`.", + "difference": "Discovery by content instead of by filename. A `*.sh` glob would lint 10 of 31 files here and silently skip the 21 that matter most. The container step is the second half: it makes the lint reproducible without adding a tool to `Brewfile` or a setup step to CI.", + "effort": "S", + "risk": "low", + "translation": "Add `test/test_shellcheck.sh` using `git ls-files` (not `find`, so the `mattpocock-skills` submodule and untracked runtime state are excluded automatically) filtered through `file(1)`, collecting failures rather than exiting on the first. Expect an initial backlog: start at `--severity=error` to get it green, then ratchet. Wire into `make test`, or into `make preflight` if #40 lands first.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "makefile-help", + "title": "Self-documenting Makefile with a help target", + "kind": "leaf", + "problem": "Finding out what this repo can do means reading the Makefile.", + "you": "The Makefile has 20 targets and the `default` target prints \"Cowardly refusing to run on . Use platform specific targets.\" -- it declines to act without saying which targets exist. Several are non-obvious from their names (`skip-mutable-settings`, `unskip-mutable-settings`, `restore-preferences`, `init-submodules`), and their explanations live in comments above the rule, visible only when reading the file.", + "them": "Every target carries a `## description` on the rule line, and `make help` greps the makefile and prints an aligned, colorized target/description table. The documentation cannot drift out of the file it documents because it is on the same line as the rule.", + "difference": "Where target documentation lives. Ours is in comments above rules \u2014 good for a reader of the file, invisible to someone at a prompt. Theirs is one line and one grep, and it stays correct because renaming a target moves its description with it.", + "effort": "S", + "risk": "low", + "translation": "Append `## ` to each target line and add the standard `help` grep/awk rule. Make `help` the `default` target instead of the current refusal message, which then still refuses to build anything but at least tells you what is available. The long explanatory comments stay where they are \u2014 the `##` text is a one-line summary, not a replacement for them.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "dependabot-actions", + "title": "Dependabot on GitHub Actions versions", + "kind": "leaf", + "problem": "CI action versions are pinned by hand and drift silently until something breaks or goes stale.", + "you": "`.github/workflows/ci.yml` pins `actions/checkout@v6` and `actions/setup-python@v6` by major tag, and `python3 -m pip install PyYAML==6.0.2` by exact version. Nothing watches any of them. No `dependabot.yml` exists.", + "them": "`.github/dependabot.yml` runs the `github-actions` ecosystem weekly with `reviewers: [jessfraz]`, so action bumps arrive as reviewable PRs rather than as a discovery during an unrelated change.", + "difference": "Whether CI dependency updates are pull or push. Small, but this is the supply chain that has write access to the workflow \u2014 a stale major tag is also where an unmaintained action sits unnoticed. Ten lines of YAML, no maintenance.", + "effort": "S", + "risk": "low", + "translation": "Add `.github/dependabot.yml` for the `github-actions` ecosystem on a weekly schedule. Skip the `reviewers` key \u2014 GitHub deprecated it in favour of `CODEOWNERS`. Worth considering whether the `pip` ecosystem is worth adding for the pinned PyYAML, though a single pinned dev dependency probably is not.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "codex-harness-support", + "title": "Codex CLI as an additional agent harness fed from home/.agents/rules", + "kind": "leaf", + "problem": "The rules in `home/.agents/rules/` reach Claude, Copilot, Gemini and Pi, but not Codex.", + "you": "`home/.agents` is the source of truth and each harness gets a shim: `~/.claude/rules` symlinks the directory, and `home/.copilot/instructions/*.instructions.md` are per-file symlinks mirroring `home/.agents/rules/*.md`. Both shims work because those harnesses read a *directory*. There is no machinery on `main` for a harness that reads a single concatenated file, and no Codex shim.", + "them": "`.codex/AGENTS.md` is a single long agent profile checked into the dotfiles alongside `.codex/notify.py`, treated as ordinary tracked config rather than as machine state.", + "difference": "Codex reads exactly one `AGENTS.md`, so it needs concatenation rather than symlinking -- a shape this repo does not yet have on `main`. That makes the cost dependent on unlanded work: open PR #32 (`pi-harness-support`) builds exactly that machinery for Pi in `scripts/build-pi-agents.sh`, and if it lands, Codex becomes a second output path for an existing script. Standalone against `main` today, it means writing the concatenator first and owning its staleness guard. Either way the value is zero unless Codex is actually in use here.", + "effort": "M", + "risk": "low", + "translation": "Blocked on, or much cheaper after, PR #32. Once `scripts/build-pi-agents.sh` exists, generalize it to emit to more than one destination and add `home/.codex/AGENTS.md` as a second target, with a `manifest` row and the same self-heal-locally / fail-in-CI guard. Against `main` as it stands, this is the whole concatenator plus the guard, for a harness with no demonstrated use. Note their `notify.py` is not part of this item -- turn-end notification is already covered by the adopted `agent-notify-hook` (#23).", + "pilot": "", + "spike_question": "", + "decision": "reject", + "rationale": "No demonstrated Codex use here, and the concatenation machinery it depends on is unlanded (PR #32 builds it for Pi); adding a fifth harness shim before the fourth has merged compounds unproven work. Reconsider if Codex enters the toolchain." + }, + { + "id": "nix-home-manager", + "title": "Nix flake exposing a home-manager module, additive to the existing deploy", + "kind": "foundation", + "problem": "Deployment is symlinks plus Homebrew plus asdf, none of which pin what actually gets installed.", + "you": "`manifest` + `deploy.sh` places symlinks; `Brewfile` names packages without pinning versions; `home/.tool-versions` pins five runtimes via asdf (`bun 1.3.14`, `deno 2.8.0`, `nodejs 25.8.1`, `python 3.14.4t`, `rust 1.92.0`). Reproducing a machine means running three mechanisms and accepting whatever Homebrew resolves that day. `deploy.sh` is deliberately symlink-only, a parked decision in `docs/superpowers/specs/2026-07-06-manifest-deploy-spike-design.md`.", + "them": "`flake.nix` exposes `homeManagerModules.default` across four systems (`x86_64-linux`, `aarch64-linux`, `x86_64-darwin`, `aarch64-darwin`), with `flake.lock` pinning inputs and a `nix.yml` workflow running `nix flake check --all-systems`. Crucially it sits **alongside** the Makefile symlink install rather than replacing it \u2014 the repo offers both, and neither is required for the other to work.", + "difference": "Real reproducibility, in the sense the current stack cannot provide: a lockfile that resolves to identical package versions on every machine, checked by CI across four platforms. The additive framing is what makes this worth listing at all \u2014 this repo previously deferred nix on the grounds that it would mean replacing Homebrew, asdf and `deploy.sh` together, and jessfraz demonstrates that it does not have to.", + "effort": "L", + "risk": "high", + "translation": "Would mean expressing package sets as nix derivations and maintaining them beside `Brewfile` rather than instead of it. The honest cost is not the flake, it is the second definition of every package and the ongoing obligation to keep two lists in agreement \u2014 precisely the drift problem `check-copilot-instructions` and `check-pi-agents` exist to police, but for the whole package set.", + "pilot": "Express only the five asdf-pinned runtimes as a flake devShell and see whether it reproduces on both the macOS laptop and the Linux VM without touching `Brewfile`, `manifest` or `deploy.sh`. That is a direct like-for-like comparison against the one part of the stack that already claims to pin versions. Fully reversible: delete two files.", + "spike_question": "Does a nix flake devShell expressing the five asdf-pinned runtimes (`bun 1.3.14`, `deno 2.8.0`, `nodejs 25.8.1`, `python 3.14.4t`, `rust 1.92.0`) actually reproduce on both the macOS laptop and the Linux VM without touching `Brewfile`, `manifest` or `deploy.sh`? Second half, and the one that decides it: is maintaining a nix definition in parallel with `Brewfile` tolerable, or does it recreate the same drift problem `check-copilot-instructions` and `check-skills` exist to police, but across the whole package set? A nameable reproducibility benefit is what separates this from the rejected `mise-vs-asdf` (ergonomics only) -- the spike has to confirm that benefit is real here, not just available.", + "decision": "spike", + "rationale": "" + } + ] +} \ No newline at end of file diff --git a/home/.agents/skills/find-inspiration/runs/2026-08-23-paulirish.json b/home/.agents/skills/find-inspiration/runs/2026-08-23-paulirish.json new file mode 100644 index 0000000..e6e031a --- /dev/null +++ b/home/.agents/skills/find-inspiration/runs/2026-08-23-paulirish.json @@ -0,0 +1,102 @@ +{ + "source_repo": "paulirish/dotfiles", + "run_date": "2026-08-23", + "items": [ + { + "id": "skill-script-tests", + "title": "Test the executable code that lives inside skills", + "kind": "leaf", + "problem": "A skill stops being unverifiable prose the moment it contains code, and this repo's one piece of skill code has no tests.", + "you": "`home/.agents/skills/find-inspiration/bin/triage-issues.py` is 255 lines that parse a run JSON, validate it, dedup GitHub issues on a `[find-inspiration:]` title marker, and append to `decisions.jsonl` \u2014 the durable memory of the whole triage workflow. `make check-skill-frontmatter` proves the frontmatter parses and `make check-skills` proves the symlinks resolve; neither executes a line of that script. A regression in `load_decisions()` or `validate()` surfaces as a mis-filed issue or a corrupted log, discovered by hand.", + "them": "`agents/` is a pnpm workspace where each script sits beside a `*.test.ts` run by `node --test`, with checked-in fixtures. `gather-reviews.test.ts` imports the real `renderThread`/`convertToMarkdown` and asserts on rendered output; `fixture.test.ts` replays a captured JSON payload through the converter and asserts on the result.", + "difference": "They treat skill scripts as code with a test suite; we treat them as content. The gap is not conceptual \u2014 `validate()`, `load_decisions()` and the marker dedup are pure functions over a JSON file, and `--dry-run` already makes the whole path network-free, so this is testable today with no refactor.", + "effort": "S", + "risk": "low", + "translation": "Add `test/test_skills.sh` following the existing bespoke-harness shape: run `triage-issues.py --summarize-decisions` against a fixture decisions log and `--run --dry-run` against a fixture run JSON, asserting the CREATE/LOG lines and the exit codes for malformed input (duplicate id, reject without rationale). Keep fixtures under `test/fixtures/`. Wire into `make test`.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "preflight-gate", + "title": "One preflight command, so CI and the developer share a definition of \"checked\"", + "kind": "leaf", + "problem": "The list of things to run before committing exists only inside the CI workflow, and it is already incomplete.", + "you": "`make test` runs the two suites. The guards live in separate targets -- `check-symlinks`, `check-skills`, `check-skill-frontmatter`, `check-copilot-instructions` -- and `ci.yml` re-lists a subset of them by hand. **`check-symlinks` is defined in the Makefile but never run by CI.** The duplication is also actively growing: open PR #32 (`pi-harness-support`) adds a fifth target, `check-pi-agents`, guarding the concatenation of `home/.agents/rules/` into `home/.pi/agent/AGENTS.md` -- and does not add it to `ci.yml` either, so it will land orphaned the same way.", + "them": "`pnpm preflight` is `pnpm typecheck && pnpm test` \u2014 one command named for the moment you run it, with the full list defined once in `package.json`.", + "difference": "Whether the definition of \"safe to commit\" has one home or two. Ours is duplicated between the Makefile and the workflow YAML, and the duplication has already drifted once and is about to drift again with PR #32. The self-healing check targets only self-heal for someone who remembers to invoke them.", + "effort": "S", + "risk": "low", + "translation": "Add `make preflight` running `test` plus every `check-*` target, then reduce `ci.yml` to `make preflight` (keeping the temp-`$HOME` deploy apply/audit step and the PyYAML install, which are environment setup rather than checks). The target list then has exactly one definition, and adding a guard automatically covers both paths -- which would pick up `check-pi-agents` for free when PR #32 lands, rather than requiring a second edit.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "skill-index", + "title": "Generated index of available skills", + "kind": "leaf", + "problem": "There is no listing of what skills exist; finding out means `ls` plus opening files.", + "you": "`home/.agents/skills/` holds 8 real directories and 18 symlinks into the `mattpocock-skills` submodule \u2014 26 entries, no index anywhere in the repo. `scripts/check-skill-frontmatter.py` already parses every frontmatter block and then discards the data.", + "them": "`agents/README.md` is a per-skill index kept beside the skills: name, one-line description lifted from frontmatter, and the exact install command for each.", + "difference": "Discoverability for both readers of this repo \u2014 the human and the agent that has to decide which skill applies. The parser already exists here; only the emit step is missing, and the vendored-versus-local distinction (which entries are editable, which are submodule symlinks) is information currently recorded only in `CLAUDE.md` prose.", + "effort": "S", + "risk": "low", + "translation": "Emit `home/.agents/skills/README.md` from the frontmatter `name`/`description` already parsed by `scripts/check-skill-frontmatter.py`, flagging symlinked entries as vendored. Add `make check-skill-index` that regenerates and fails when stale under `$CI`, matching the self-heal-locally / fail-in-CI pattern `check-copilot-instructions` already establishes. Land together with frontmatter-required-keys -- both are edits to scripts/check-skill-frontmatter.py, and splitting them means touching the same file twice.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "frontmatter-required-keys", + "title": "Validate frontmatter content, not just that it parses", + "kind": "leaf", + "problem": "A skill with missing or stale frontmatter passes every check and is silently never loaded.", + "you": "`scripts/check-skill-frontmatter.py` runs `yaml.compose()` on the block and stops there. A `SKILL.md` with no `name`, no `description`, or a `name` that disagrees with its directory is valid YAML and therefore passes.", + "them": "The `agents/README.md` contribution guide documents the required frontmatter contract (`name`, `description`) explicitly, and `npm-trusted-publishing` ships a `scripts/validate.mjs` that checks metadata shape rather than trusting it.", + "difference": "`description` is the field the harness matches on to decide whether to load a skill at all. An absent or drifted one produces a skill that exists, passes CI, and never fires \u2014 the quietest possible failure. Checking that YAML parses does not check that.", + "effort": "S", + "risk": "low", + "translation": "Extend `scripts/check-skill-frontmatter.py`: require non-empty `name` and `description`, and require `name` to equal the containing directory. Applies only to the real skill directories \u2014 the vendored `mattpocock-skills` symlinks are upstream's contract, not ours, so skip or warn rather than fail on those.", + "pilot": "", + "spike_question": "", + "decision": "adopt", + "rationale": "" + }, + { + "id": "scheduled-automation", + "title": "Checked-in cron for recurring local automation", + "kind": "leaf", + "problem": "The crontab is machine state that lives nowhere and is lost on rebuild.", + "you": "No scheduled local automation at all. Everything runs by hand or from CI on push. Nothing is currently missing because of it.", + "them": "`cron/crontab` is tracked with a documented field legend, alongside per-interval scripts (`every-hour.sh`, `weekly.sh`, `workday-end.sh`) and a `cron/.gitignore` for the machine-private ones.", + "difference": "Honest read: this is a capability with no demonstrated need here, and their own implementation carries a comment admitting the tracked file and the real crontab are kept in sync by hand \u2014 the same drift problem this repo already solved for symlinks with `deploy.sh audit`. Adopting it would mean importing the drift and building the audit.", + "effort": "M", + "risk": "low", + "translation": "Would mean a `cron/` directory, a tracked crontab, and an `audit` mode comparing it against `crontab -l` to avoid inheriting their drift.", + "pilot": "", + "spike_question": "", + "decision": "reject", + "rationale": "No demonstrated need for scheduled local automation here, and their implementation concedes in-comment that the tracked crontab drifts from the real one by hand -- adopting it would import the drift and then require building the audit to fix it." + }, + { + "id": "skill-distribution", + "title": "Publish skills as an installable package, not just a symlink source", + "kind": "foundation", + "problem": "Using one skill on another machine means cloning the entire personal dotfiles repo.", + "you": "Skills are consumed by symlink: `~/.claude/skills` and `home/.gemini/antigravity-cli/skills` point into `home/.agents/skills`, and `deploy.sh` is what puts them there. The unit of sharing is the whole repo \u2014 a fresh VM, a work machine or a collaborator gets everything or nothing. `home/.agents/rules/workflow.md` already asserts that reusable artifacts must be committed into the package they serve, parameterized and scrubbed; today the only package on offer is this entire tree.", + "them": "`agents/` is a self-contained versioned package \u2014 `@paulirish/agents`, with its own `package.json`, `tsconfig.json`, README, `.npmignore` and dependency set \u2014 installable one skill at a time via `npx skills add paulirish/dotfiles/agents --skill `, with no dotfiles involved.", + "difference": "The unit of distribution. This is foundation rather than leaf because `home/.agents` would stop being purely a deploy source and become a published artifact with a version, a changelog and a compatibility contract \u2014 which in turn constrains how freely rules and skills can be renamed, the very thing `check-copilot-instructions` and `check-pi-agents` exist to police.", + "effort": "L", + "risk": "med", + "translation": "Add package metadata under `home/.agents` (or a thin `agents/` wrapper) so the vercel `skills` CLI can resolve a single skill by name from this repo, without disturbing the symlink deploy path that `manifest` and `deploy.sh` already own. The two must coexist: local deploy stays the primary consumer.", + "pilot": "Pick the one skill with zero coupling to this repo \u2014 `prose-register` or `code-review-register`, both pure prose with no scripts \u2014 add the minimal metadata, and verify it installs into a scratch directory from a clean checkout. Explicitly not `find-inspiration`: its `triage-issues.py` hardcodes `REPO = \"nonrational/dotfiles\"` and would need parameterizing first. Reversible \u2014 the metadata is additive; delete it if the CLI cannot resolve a nested path.", + "spike_question": "Can the vercel `skills` CLI install a single skill from a nested path in this repo (`nonrational/dotfiles/home/.agents`), or does it require the package at a repo root? If nested paths do not resolve, the cost jumps from adding metadata to restructuring the tree that `manifest` and four harness symlinks depend on. Second half: what compatibility contract does publishing impose on renaming rules and skills, given that renames here have already broken the copilot and Pi mirrors twice?", + "decision": "spike", + "rationale": "" + } + ] +} \ No newline at end of file