Lessons captured from past work to inform future development. Updated when merging PRs.
-
Prefer symlinks over copies: Symlinked config files stay in sync with the repo automatically. Use
ln -sinstead ofcpfor anything that might change. This applies to skills directories, not just individual config files. -
Updating a script doesn't apply the change: If you modify an install script (e.g., changing
cptoln -s), the old state persists until you actually run the script. Don't assume the change is live just because you committed it. -
Timestamp backup filenames beat numbered suffixes:
file.bak.$(date +%Y%m%d-%H%M%S)is self-documenting and can't clobber prior backups. Numbered suffixes (.bak.1,.bak.2) require exists-checking and can race. -
settings.jsonis a bad symlink target when the app writes back: Claude Code writesfeedbackSurveyStateto~/.claude/settings.jsonon its own. If symlinked into the dotfiles repo, every timestamp update creates git churn. Usejqto merge in place, preserving an explicit allowlist of transient keys.
-
Skill vs slash command vs sub-agent vs hook: Skill = model-invoked ambient methodology (Claude picks when). Slash command = user-invoked discrete action (you type
/name). Sub-agent = fresh-context specialist that returns findings (read-only, doesn't mutate state). Hook = harness-level automation on Claude Code events. Rule of thumb: if the trigger is fuzzy or the action costs external money/time, it's a slash command, not a skill. -
Hooks fire on Claude Code events only, not external events:
Stop,PreToolUse,SessionStartetc. There is no "PR pushed" or "PR merged" hook. For external-event automation use GitHub Actions. Don't try to bolt it ontoPreToolUsematchers againstgh pr merge— brittle and misses the GitHub UI path. -
AGENTS.md at repo root, minimal CLAUDE.md imports it: Cross-tool convention (Cursor, Codex, Aider all read AGENTS.md). A
CLAUDE.mdcontaining just@AGENTS.mdis the documented minimal form. -
.claude/settings.local.jsonmust be gitignored: Claude Code writes per-project permission grants there. Sub-agents probing during review can accumulate dangerous entries likeBash(rm -rf *). Never commit this file. -
Claude Code auto-updates on launch: No need for explicit
claude updatein install scripts unlessDISABLE_AUTOUPDATERis set or auto-update is disabled in settings.
-
bash 3.2 (macOS default) needs
${arr[@]+"${arr[@]}"}for empty arrays underset -u: Plain"${arr[@]}"on an empty array throws "unbound variable". Matters especially intraphandlers that can fire before any appends happen. -
set -ein a sourcing parent propagates to sourced children:scripts/update.zshusesset -eand sourcesapps/*/install.zsh. Ajqcall that errors on null input kills the wholedotrun, not just the install step. Use defensive// {}guards and|| { ... }wrappers on risky operations. -
jq's
*merge is append-only: Removing keys from the source file doesn't remove them from the target. For "repo is source of truth" semantics, use$repo + (only-transient-keys-from-live)with an explicit transient allowlist rather than$repo * $live. -
Modern macOS (Sequoia 15+) ships
jqat/usr/bin/jq: Tests that try to force a grep fallback viaPATH=/usr/bin:/binsilently hit the real jq and pass vacuously. Use an empty shim directory as PATH to genuinely remove jq from a test environment.
-
Resolve PR comment threads via GraphQL: Use
gh api graphqlwith theresolveReviewThreadmutation to programmatically mark review threads as resolved. Requires the thread's node ID from thereviewThreadsquery. -
gh api graphql -f key=valuedefines GraphQL variables: Any-fpair alongside-f query=...becomes a variable in the mutation. Useful for bodies containing quotes/colons that are awkward to inline:gh api graphql -f threadId=PRRT_... -f body='...' -f query='mutation($threadId: ID!, $body: String!) { ... }'.
- Verify Copilot PR review claims before applying: In a sample of 3 comments on one PR, all 3 were confidently wrong: false claims about macOS
mktempsemantics, and missing context about what's in the Brewfile. Always run the failing case yourself before changing code to match a review suggestion.