Skip to content

Add /harness skill (control surface for ~/.claude/)#1

Merged
datashaman merged 13 commits into
mainfrom
add-bootstrap-harness-skill
May 1, 2026
Merged

Add /harness skill (control surface for ~/.claude/)#1
datashaman merged 13 commits into
mainfrom
add-bootstrap-harness-skill

Conversation

@datashaman
Copy link
Copy Markdown
Owner

@datashaman datashaman commented May 1, 2026

Summary

Adds a /harness skill that turns ~/.claude/ (or <project>/.claude/) into a proper harness — feedforward guides Claude reads before it acts, deterministic sensors that catch drift after, and an optional drift-detection loop that PRs deltas against the latest releases each month.

A control surface, not a one-shot bootstrap. Eight sub-actions, all idempotent:

Sub-action What it does
install Operating-contract CLAUDE.md, four guardrail hooks, /verify and /plan slash commands, auto-memory seeds, settings.json patch (env + hooks only — never touches permissions / marketplaces / statusLine). Stack signals auto-filled from manifest detection.
uninstall Symmetric reversal; sha256 content-match keeps user-modified files; --all for full sweep; --keep-* escape hatches
update Refresh installed files vs current templates without clobbering customisations. --merge writes new template to <file>.new for diffable side-by-side review
doctor End-to-end diagnostic: sha256 + python3 capability, write perms, settings.json validity, all 4 hooks executable + wired (no foreign paths), block-force-push smoke-test, memory dir populated, CLAUDE.md not still placeholder
adopt Retrofit into an existing project — detects stack, scaffolds a starter scripts/harness-check.sh pass/fail gate, prints next-step install command. Refuses $HOME
snapshot Sanitised mirror of ~/.claude/ → a private git repo (caches scrubbed, secret-pattern scanned with --exclude-dir=.git, idempotent)
status Read-only — reports installed / modified / missing per surface, hook-wiring, env var, snapshot-repo state
audit Prepares a monthly remote routine (via /schedule) that researches the last ~30 days of Anthropic releases + canonical Claude Code voices and PRs audits/YYYY-MM-DD-setup-audit.md against the snapshot repo

Scope auto-detection: derived from $SKILL_DIR. Override with --scope=user|project or --target=PATH.

The four hooks:

  • block-force-push.sh (PreToolUse:Bash) — segment-aware matcher. Blocks force-push to main/master, hard reset to remote, rm -rf ~, --no-verify, world-writable chmod, branch -D on protected names. Allows --force-with-lease and worktree feature-branch deletion. Doesn't false-trigger on echoed strings.
  • format-on-edit.sh (PostToolUse:Write|Edit) — runs Pint / bun run format / npm run format / ruff / gofmt / cargo fmt if config exists. Silent on success.
  • post-compact-reinject.sh (PostCompact) — re-cats ./CLAUDE.md, ./AGENTS.md, ~/.claude/CLAUDE.md after autocompact, so the operating contract survives.
  • verify-before-stop.sh (Stop) — refuses Stop only if the working tree is dirty AND ./scripts/harness-check.sh fails. Clean-tree short-circuit prevents stranding mid-investigation. CLAUDE_SKIP_VERIFY=1 to override.

CI

GitHub Actions workflow (.github/workflows/skill-harness.yml):

  • shellcheck across every harness .sh
  • install/uninstall round-trip on ubuntu-latest + macos-latest
  • exercises adopt scaffolding + $HOME refusal + customisation preservation

Sources

The vocabulary follows OpenAI's Harness engineering and Martin Fowler's writeup. Day-to-day patterns are convergent picks from:

Sibling to datashaman/harness-template (the project-scope counterpart). /harness adopt is the bridge — drops the spine of harness-template's pattern into any project.

A note on the name

Originally drafted as bootstrap-harness. Renamed to harness because:

  1. Bootstrap implies one-shot setup, but install is just one of eight sub-actions.
  2. Harness is the noun the discipline already uses (per the OpenAI/Fowler articles). /harness <action> reads like the corresponding sub-CLI in any other tool.

🤖 Generated with Claude Code

datashaman and others added 3 commits May 1, 2026 09:19
Packages the harness-engineering setup as a reusable, idempotent skill:
operating-contract CLAUDE.md template, 4 guardrail hooks (block-force-push,
format-on-edit, post-compact-reinject, verify-before-stop), /verify + /plan
slash commands, auto-memory seeds (MEMORY.md index + concise / plan-first /
verification-gate feedback + user_role template), and helper scripts for
snapshotting ~/.claude/ to a private git repo plus a prompt template for a
monthly remote-audit routine.

The installer (scripts/install.sh) supports --dry-run / --force /
--skip-memory / --skip-settings and never clobbers existing files unless
asked. settings.json is patched additively (env + 4 hook entries only),
preserving permissions/marketplaces/statusLine/etc.

Smoke-tested in an isolated $HOME: dry-run writes nothing, real run installs
12 files + patches settings, second run is fully idempotent (skip-exists
everywhere, "settings.json already current"), and the installed
block-force-push.sh correctly blocks 'git push --force origin main' while
allowing --force-with-lease and echoed strings.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Symmetric reversal of install.sh, conservative by default:
- Removes hooks and slash commands only if their sha256 still matches the
  installed template; user-modified files are kept and reported.
- Strips the 4 hook entries from settings.json; drops empty hook arrays.
  Leaves permissions, marketplaces, statusLine, etc. untouched.
- Keeps CLAUDE.md, memory entries, and CLAUDE_CODE_AUTO_COMPACT_WINDOW by
  default — those tend to be customised. Opt in with --remove-claude-md,
  --remove-memory, --remove-env, or --all.
- Flags: --dry-run, --force (skip content-match), --all.

Smoke-tested:
- Default uninstall after fresh install removes 6 files + cleans settings,
  keeps CLAUDE.md/memory/env. Empty parent dirs rmdir'd.
- Modified hook is reported "keep (modified)" without --force; removed with.
- --all on a fresh install reduces 13 files → empty settings.json + 0 files.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…ources

Restructure as a control surface with five sub-actions (install / uninstall /
snapshot / status / audit) instead of a one-shot bootstrap. Rationale: the
skill's job has grown beyond first-time setup — uninstall, snapshotting, and
the monthly audit loop are equal peers, and "bootstrap" undersold what it does.

Changes:
- skills/bootstrap-harness/ → skills/harness/
- SKILL.md frontmatter: name: harness; description rewritten around sub-actions
- README.md: rewritten human-facing overview with prominent sources section
  (OpenAI harness-engineering article, Fowler writeup, Cherny / Willison /
  Vincent / Huntley / Husain / Yegge), plus a sibling-link to
  datashaman/harness-template (the project-scope counterpart)
- New scripts/status.sh: read-only reporter — installed / modified / missing
  per surface, plus settings.json hook-wiring + env var, plus snapshot-repo
  ahead/behind if SNAPSHOT_REPO is set
- install.sh, uninstall.sh: self-reference text updated; stale audit-routine.json
  reference fixed to audit-prompt.md
- Top-level README.md: skill heading renamed and rewritten to advertise
  sub-actions and credit sources

Smoke-tested round trip: install → status (everything 'installed'/'wired') →
modify a hook → status (correctly reports 'modified') → uninstall (default
keeps memory/CLAUDE.md/env) → uninstall --all (sweeps everything; settings.json
back to {}).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@datashaman datashaman changed the title Add /bootstrap-harness skill Add /harness skill (control surface for ~/.claude/) May 1, 2026
@datashaman datashaman requested a review from Copilot May 1, 2026 07:38
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Adds a new /harness skill that packages a reusable, user-scope (~/.claude/) Claude Code “harness” setup (operating contract, hooks, slash commands, memory seeds) with install/uninstall/status/snapshot utilities and an audit prompt.

Changes:

  • Introduces the harness skill definition + documentation, including sub-action dispatch (install, uninstall, status, snapshot, audit).
  • Adds idempotent installer/uninstaller plus status + snapshot scripts to manage ~/.claude/ surfaces and patch settings.json.
  • Adds the shipped templates: CLAUDE.md operating contract, guardrail hooks, slash commands, and memory seeds; plus an audit prompt template.

Reviewed changes

Copilot reviewed 20 out of 20 changed files in this pull request and generated 11 comments.

Show a summary per file
File Description
skills/harness/scripts/uninstall.sh Conservative uninstaller that removes only unmodified installed files and cleans related settings.json entries
skills/harness/scripts/status.sh Read-only status report for installed/modified/missing surfaces and settings wiring
skills/harness/scripts/snapshot.sh Mirrors ~/.claude/ into a sanitized git snapshot repo, secret-scans, commits, and pushes
skills/harness/scripts/install.sh Idempotent installer that lays down templates and patches settings.json to wire hooks/env
skills/harness/scripts/audit-prompt.md Prompt template for a monthly remote audit routine
skills/harness/assets/memory/user_role.md.tmpl User-role memory template seed
skills/harness/assets/memory/feedback_verification.md.tmpl Verification-gate memory template seed
skills/harness/assets/memory/feedback_plan_first.md.tmpl Plan-first memory template seed
skills/harness/assets/memory/feedback_concise.md.tmpl Concise-output memory template seed
skills/harness/assets/memory/MEMORY.md.tmpl Memory index template seed
skills/harness/assets/hooks/verify-before-stop.sh Stop hook to block “done” when verification fails
skills/harness/assets/hooks/post-compact-reinject.sh Post-compact hook to re-inject key context files
skills/harness/assets/hooks/format-on-edit.sh PostToolUse hook to run available formatters after edits
skills/harness/assets/hooks/block-force-push.sh PreToolUse hook to block destructive shell commands (e.g., force push / rm -rf)
skills/harness/assets/commands/verify.md /verify slash command definition
skills/harness/assets/commands/plan.md /plan slash command definition
skills/harness/assets/CLAUDE.md.tmpl User-scope operating contract template
skills/harness/SKILL.md Skill manifest + agent-facing operating instructions and sub-action dispatch
skills/harness/README.md Human-facing README for the harness skill
README.md Repository README updated to list and describe /harness

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread skills/harness/README.md Outdated
Comment thread skills/harness/scripts/install.sh Outdated
Comment thread skills/harness/scripts/install.sh Outdated
Comment thread skills/harness/scripts/status.sh Outdated
Comment thread skills/harness/assets/hooks/block-force-push.sh Outdated
Comment thread skills/harness/scripts/install.sh Outdated
Comment thread skills/harness/scripts/uninstall.sh Outdated
Comment thread skills/harness/scripts/uninstall.sh Outdated
Comment thread skills/harness/scripts/uninstall.sh Outdated
Comment thread skills/harness/scripts/status.sh Outdated
datashaman and others added 4 commits May 1, 2026 09:50
Substantive fixes:

- **Portable SHA-256.** Replace bare `shasum` with a fallback chain (sha256sum
  preferred on Linux → shasum on macOS → python3 hashlib). uninstall.sh and
  status.sh both have the same helper. status.sh now reports `cannot hash`
  explicitly when no tool is available, instead of silently treating empty
  hashes as "matching".

- **Drop `HOME_CLAUDE` override.** Was half-implemented — the env override
  affected file paths but settings.json had hard-coded `~/.claude/...`, so
  hooks installed under an overridden home would never actually run. Use
  $HOME/.claude consistently across install/uninstall/status. Tests use
  HOME=/tmp/... instead. settings.json still stores the literal "~/.claude/..."
  form (Claude Code expands ~ at hook-execution time, which is what we want
  for portability across machines/users).

- **`do_or_dry` no longer uses `eval`.** Take argv directly via "$@" and use
  printf %q for shell-quoted dry-run preview. Smoke-tested with a path
  containing a single quote (`foo'bar`) — prints correctly escaped, doesn't
  break.

- **`changed` flag now tracks env-var insertion.** Was a real bug: if hooks
  were already wired but the env var was missing, install rewrote the file
  but reported "already current". Now any env-var change marks `changed`.

Doc/comment fixes:

- install.sh usage no longer says "interactive" (script is non-interactive).
- block-force-push.sh comment corrected: handles `&&`, not bare `&`.
- uninstall.sh `--help` line range fixed: was 2-17, leaked `set -euo pipefail`;
  now 2-16 (last comment line).
- README requirements: list sha256sum/shasum/python3 explicitly under the
  hashing dependency.

End-to-end smoke test (HOME override mode) verifies:
- install (fresh) → `settings.json updated`; second install → `already current`
- env-var bug: strip env after install, re-run → correctly says `updated`
- modified hook reports `modified` in status, `keep (modified)` in uninstall
- settings.json hook commands stay as literal `~/.claude/...` strings
- single-quote in $HOME path doesn't break dry-run preview
- `--all` sweep: 24 actions, settings.json reset to `{}`, all files gone

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Address two pieces of feedback:

1. Scope is no longer hard-coded to user — derive it from $SKILL_DIR.
   The skill installs at the scope it itself lives in:
   - $SKILL_DIR is under <X>/.claude/skills/... → target is <X>/.claude/
   - If <X> is $HOME → user scope (CLAUDE.md at ~/.claude/, settings hooks
     use ~/.claude/hooks/..., env var set, memory seeded)
   - Otherwise → project scope (CLAUDE.md at <project>/, settings hooks use
     project-relative .claude/hooks/..., no env var, no memory seeding —
     memory is per-user by design)
   - Running from a checkout with no .claude/ ancestor errors with a clear
     prompt for --scope=user|project or --target=PATH
   - Override always available via --scope or --target

2. Escape hatches so users can configure which changes to accept:
   install.sh: --skip-{claude-md,hooks,commands,memory,settings}, plus a
   positive --include=LIST shorthand (e.g. --include=hooks,commands).
   uninstall.sh: --keep-hooks, --keep-commands, --keep-settings to preserve
   specific surfaces that the default would remove. Existing --remove-*
   flags continue to broaden the sweep.

3. Preflight banners on both install.sh and uninstall.sh — print scope,
   target, the per-surface plan (with SKIP / KEEP markers reflecting active
   flags), and a pointer to the inverse script with --all warnings. Read
   before proceeding. Always recommend --dry-run for first runs.

4. Bug fixes:
   - uninstall.sh CLAUDE_MD_PATH ordering: was referenced in banner before
     definition; moved up.
   - uninstall.sh --help: was using fixed line range that overshot when the
     usage block grew; replaced with awk that prints leading comment block
     up to first non-comment line — robust to script edits.

Smoke-tested both scopes end to end:
- User scope (skill at $HOME/.claude/skills/harness/): installs full kit,
  settings.json hook commands are ~/.claude/hooks/..., status reports
  scope=user, --all sweeps everything.
- Project scope (skill at <proj>/.claude/skills/harness/): installs only
  to <proj>/.claude/ + project CLAUDE.md, hook commands are .claude/hooks/...,
  $HOME is not touched, --all sweeps the project files.
- --include=hooks,commands installs only those two surfaces.
- --keep-hooks --keep-commands during uninstall preserves them.
- Round trip: 12 installed → 14 removed default → 21 removed --all.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Add CI workflow: shellcheck + install/uninstall round-trip on Linux + macOS.
- Soften block-force-push.sh: branch -D rule narrowed to protected names so
  worktree feature-branch cleanup is allowed.
- Soften verify-before-stop.sh: only fires on a dirty working tree; clean tree
  short-circuits to allow Stop. Documents CLAUDE_SKIP_VERIFY=1 override.
- Add scripts/_detect_stack.py — detects PHP/Laravel, Node/JS/TS, Python, Go,
  Rust, Ruby, Elixir from manifest files and emits Markdown bullets for the
  '## Stack signals' section.
- install.sh now auto-fills CLAUDE.md '## Stack signals' from the detector at
  install time, removing one of the manual hand-edit steps.
- Cosmetic: shellcheck-clean status.sh and uninstall.sh; selective
  SC2088/SC2016 disables for literal $VAR/tilde strings written into JSON.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- scripts/update.sh: refresh installed files vs current templates without
  clobbering customisations. sha256 content-match per file → identical /
  missing / modified. Modified files default to SKIP with a printed diff;
  --force overwrites, --merge writes the new template to <file>.new alongside
  for manual review.
- scripts/doctor.sh: end-to-end diagnostic. Combines status with sanity
  checks: hashing tool present, target writable, settings.json parseable, all
  4 hooks executable and wired (no foreign paths), block-force-push smoke-
  test, memory dir populated, CLAUDE.md '## Stack signals' not still
  placeholder, snapshot repo recency.
- SKILL.md + README.md: dispatch tables, file inventories, and prose updated
  for the new sub-actions.
- Add MIT LICENSE at repo root and link from main README.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Copy link
Copy Markdown

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Copilot reviewed 25 out of 25 changed files in this pull request and generated 8 comments.


💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread skills/harness/scripts/status.sh
Comment thread skills/harness/scripts/snapshot.sh Outdated
Comment thread skills/harness/assets/hooks/block-force-push.sh Outdated
Comment thread skills/harness/scripts/update.sh
Comment thread skills/harness/SKILL.md Outdated
Comment thread README.md Outdated
Comment thread skills/harness/scripts/install.sh
Comment thread skills/harness/scripts/doctor.sh
datashaman and others added 2 commits May 1, 2026 10:40
Adoption was buried in chat. This wires it into the skill so the same
language ("adopt", "retrofit", "add to my project") routes to a real path.

- scripts/adopt.sh: project-scope only. Detects project root (refuses to
  write into $HOME), reports existing CLAUDE.md / .claude/ / settings.json /
  scripts/harness-check.sh, runs _detect_stack.py for stack signals, scaffolds
  a starter scripts/harness-check.sh from a template, prints the next-step
  install command. Skips overwrite without --force; --dry-run supported.
- assets/harness-check.sh.tmpl: stack-aware starter pass/fail gate. Runs
  lint / types / tests for whichever ecosystem files are present
  (package.json, composer.json, pyproject.toml, go.mod, Cargo.toml, Gemfile).
  Empty-sensor case is treated as PASS so the script never strands Stop.
- SKILL.md / README.md / main README updated with dispatch entry, walkthrough,
  flag table, and file inventory.
- CI roundtrip: new step verifies adopt writes the starter, won't overwrite
  customisations without --force, refuses $HOME with exit 2, and emits the
  expected starter blocks for the detected stack.

Doesn't run install itself — adopt prepares, the user reviews the install
preflight separately.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- status.sh: guard python3 availability and try/except settings.json parse
  so missing python3 / invalid JSON produces a clear status line instead of
  a runtime error.
- snapshot.sh: secret scan now uses --exclude-dir=.git/node_modules/__pycache__
  so it doesn't traverse those trees before filtering.
- block-force-push.sh: comment claimed segment splitter handles lone &
  (background); it doesn't. Fix the comment to match reality.
- update.sh: 'identical — no-op' now genuinely skips the cp instead of
  rewriting the file with the same contents (no mtime churn).
- install.sh fill_stack_signals: export DETECT_ROOT inside the function so
  the log line is accurate without relying on caller-provided env. Drop the
  redundant DETECT_ROOT= prefixes from the two callers.
- SKILL.md: collapse the two near-duplicate post-install hand-edit blocks
  into one that covers both user and project scope.
- doctor.sh: explicit python3 capability check; settings.json validity +
  hook-wiring checks now skipped (with a clear WARN) when python3 is missing
  rather than misreporting 'not valid JSON'.

PR description updated to reflect the actual count and surface of sub-
actions (eight, including update / doctor / adopt added later in the PR).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@datashaman
Copy link
Copy Markdown
Owner Author

Copilot round-2 review — triage + fixes

Addressed in f532c70. Triaged each comment against current code; round-1 items (11 comments on 03f3ac9) were already resolved in be44002 / 1e9e87f / earlier work — verified each against current HEAD rather than re-fixing.

Round 2 (8 comments) — fixes

# File / line Issue Fix
1 status.sh:200 python3 invoked unconditionally; no try/except around json.load Added command -v python3 guard with a clear unknown status line; wrapped json.load in try/except so parse errors print cannot parse settings.json: ... instead of stack-tracing
2 snapshot.sh:70 grep -r traversed .git/ before filtering Switched to --exclude-dir=.git --exclude-dir=node_modules --exclude-dir=__pycache__ so the scan never enters those trees
3 block-force-push.sh:24 Comment claimed splitter handles lone & (background); implementation only handles && Comment corrected to match reality — none of the patterns we block care about backgrounding, so the splitter behaviour is intentional
4 update.sh:122 identical — no-op action still did cp $template $installed Genuine no-op now (: placeholder); no mtime churn when files match
5 SKILL.md:108 Two near-duplicate post-install hand-edit blocks Collapsed to one block covering both user and project scope; mentions the auto-fill from manifests so the user knows to verify, not type from scratch
6 README.md PR description claimed five sub-actions; docs list more PR body updated — now correctly states eight (install / uninstall / update / doctor / adopt / snapshot / status / audit) with descriptions for each
7 install.sh:277 fill_stack_signals relied on caller-supplied DETECT_ROOT env var; brittle if called without it Export DETECT_ROOT="$detect_root" inside the function so the log is always accurate; dropped the now-redundant DETECT_ROOT= prefixes from the two callers
8 doctor.sh:90 Capability check only verified sha256; downstream JSON checks assumed python3 Added explicit python3 availability check (1b); JSON validity + hook-wiring checks now WARN-skip with a clear message when python3 is missing instead of misreporting not valid JSON

Round 1 (11 comments on 03f3ac9) — verified, no changes needed

All resolved in earlier commits on this PR:

  • README Requirements lists sha256sum / shasum / python3
  • do_or_dry uses "$@" + printf %q, no eval
  • HOME_CLAUDE env-var indirection removed; replaced with $TARGET derived from scope detection ✓
  • sha() in status.sh has the full fallback chain (sha256sumshasumpython3) and reports cannot hash explicitly on failure ✓
  • install.sh usage comment no longer says "interactive" ✓
  • env-var changes tracked in the changed flag (line 369: changed = True on env insertion) ✓
  • uninstall.sh --help uses awk-based comment parsing — no set -euo pipefail leak ✓
  • uninstall.sh sha256() has same fallback chain ✓
  • OUR_CMDS in both uninstall.sh and status.sh derive paths from f"{base}/..." (where base is scope-aware) — no hard-coded ~/.claude/...

Local verification

  • shellcheck silent across all .sh files (incl. assets/hooks/*.sh, assets/harness-check.sh.tmpl)
  • doctor.sh against my live ~/.claude: 11 pass / 0 warn / 0 fail
  • update.sh against modified vs identical files: diff-only on modified, no rewrite on identical

datashaman and others added 4 commits May 1, 2026 10:51
Addresses two genuine correctness/security gaps from review.

- install.sh + uninstall.sh: settings.json writes go through tmp + os.replace
  so an interrupted process can't leave settings.json empty/partial. Claude
  Code refuses to load invalid JSON, and "interrupted" is plausible (laptop
  sleep, ^C between truncate and flush, OOM).

- block-force-push.sh: previously aspirational name — `git push origin :main`
  (refspec push-delete), `git push --delete origin main`, and `git push origin
  +HEAD:main` (the leading + is force without --force) all bypassed the rule.
  Three new patterns close the loop. Protected-branch list matches the existing
  `git branch -D` rule so feature-branch deletes still flow through.

- CI: matrix step exercises all three new push patterns (block + allow), plus
  asserts no settings.json.tmp leftover after install and that the resulting
  JSON parses.

Verified locally: 15/15 block-force-push test cases pass (8 block, 7 allow);
shellcheck silent; doctor 11/0/0; settings.json write leaves no tmp file.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- harness-check.sh.tmpl: drop the narrow grep on composer.json scripts
  before running pint. If ./vendor/bin/pint exists, just run it. The
  previous guard skipped pint in any project that didn't have a "pint" or
  "format:check" script wired — which is most of them.

- CI: drop a fake package.json into the test $HOME so _detect_stack picks
  it up; assert the placeholder block in CLAUDE.md was replaced and that
  the detected bullets contain something from the manifest (TypeScript /
  Next / React / npm).

- CI: doctor.sh round-trip step asserts "0 fail" on a clean install and
  that the block-force-push smoke result is present in the output.

- CI: update.sh round-trip — modify a hook, run update, assert (a) the
  modified hook is flagged as such and not overwritten, (b) an identical
  hook's mtime is unchanged (genuine no-op), and (c) update --merge writes
  <file>.new alongside without clobbering the original.

Smoke verified locally: stack signals filled correctly (TypeScript / Next /
Vitest detected from a fake package.json), doctor 11/0/0, update.sh
identical-mtime preserved, --merge writes .new.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The stack-signal auto-fill (added earlier this PR) replaces the placeholder
in CLAUDE.md with detected bullets at install time. The existing 'status —
every surface should report installed/wired/set' assertion grepped the full
status output for any 'modified' line and failed — but the modification is
now expected.

Filter CLAUDE.md out of the modified check; any other modified surface still
fails the build.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Build went green on 1fb78de — this is just clearing the Node.js 20
deprecation warning that GitHub now emits on v4. v5 ships with Node 24,
which is the runtime GitHub will force on June 2nd 2026.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
@datashaman datashaman merged commit b9ae08b into main May 1, 2026
3 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants