refactor(claude): split cloud bootstrap out of the SessionStart hook - #389
Conversation
Claude Code builds its plugin/command/skill registry at process start and never re-reads it, so anything a SessionStart hook installs is invisible to the session that ran the hook. Account environments' setup scripts now run the committed bootstrap (with CLAUDE_CODE_REMOTE=true) after clone and before the session process launches — the only path that gets plugins loaded at turn one. - git mv .claude/hooks/install-plugins.sh -> .claude/cloud-bootstrap.sh and rewrite the header for its two callers: the pre-launch environment setup script, and the SessionStart hook as per-session drift repair (the environment cache can be ~7 days stale; hook installs go live at the next resume). - Add the CLAUDE_CODE_REMOTE=true guard the split layout expects: outside cloud sessions the script exits immediately, since trusted local machines load the declared marketplace and enabledPlugins on their own. - Adjust the repo-root fallback from ../.. to .. for the script's new depth. - Repoint the SessionStart hook (startup|resume) in .claude/settings.json. Verified: bash -n clean; two runs with CLAUDE_CODE_REMOTE=true are idempotent (65 enabled, 0 newly installed, ~1.2s); a run without the variable exits in ~3ms with plugin state unchanged. Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_017ZRCysRUU7pARyJwcwg3TJ
|
Bugbot is not enabled for your account, so this pull request was not reviewed. Enable Bugbot in the Cursor dashboard to get automatic reviews on future PRs. |
|
Claude finished @kyle-sexton's task in 1m 55s —— View job Code review (
|
|
Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
## Summary Collapses the fleet's twelve hand-copied, drifted `.claude/cloud-bootstrap.sh` generations into one standards-owned component, distributed by exact materialization with the standard tri-mode lever: take (`managed`), enrich (a repo's own never-synced `.claude/cloud-bootstrap.local.sh`, run by the canonical script), or customize (`locally-owned`). ## Fix - **New component `components/cloud-bootstrap/`** — the canonical script is generic by construction: no repo names, marketplace identifiers, or pinned versions. Node from `.node-version`, `npm ci` from the root lockfile, .NET exactly as `global.json` pins, marketplaces and plugins from `.claude/settings.json` (every declared marketplace; every `enabledPlugins: true` entry, whichever marketplace it names — which also removes the previous hardcoded-marketplace second code path). Adds an environment-snapshot stamp log line (per-account build visibility) and shallow-clone repair (`origin/main` resolvable for base-ref diffs). - **`distribution/sync-manifest.yml`** — `managed` for `.github`, `ci-runner`, `ci-workflows`, `github-iac`, `provisioning`; `locally-owned` with recorded reasons for `claude-code-plugins` (directory-source dogfooding) and `medley` (repo-specific toolchain; converge later); `dotfiles` excluded pending its cloud adoption. - **Contract tests** — `cloud-bootstrap.test.sh` (parse, generic-by-construction, calling-contract landmarks, stamp-path lockstep with cloud-environment, byte-equality with this repo's own materialized copy); `setup.test.sh` now lockstep-tests the cloud-environment Node warm-cache pin against `.node-version`. - **`distribution/check-plugin-baseline.sh`** — report-only fleet drift check of each target's plugin catalog against this repository's own settings file (the dogfooded fleet baseline), with an offline `--compare` mode under test. - CI: the cloud-environment job now runs both cloud contract tests. ## Verification - `harness/shell/run-tests.sh` over the three touched test files: 3 passed. - `distribution/sync-manifest.sh validate`: `Manifest valid: 38 components, 8 targets`; `plan --targets melodic-software/ci-runner` renders `100755 components/cloud-bootstrap/cloud-bootstrap.sh -> .claude/cloud-bootstrap.sh`. - shellcheck + shfmt clean on all new/changed shell; markdownlint clean on changed docs; lefthook pre-commit green. - `sync-manifest.test.sh`: engine fixture cases pass except the two symlink-rejection cases, which fail only on Windows (symlink creation degrades to a regular file there); they are fixture-local and unaffected by this change — CI's Ubuntu run is authoritative. - Post-merge follow-ups (deliberate, not in this PR): the provisioning fan-out PR needs its repo-specific toolchain re-added as `.claude/cloud-bootstrap.local.sh` (extraction prepared); a live cloud verification per the cloud-environment README's stamp checklist. No linked issue ## Related - Builds on #388/#389/#390 (canonical environment script; bootstrap split; Node toolchain). - Node-pin lockstep follows the #394 precedent for hand-duplicated-value drift. 🤖 Generated with [Claude Code](https://claude.com/claude-code) https://claude.ai/code/session_012tKxg98B3QySqEPf8UqwX3 --------- Co-authored-by: Claude Fable 5 <noreply@anthropic.com>
Migrates the Claude Code cloud bootstrap from the SessionStart-hook layout to the split cloud-bootstrap layout, per melodic-software/claude-code-plugins
docs/CLOUD-SESSIONS.md§"Plugins in sessions on this repo" (verified 2026-08-15).No linked issue
Why
Claude Code builds its plugin/command/skill registry at process start and never re-reads it, so anything a SessionStart hook installs is invisible to the session that ran the hook. Account environments' setup scripts run the repo's committed
.claude/cloud-bootstrap.sh(withCLAUDE_CODE_REMOTE=true) after clone and before the session process launches — that pre-launch call is what makes plugins live at turn one. The SessionStart hook stays registered and runs the same script per start/resume as drift repair (the environment cache can be ~7 days stale); its installs go live at the next resume.What changed
git mv .claude/hooks/install-plugins.sh→.claude/cloud-bootstrap.sh(history preserved;.claude/hooks/is now empty and gone). The repo's script was namedinstall-plugins.sh, notsession-start.sh— it is the same script the SessionStart hook ran, so it is the one migrated.reloadSkillsemission are unchanged.CLAUDE_CODE_REMOTE=trueguard the split layout expects — the previous script had none. Outside cloud sessions the script now exits immediately; trusted local machines load the declared marketplace andenabledPluginsfromsettings.jsonon their own (the script's own long-standing rationale), so the hook was only doing real work in cloud sessions anyway. Flagging it since it is the one behavioral change beyond the move./../..to/..for the script's new depth (one level shallower);CLAUDE_PROJECT_DIRremains the preferred root source..claude/settings.jsonSessionStart hook (matcherstartup|resume) repointed tobash "$CLAUDE_PROJECT_DIR/.claude/cloud-bootstrap.sh". No other hooks or settings touched.session-start.shandinstall-plugins).Verification (in a cloud session)
bash -n: clean.shellcheck: not available in the session container.CLAUDE_CODE_REMOTE=true: both idempotent no-ops —65 enabled, 0 newly installed, ~1.2 s each.CLAUDE_CODE_REMOTEunset: exits in ~3 ms, no output,claude plugin liststate hash unchanged.CLAUDE_PROJECT_DIRunset, invoked from outside the repo: resolves to the repo root (not its parent) and reads.claude/settings.jsoncorrectly.Related
🤖 Generated with Claude Code
https://claude.ai/code/session_017ZRCysRUU7pARyJwcwg3TJ
Generated by Claude Code