taloncjones

dotfiles-diagnostics-and-tooling

Use when you need to MEASURE the state of a dotfiles machine or cloud container instead of eyeballing it -- interpreting identity-doctor output, running/reading bin/dotfiles-tests, inspecting plugin state (installed_plugins.json, known_marketplaces.json), diffing live settings.json against the template, or running this skill's cloud-doctor.sh / symlink-audit.sh scripts. Trigger phrases -- "is this environment healthy", "why did identity-doctor fail", "which test suite broke", "are the plugins actually installed", "did settings drift", "audit the symlinks". NOT for fixing what you find (dotfiles-run-and-operate), rebuilding from scratch (dotfiles-build-and-env), debugging a specific symptom (dotfiles-debugging-playbook), or single-fact verification recipes (dotfiles-verification-toolkit).

taloncjones 0 Updated 4w ago

Resources

1
GitHub

Install

npx skillscat add taloncjones/dotfiles/dotfiles-diagnostics-and-tooling

Install via the SkillsCat registry.

SKILL.md

Dotfiles diagnostics and tooling

Measure instead of eyeball. This repo ships read-only doctors, an aggregate
test runner, and on-disk state files that answer "is this machine healthy?"
deterministically. This skill is the interpretation guide for each tool plus
two executable doctors in scripts/ for the cases the built-ins do not cover
(cloud sessions, full symlink audits).

[WARNING] Every tool here is read-only EXCEPT bin/dotfiles-repair (mutates
symlinks, may pull main). Never trust claude plugins install exit codes --
the on-disk ground truth is installed_plugins.json (see Plugin-state
inspection below; incident history in dotfiles-failure-archaeology).

When NOT to use this skill

You want to... Use instead
Fix what a doctor found (relink, reinstall plugins, update) dotfiles-run-and-operate
Rebuild a machine/container from nothing dotfiles-build-and-env
Triage a symptom ("hook not firing", "wrong identity") dotfiles-debugging-playbook
One-off proof recipe (a single symlink chain, one hook firing) dotfiles-verification-toolkit
Know what counts as evidence / add a test dotfiles-validation-and-qa
Understand why the exit-code-lies design exists dotfiles-failure-archaeology

Tool inventory

Tool Scope Mutates? Exit semantics
bin/identity-doctor (alias git identity) git/SSH/Claude identity chain No 1 if any [X]
bin/dotfiles-tests all nine test suites No 1 if any suite fails
bin/dotfiles-repair symlinks, settings size, GSD check YES non-zero on hard error (set -e)
scripts/cloud-doctor.sh (this skill) cloud/ephemeral session health No 1 if any [X]
scripts/symlink-audit.sh (this skill) expected symlink map + orphan scan No 1 if any non-OK entry, orphan link or scan error
claude plugin list / state JSONs plugin install truth No n/a

identity-doctor interpretation

Run: identity-doctor (or git identity from any repo). Source:
bin/identity-doctor. Sections in output order:

Section Checks [X] means [WARNING] means
dotfiles links ~/.gitconfig, ~/.gitconfig-personal, ~/.ssh/config are resolving symlinks dangling or missing link -- re-run link.sh path exists but is a real file (unmanaged; a prior manual copy shadows the repo)
git identity ~/.gitconfig-work is machine-local; user.useConfigOnly=true work config is a SYMLINK into the repo (leaks employer values), or useConfigOnly off (silent guessed identities) work config missing/empty -- commits under ~/Git/work refused until identity-setup runs (expected on personal-only machines)
ssh keys 1Password agent socket, agent.toml, both pubkeys, ssh -G pinning personal pubkey missing, or ssh -G github.com/Git-work would offer the WRONG key agent socket absent (1Password not running), work pubkey absent ([INFO] -- expected on personal-only machines)
current repo resolved user.email matches the ~/Git/{personal,work} path convention; remote routes via the right host alias identity mismatch or NO resolved email inside a convention dir repo outside convention dirs but has an identity (repo-local override?), or work remote not rewritten to Git-work
claude accounts which config dir a claude launch here uses; both dirs exist -- CLAUDE_CONFIG_DIR exported (overrides directory routing for EVERY launch)
gh gh auth status -- installed but unauthenticated

Key discriminations: [X] = broken invariant, fails the run. [WARNING] =
degraded but by-design-possible. [INFO] = expected on some machine classes.
"repo outside ~/Git/{personal,work} refuses to commit" is [INFO] -- that loud
failure IS the design (user.useConfigOnly, see dotfiles-architecture-contract).

dotfiles-tests: the suites

Run: bin/dotfiles-tests (what CI .github/workflows/tests.yml runs).
List without running: bin/dotfiles-tests --list. Each suite prints its own
PASS/FAIL lines; the runner adds per-suite verdicts and a summary.

Suite What a failure means
install/install.test.sh Brewfile regressions (e.g. renamed docker cask). Static rg asserts -- needs ripgrep on PATH.
claude/hooks/claude-hooks.test.sh A guard hook stopped blocking (or started over-blocking) its payload class -- hooks are RUN with real payloads -- OR live ~/.claude*/settings.json drifted from the template (missing SessionStart hook = "hand-merge required"; see Settings drift below).
codex/hooks/codex-hooks.test.sh Codex twin of the guard hooks regressed.
codex/skills/codex-skills.test.sh Repo-managed Codex bridge skills malformed.
git/hooks/commit-msg.test.sh Commit-message guard regressed (attribution phrases, AI co-author trailers, emoji -- the hook does NOT check scope/length format; that is review convention).
git/hooks/post-checkout.test.sh Static-shape assertions over the worktree-hydration hook source failed. [WARNING] static-only -- passing does NOT prove runtime behavior (known gap).
git/hooks/public-safety.test.sh Public-repo safety tripped: tracked todo.md, a hardcoded local user home path, or a secret-shaped string ANYWHERE in the working tree (rg --hidden, so untracked files under .claude/ count). Find the culprit with the failing suite's own rg pattern minus the !.
claude/skills/lib/test_work_state.sh Shared work-state library for skills broke.
claude/skills/todos/scripts/tests/todos_test.sh todos skill scripts broke (deterministic via TODOS_TODAY/TODOS_REGISTRY).

[X] MISSING <path> from the runner means a suite file was moved/deleted --
update the SUITES list in bin/dotfiles-tests (audit:
grep -rl 'passed, .* failed').

Baseline (2026-07-02): all 9 suites pass. A public-safety failure is NEVER
an acceptable baseline -- it is a stop-the-line defect on a public repo; find
the offending content with the failing suite's own rg pattern and fix it
before any push.

dotfiles-repair as a diagnostic

bin/dotfiles-repair mutates, but its five steps report state you can read
even when you let it run (each step is idempotent):

Step Reports Diagnostic value
1/5 pull "not on main" / "local changes; skipping pull" tells you the checkout is dirty or on a branch before anything else runs
2/5 re-link runs platform link.sh [claude-links] Backed up pre-existing ... lines reveal real dirs that were shadowing symlinks
3/5 settings settings.json size vs template (flags <50% of template size) tiny file = something rewrote it through a stale symlink, dropping plugin/hook config
4/5 GSD compromised get-shit-done-cc reappearance (command, npm -g, skill dirs) supply-chain tripwire; if flagged, run gsd-uninstall (see dotfiles-external-positioning)
5/5 verify ~/.gitconfig and one codex hook symlink resolve quick post-fix smoke

Diagnose-only alternative: this skill's symlink-audit.sh covers step 2/5's
domain read-only and per-entry.

Plugin-state inspection

Ground truth is on disk under $CLAUDE_CONFIG_DIR/plugins/ (default
~/.claude/plugins/). NEVER conclude from an install command's exit 0 --
claude plugins install can no-op with exit 0 when a marketplace manifest is
not warm yet (commits 722c653, 7cb28b7; full story in
dotfiles-failure-archaeology).

File / path Truth it holds
plugins/installed_plugins.json THE install record: plugin id -> version, installPath, gitCommitSha. Absent id = not installed, whatever the CLI said.
plugins/known_marketplaces.json registered marketplaces: source URL, installLocation, lastUpdated
plugins/marketplaces/<name>/.claude-plugin/marketplace.json the resolver's manifest -- a plugin must be listed here before install can succeed
plugins/cache/<marketplace>/<plugin>/<version>/ the installed payload itself
# Any installed plugin, straight from ground truth (Superpowers retired 2026-09,
# ECC retired 2026-09; no plugin is required by this repo any more):
grep -E '"<plugin-id>"' ~/.claude/plugins/installed_plugins.json

# CLI views (convenient, but derived):
claude plugin list            # installed + enabled status (also: list --json)
claude plugin marketplace list
claude plugin marketplace update <name>   # force a manifest refresh

# Is the plugin even resolvable yet? (the wait condition a manual install polls)
grep '"name"' ~/.claude/plugins/marketplaces/<marketplace>/.claude-plugin/marketplace.json | head

CLI verbs (from claude plugin --help, 2026-07-02): details, disable, enable, eval, init|new, install|i, list, marketplace {add,list,remove,update}, prune|autoremove, tag, uninstall|remove, update, validate. Neither runtime installs a plugin during
update/install.sh/bootstrap-cloud.sh any more; the retired Superpowers
install path verified against installed_plugins.json the same way this
table still documents.

Settings drift inspection

settings.json is machine-local per config dir, seeded ONCE from
claude/settings.json.tmpl (seed_machine_local_file in
install/common/claude-links.sh). Template changes do NOT propagate to
existing machines -- manual merge required (known open weak point; cloud
reconciles automatically via reconcile_claude_settings in
bootstrap-cloud.sh, machines do not).

This is the CANONICAL drift script for the library (both config dirs, nested
plugin-key logic); dotfiles-verification-toolkit Recipe 6 defers to it.

# Key-by-key drift: which top-level keys differ between live and template?
python3 - <<'PY'
import json, os
tmpl = json.load(open(os.path.expanduser("~/dotfiles/claude/settings.json.tmpl")))
for cdir in ("~/.claude", "~/.claude-work"):
    p = os.path.expanduser(cdir + "/settings.json")
    if not os.path.isfile(p):
        print(f"[INFO] {p}: absent"); continue
    live = json.load(open(p))
    for k in sorted(set(tmpl) | set(live)):
        if k in ("enabledPlugins", "extraKnownMarketplaces"):
            missing = set(tmpl.get(k, {})) - set(live.get(k, {}))
            if missing: print(f"[WARNING] {p}: {k} missing template entries: {sorted(missing)}")
        elif tmpl.get(k) != live.get(k):
            tag = "live-only" if k not in tmpl else ("missing" if k not in live else "differs")
            print(f"[WARNING] {p}: {k} {tag}")
PY

Interpretation: enabledPlugins/extraKnownMarketplaces are OWNED by plugin
installers -- live extras there are normal; only template entries MISSING from
live are drift. Everything else (hooks, statusLine, permissions, env)
differing means a hand-merge was missed. claude/hooks/claude-hooks.test.sh
enforces the highest-stakes slice of this (SessionStart hooks present) but not
full-key equality. Adjust the ~/dotfiles path to your checkout.

Skill scripts

Both are bash, set -u, read-only, self-locating (derive the repo root from
their own path; symlink-audit.sh accepts a DOTFILES= override).

scripts/cloud-doctor.sh

End-to-end health of a cloud/ephemeral session: ~/.claude assets link into a
real dotfiles checkout, settings.json is machine-local with SessionStart
hooks (account_guard.py), both plugins in installed_plugins.json, git
author is not the platform default Claude <noreply@anthropic.com>, and
python3/node are on PATH. Exit 1 on any [X].

.claude/skills/dotfiles-diagnostics-and-tooling/scripts/cloud-doctor.sh

Captured in this cloud container, 2026-07-02 (abridged):

--- claude assets (/root/.claude) ---
[OK]      CLAUDE.md links into dotfiles checkout at /home/user/dotfiles
[OK]      operating-principles.md -> /home/user/dotfiles/claude/operating-principles.md
...
--- settings.json ---
[OK]      SessionStart hooks registered: account_guard.py
--- git identity ---
[OK]      git author: Talon Jones <taloncjones@gmail.com>
--- runtimes ---
[OK]      python3 on PATH (/usr/local/bin/python3)
[OK]      node on PATH (/opt/node22/bin/node)

cloud-doctor: all checks passed.

[X] git author is still the platform default = reattribute_git_identity
never ran -- commits would be misattributed to the platform seed identity
(incident: commit 2304015).

scripts/symlink-audit.sh

Two passes, both read-only.

Pass 1 walks the expected symlink map: a transcription of what the platform
install/<platform>/link.sh writes (install/common/link.sh,
link_claude_config_dir, link_codex_surfaces, and the macOS VSCode/Zed links
on Darwin). Per entry: OK | WRONG-TARGET | DANGLING | NOT-A-LINK | MISSING;
machine-local files (settings.json in each Claude dir, ~/.gitconfig-work,
~/.ssh/config_cloudflared, ~/.config/1Password/ssh/agent.toml) are checked
inversely (IS-A-LINK is the failure).

Pass 2 scans fixed dirs for symlinks outside that map whose literal target lies
inside a dotfiles checkout ($DOTFILES plus each --root PATH):

State Meaning Fails the run?
ORPHAN-DANGLING target is gone from a checkout (any scanned dir) Yes
ORPHAN-LIVE target exists; link sits in an installer-owned dir Yes
UNOWNED-LIVE target exists; link sits in a shared dir ([INFO], not judged) No
INCOMPLETE a scan or a proof of absence could not finish (permissions, a crash) Yes

Scanned dirs (depth): ~ 1, ~/bin 1, ~/.ssh 1, ~/.local/bin 1,
~/.config 3, ~/.codex 2, ~/.claude 2, ~/.claude-work 2, and on macOS
~/Library/Application Support/Code/User 1. Installer-owned dirs, where an
unmapped live link is stale by the architecture contract: ~/.claude,
~/.claude-work, ~/.codex/hooks, ~/.codex/skills, ~/.codex/rules.
--cloud restricts both passes to ~/.claude (what bootstrap-cloud.sh
creates). Links to other checkouts count only when named with --root,
including a deleted checkout; audit from the installed checkout (or set
DOTFILES=), otherwise pass 1 reports WRONG-TARGET for every entry.

.claude/skills/dotfiles-diagnostics-and-tooling/scripts/symlink-audit.sh                  # full machine
.claude/skills/dotfiles-diagnostics-and-tooling/scripts/symlink-audit.sh --cloud          # container
.claude/skills/dotfiles-diagnostics-and-tooling/scripts/symlink-audit.sh --root ~/dotfiles-old
.claude/skills/dotfiles-diagnostics-and-tooling/scripts/symlink-audit.sh --list-expected  # the map as TSV

Output format (cloud mode on a healthy container; format example, not a
captured run):

symlink-audit: dotfiles root = /home/user/dotfiles  (mode: cloud)

--- claude config dir: /root/.claude ---
[OK] OK            /root/.claude/CLAUDE.md
...
[OK] OK            /root/.claude/settings.json (machine-local file)

--- orphan scan ---
[INFO] checkout root: /home/user/dotfiles
[INFO] 0 symlink(s) outside dotfiles checkouts ignored

symlink-audit: all 9 entries OK, no orphan links.

A failing run ends with
symlink-audit: <B> of <N> entries NOT OK, <O> orphan link(s), <I> scan error(s).
and exits 1. Full mode on a container reports the zsh/git/ssh/codex and
~/.claude-work entries MISSING/NOT-A-LINK, which is expected there. On a real
machine, WRONG-TARGET/DANGLING usually means a deleted or moved sibling
checkout (the rot dotfiles-repair step 2/5 fixes), and an ORPHAN-* line
names a leftover from a retired tool or file. The audit never deletes; remove
a confirmed orphan by hand.

symlink-audit.test.sh runs the real installer from a disposable copy of the
checkout into a scratch HOME and fails when the map drifts from link.sh, so
update the map in the same change as any installer link.

Provenance and maintenance

All facts verified against the working tree and live container on 2026-07-02.
Volatile facts and how to re-verify:

Fact (as of 2026-07-02) Re-verify
Nine test suites in the runner bin/dotfiles-tests --list
CI runs the runner grep dotfiles-tests .github/workflows/tests.yml
identity-doctor sections/labels grep -n 'section ' bin/identity-doctor
repair's five steps grep -n 'step "' bin/dotfiles-repair
plugin CLI verbs claude plugin --help; claude plugin marketplace --help
plugin state paths ls ~/.claude/plugins/
no plugin is required (ECC and Superpowers both retired 2026-09) grep -q ensure_plugin bootstrap-cloud.sh; echo $? (expect 1)
template-vs-live drift checker coverage sed -n '80,110p' claude/hooks/claude-hooks.test.sh
symlink map (script transcription) sh .claude/skills/dotfiles-diagnostics-and-tooling/scripts/symlink-audit.test.sh (parity cases run the real installer into a scratch HOME)
baseline (all suites pass; any public-safety failure is stop-the-line) bin/dotfiles-tests
platform default author email noreply@anthropic.com grep PLATFORM_DEFAULT_EMAIL bootstrap-cloud.sh

Update triggers: adding/removing a test suite, renaming a doctor, changing the
symlink map, template key changes, or a new plugin becoming dotfiles-required.