2026-07-17 Update-Changelogs Feature via Superpowers SDD

What I set out to do

Started from a failed update -c (the claude-settings flake check rejected the @internal skipWorkflowUsageWarning key after a schema bump). Fixed that, then the session turned into a research + build: is there tooling that resolves an arbitrary package to its changelog between two versions, and if not, build a small one for the dotfiles update script.

What I actually did

  • Unblocked update -c: added skipWorkflowUsageWarning to schemaLagExceptions in nix/tests/claude-settings/default.nix (the key is @internal, never in schemastore). Committed, re-ran update -c clean (profile 475→476). Documented the schema-lag gotcha on the existing consent-gate memory.
  • Researched the changelog-resolution landscape (two verified subagent sweeps): Renovate’s changelog/source.ts has the best reusable heuristics but isn’t a standalone lib; ecosyste.ms is the most reusable hosted engine; Libraries.io/Repology/lastversion each cover a slice. Concluded a bespoke thin resolver is justified because the dotfiles ecosystems (Nix src/meta.changelog, lazy-lock SHAs, PyPI project_urls) already encode the repo.
  • Built update-changelogs end-to-end via the superpowers workflow: brainstorm → ADR 0011 → plan → subagent-driven-development (5 tasks, per-task spec+quality reviews, opus whole-branch review, fix waves). New standalone bash binary (nix/nvim modes) enriches the end-of-run summary with condensed GitHub release-note bullets + a changelog link; hermetic pytest flake check (gh/nix stubbed on PATH). Merged to main (402f56e).
  • Fixed a stale nvim ref: nvim/lua/plugins/git.lua pinned echasnovski/mini.nvim (upstream renamed the org to nvim-mini); retargeted it.

What was striking

  • The live deploy-verify earned its keep. Unit tests passed on an idealized fixture, and even the opus whole-branch review (working from the diff) missed it — but running the real thing after hm switch showed mini.nvim resolving to the stale echasnovski owner. The real git.lua has that stale ref as its first owner/repo spec, which fooled the “first-spec-in-file” heuristic. Fix: resolve the owner by majority vote across all specs (nvim-mini ×2 beats echasnovski ×1). Lesson: for heuristics over real config, a diff-based review and a synthetic fixture are both blind in the same way; only exercising the live data exposed it.
  • A .gitignore unanchored *logs pattern silently swallowed the new update-changelogs script + test dir (basenames end in “logs”), so Nix couldn’t see them. Also superpowers/ blocks docs/superpowers/, so the plan/spec had to live in docs/decisions/ as an ADR. Captured as a durable memory.
  • The codebase already had the exact heuristic in get_compose_input_diffs() (resolve owner/repo/rev from a lock file → gh api compare). The feature is really a generalization of an established local pattern, not an invention.

Follow-up: render polish (same day)

After the first live update -c, the enriched summary worked but had rough edges. Fixed on branch feat/changelog-render-polish, merged to main (1dc3e20), 31 tests:

  • Prefer markdown list items in release bodies. This both drops scaffolding (headings, HTML comments, block quotes, horizontal rules) AND skips the boilerplate preamble in one move — e.g. ripgrep now shows its actual change list instead of the sponsorship/description intro; lazygit/mermaid lose their ##/<!-- --> noise. Prose fallback (no list) strips the same scaffolding.
  • Generalized GitHub URL parsing (parse_github_ref): a meta.changelog pointing at a CHANGELOG filegithub.com/O/R/blob/TAG/… or raw.githubusercontent.com/O/R/refs/tags/TAG/… — now yields owner/repo/tag, so opentofu, codex (non-guessable rust-v* tag), and cloudflared fetch real release notes instead of just linking.
  • Checksum + code-fence filtering: cloudflared-style release bodies (fenced SHA256 blocks) collapse to link-only instead of emitting junk bullets.
  • Colorized output (cyan bullets, blue links, bold name / cyan arrow in the header), TTY-gated and honoring NO_COLOR / CLICOLOR_FORCE so pipes/logs/tests stay plain.
  • zsh stays homepage-link-only: it’s a SourceForge project (no GitHub repo), so a GitHub-centric resolver can’t do better without per-package hardcoding, which I deliberately avoided.