Skip to content

feat(archdev-skill): install harness hooks from the skill bootstrap through the CLI - #33

Merged
calvin-archastro merged 7 commits into
mainfrom
feat/skill-plugin-bootstrap
Sep 28, 2026
Merged

calvin-archastro merged 7 commits into
mainfrom
feat/skill-plugin-bootstrap

Conversation

@calvin-archastro

@calvin-archastro calvin-archastro commented Sep 25, 2026 •

Copy link
Copy Markdown
Contributor

Review on ArchCode

Problem and author intent

ArchDev's contract (read the room, post lifecycle moments, store PR review annotations after every push) reaches an agent in two ways: the archdev skill, and the harness hooks that archdev repo hook setup installs. Before this PR, both depended on something the agent or user had to do first:

user installs skill (npx skills add)          -> skill on disk, hooks not installed
session starts                                -> model decides whether to load the skill
  skill not loaded                            -> no bootstrap, no hooks, no contract
  skill loaded -> bootstrap.sh                -> repo hook setup --refresh
                                                 (updates existing hooks only; a user who never
                                                  ran the Map phase still has none)

Observed: in one operator session the skill was never loaded and about ten PRs went out without review annotations. archdev repo status on that machine reported hooks:claude missing even though ArchDev was installed and logged in.

The broken invariant: once ArchDev is installed for a harness, that harness's sessions (and, in Claude Code, their subagents) should receive the contract without anyone choosing to load a skill or run repo hook setup. This PR is Track 2 of that fix: the skill bootstrap installs hooks for the harness running it.

What changed

Bootstrap installs, not only refreshes (archdev/scripts/bootstrap.sh, bootstrap.ps1)

  • Before (main): repo hook setup --refresh, which touches only harnesses that already have ArchDev hooks. A harness with none stays without hooks.
  • After: the bootstrap detects the harness from the marker it sets on shells it spawns (CLAUDECODE=1 → claude, CODEX_THREAD_ID → codex, GROK_SESSION_ID → grok) and runs archdev repo hook setup --harness <h>, then repo hook setup --refresh as before. The CLI makes every decision: setup --harness <h> without --force leaves current hooks alone, replaces stale ones, and skips a harness the user removed with --uninstall (recorded in ~/.archdev/hook-opt-out.json). The bootstrap no longer reads any harness config file.
  • Inside a Factory worker or daemon pipeline step (ARCHDEV_FACTORY_AGENT_ROLE, ARCHDEV_JOB_ID, or ARCHDEV_STEP_ID set) the bootstrap installs nothing and only refreshes, matching the CLI self-heal's rule that the host owns those sessions' harness config. repo hook setup --harness has no such guard of its own, so the bootstrap carries it.
  • Minimum CLI is now 0.46.6 (was 0.46.5) in both scripts, SKILL.md, and references/bootstrap.md. 0.46.6 is the first release with the opt-out record and the self-healing install (firstlanding 0d7743c67d); against 0.46.5, setup --harness <h> would undo a deliberate uninstall. An older CLI is upgraded by the existing bootstrap upgrade path, so the capability probe is gone.
  • Failures still print a remediation line (then run: archdev repo hook setup --harness <h>) and never block the skill; stdout is still only the archdev path.

Skill docs (archdev/SKILL.md, references/map.md, references/monitor.md, references/bootstrap.md, README.md)

  • The CLI is stated as the single setup path, with the exact commands: install/upgrade the CLI (bootstrap or official installer), archdev setup (first run: login, repo, skills, and hooks for every harness), archdev setup --skills (skills only, via npx skills add ArchAstro/archdev --skill '*' --global --agent <detected>), archdev repo hook setup (every harness), archdev repo hook setup --harness <h> (one harness), and archdev repo status to verify (hooks:<harness> missing/stale).
  • Documents that most archdev commands run inside Claude Code or Codex reinstall that harness's missing or stale hooks (not setup, repo hook …, --help/--version, or Factory/daemon sessions), and the opt-out rules: --uninstall records it; full setup, the self-heal, setup --harness, --refresh, and the bootstrap respect it; a bare repo hook setup or --force clears it. repo status still reports an opted-out harness as hooks:<h> missing, and the skill tells the agent not to "fix" that.
  • New "Subagents and spawned agents" section in SKILL.md. What the CLI hooks already do (checked against CLI 0.46.8 and firstlanding harnesses.ts): Claude gets SessionStart, UserPromptSubmit, PostToolUse, Stop, SubagentStart, and SubagentStop; Codex gets the first four; Grok gets SessionStart, PostToolUse, Stop. In a Git checkout, repo hook subagent-start injects additionalContext that says "Load the archdev skill now … and follow it", the annotation rule, and "You are a subagent: do not post to the team room." subagent-stop holds the subagent once for a PR head it pushed without annotations. Harnesses without a subagent event (Codex, Grok) and Claude without hooks get nothing in spawned agents, so the section tells the parent to put the instruction in the spawned agent's prompt and to stay responsible for room posts and annotation checks.
  • monitor.md still documents the Claude stop and subagent-stop annotation hold; map.md describes the simplified bootstrap and the opt-out semantics.

Unchanged: the skill description's session-start trigger, npx skills add as what archdev setup --skills runs, tasks/scripts/bootstrap.* (still refresh-only), and the CLI itself.

Scope and risk

  • Scope: skill scripts, docs, tests, CI in the public distribution repo. No CLI or server code.
  • Risk: low to medium. The bootstrap now writes harness config, but only through archdev repo hook setup --harness <h> for the harness running it, which is the same install archdev setup and the CLI self-heal already perform. Raising the minimum to 0.46.6 means bootstrap upgrades 0.46.5 installs on next skill load (the same upgrade path used for every previous minimum bump).

User impact

  • A user who loads the archdev skill in Claude Code, Codex, or Grok gets that harness's hooks without running the Map phase; later sessions and Claude subagents get the contract even if they never load the skill.
  • README and skill point at archdev setup / archdev repo hook setup as the setup path.

Testing

Canonical end-to-end proof: scripts/test-skill-bootstrap-cli.sh. Actors: the real archdev/scripts/bootstrap.sh, the released archdev CLI on PATH (CI installs the latest release with ./install.sh), and a throwaway HOME per case under env -i. It asserts:

  1. With CLAUDECODE=1 and an empty ~/.claude, bootstrap prints only the archdev path and settings.json gets archdev hooks for exactly PostToolUse, SessionStart, Stop, SubagentStart, SubagentStop, UserPromptSubmit (the hooks:claude missing state is fixed).
  2. The installed SessionStart and SubagentStart commands, read from settings.json and run in a Git checkout, both print "Load the archdev skill".
  3. A second bootstrap leaves settings.json byte-identical.
  4. After archdev repo hook setup --uninstall --harness claude, another bootstrap leaves no archdev hooks (opt-out respected).
  5. With CLAUDECODE=1 ARCHDEV_FACTORY_AGENT_ROLE=worker, no Claude hooks are written.
  6. With CODEX_THREAD_ID, ~/.codex/hooks.json gets PostToolUse, SessionStart, Stop, UserPromptSubmit.

Control: with the bootstrap's install step disabled, 5 of the checks fail. All 8 pass locally against CLI 0.46.8.

Unit-level, fake CLI (scripts/fake-archdev, which records every repo hook setup argv and prints to stdout to catch leaks):

  • scripts/test-skill-bootstrap.sh (10 checks) and scripts/test-skill-bootstrap.ps1 (9 cases): Claude/Codex/Grok markers → setup --harness <h> then --refresh; CLAUDECODE=0, no marker, and each of ARCHDEV_FACTORY_AGENT_ROLE / ARCHDEV_JOB_ID / ARCHDEV_STEP_ID → --refresh only; failing setup → bootstrap exits 0, still refreshes, prints the path, and (bash) names the remediation command. Every case requires stdout to be exactly the archdev path. Control: with the install step disabled, both suites fail. Both pass locally (PowerShell 7.6.6 on Linux).
  • Also ran bootstrap.ps1 under pwsh against real CLI 0.46.8 with CLAUDECODE=1: all six Claude hook events installed, stdout only the path.
  • bash -n on every script.

CI: workflow Skill Bootstrap Checks (.github/workflows/skill-bootstrap-checks.yml), on every PR and push to main. Jobs: skill-bootstrap (bash and pwsh fake-CLI suites on ubuntu), skill-bootstrap-windows (PowerShell suite under Windows PowerShell 5.1 with the fake CLI via Git Bash), and skill-bootstrap-cli (latest release + scripts/test-skill-bootstrap-cli.sh). The Windows PowerShell 5.1 job was not run locally. An independent review pass found the Factory/daemon install, the opt-out remediation loop in the docs, and several overstated doc claims; all are fixed here.

Follow-ups and known issues

  • The CLI still carries unused Claude plugin detection from firstlanding 0d7743c67d; ArchAstro/firstlanding#15536 removes it. No ArchDev plugin was ever released, so nothing depends on it.
  • A user who ran repo hook setup --uninstall on 0.46.5 or earlier has no opt-out record, so the first bootstrap after upgrading reinstalls their hooks. repo hook setup --uninstall --harness <h> on 0.46.6+ records it.
  • Grok detection uses GROK_SESSION_ID, which Grok documents for hook processes; that it is also set in the shell tool's environment is unverified. A missed detection falls back to --refresh.
  • CI runs the real-CLI suite against the latest release, not pinned 0.46.6, so the 0.46.6 floor is taken from the release history (0.46.5 lacks the opt-out record; 0.46.6 contains firstlanding 0d7743c67d).
  • The bootstrap does not detect Pi (--harness pi is documented for manual setup).
  • archdev repo status reports an opted-out harness as missing with a remediation that a bare repo hook setup would apply; a distinct opted-out state belongs in the CLI (firstlanding).
  • Codex and Grok have no subagent hook event, so spawned agents there rely on the parent's prompt (documented in SKILL.md).
  • tasks/scripts/bootstrap.sh|ps1 still only refresh.

🤖 Generated with Claude Code

calvin-archastro and others added 6 commits September 25, 2026 14:36
…ode plugin

The monitor contract reached an agent only if the model chose to load the
skill, and the skill's bootstrap only refreshed hooks that already existed,
so a user who never ran the Map phase had none. One operator session sent
about ten PRs without review annotations that way.

Bootstrap (sh and ps1) now detects the harness running it (CLAUDECODE=1,
CODEX_THREAD_ID, GROK_SESSION_ID) and runs `repo hook setup --harness <h>`
when that harness has no archdev hooks, then refreshes as before. It skips
Claude settings hooks when the archdev plugin is installed for the user and
enabled, and installs only on a CLI that lists `repo hook plugin-hooks-json`,
the Track 1 release whose `--uninstall` records an opt-out, so no released
CLI can undo a deliberate uninstall. Failures stay non-blocking.

The repository root is now the `archastro` Claude Code marketplace and the
`archdev` plugin: plugin.json serves ./archdev and ./tasks in place, and
hooks/hooks.json runs the CLI's Claude wiring behind a `command -v archdev`
guard. README documents the install; SKILL.md says the hooks deliver the
contract without the skill.

A new workflow runs the bootstrap suites against a fake archdev, validates
the plugin with `claude plugin validate`, and installs it into a temporary
Claude config to watch its hooks fire.

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

The PowerShell suite only ran under pwsh on Linux, so the path SKILL.md
actually uses on Windows (`powershell -File bootstrap.ps1`, USERPROFILE,
archdev.cmd resolution) was untested. On Windows the suite now wraps the
fake CLI in an archdev.cmd that runs it through Git Bash and runs the
bootstrap with Windows PowerShell; a windows-latest job runs it. A
.gitattributes entry keeps the Bash scripts LF on Windows checkouts.

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

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
firstlanding#15419 (Track 1) raises HOOK_SPEC to 4, prints the plugin
hooks with `repo hook plugin-hooks-json --harness claude`, and makes the
Claude stop and subagent-stop hooks hold once per pushed PR head without
review annotations.

hooks/hooks.json is now that command's output, byte for byte, so every
handler passes --spec 4. The plugin check pins spec 4 and the generated
description, and still diffs against the CLI once the archdev on PATH
provides the command. The install test expects --spec 4.

Bootstrap plugin detection now uses the CLI's claudePluginInstall rule:
an archdev@<any marketplace> install at user, managed or no scope whose
key enabledPlugins sets to true. Both bootstrap suites cover the mirror
marketplace, unscoped version-1 records, a missing enabledPlugins entry
and a similarly named plugin.

monitor.md drops "The hooks never block a stop", says which stops Claude
holds and who is exempt, and says the hooks deliver the contract at
session and subagent start; map.md lists the subagent events.

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

CLAUDE_CODE_PLUGIN_CACHE_DIR moves Claude Code's whole plugins root,
installed_plugins.json included, so a relocated plugin install looked
absent to the bootstrap, which then wrote settings.json hooks on top of
the plugin's and every hook ran twice.

Both bootstraps now resolve the plugins root with the CLI's
claudePluginsRoot rule (firstlanding#15419): the variable when set and
non-empty, with `~` or a leading `~/` expanded to the home directory, a
relative value (including `~user/`, which is not expanded) resolved
against the working directory, and otherwise <config dir>/plugins.
enabledPlugins is still read from <config dir>/settings.json.

Both suites run each case from its own working directory and add six
cases: absolute, `~/`, relative and `~user/` roots are detected; an
empty value falls back to the default root; a variable pointing at a
root without a record ignores the record under the default root. Against
the previous scripts five of the six fail; expanding every `~` prefix
fails the `~user/` case.

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

Per the repo owner, ArchDev ships no Claude Code plugin: the archdev CLI
installs skills and hooks, and the skill documents how.

- Remove .claude-plugin/, hooks/hooks.json, the plugin CI job, and the
  plugin check and install scripts.
- Bootstrap runs `repo hook setup --harness <h>` for the harness running
  it (CLAUDECODE / CODEX_THREAD_ID / GROK_SESSION_ID) and leaves every
  decision (current, stale, opted out) to the CLI; no plugin or hook-file
  parsing. Skip the install in Factory and daemon sessions. Require CLI
  0.46.6, the first release that keeps the --uninstall opt-out.
- SKILL.md, references, and README name the CLI as the single setup path
  with exact commands, the opt-out rules, and how subagents and spawned
  agents get the archdev skill.
- Fake-CLI bootstrap suites cover the new rule; test-skill-bootstrap-cli.sh
  runs the bootstrap against the released CLI in CI.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@calvin-archastro calvin-archastro changed the title feat(archdev-skill): install hooks from bootstrap and ship a Claude Code plugin feat(archdev-skill): install harness hooks from the skill bootstrap through the CLI Sep 26, 2026
No ArchDev Claude Code plugin was ever released, so the README and skill
need not say there isn't one.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
@calvin-archastro
calvin-archastro merged commit 92e6b2e into main Sep 28, 2026
8 checks passed
@calvin-archastro
calvin-archastro deleted the feat/skill-plugin-bootstrap branch September 28, 2026 00:14
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.

1 participant