Skip to content

refactor(claude): split cloud bootstrap out of the SessionStart hook - #389

Merged
kyle-sexton merged 1 commit into
mainfrom
claude/cloud-bootstrap-migration-xws8cg
Aug 15, 2026
Merged

refactor(claude): split cloud bootstrap out of the SessionStart hook#389
kyle-sexton merged 1 commit into
mainfrom
claude/cloud-bootstrap-migration-xws8cg

Conversation

@kyle-sexton

Copy link
Copy Markdown
Contributor

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 (with CLAUDE_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 named install-plugins.sh, not session-start.sh — it is the same script the SessionStart hook ran, so it is the one migrated.
  • Header rewritten: the script is the repo's cloud bootstrap with two callers (pre-launch environment setup, per-session SessionStart drift repair), not "a SessionStart hook". Provisioning logic, idempotency, bash-3.2 compatibility, and the hook-output reloadSkills emission are unchanged.
  • Added the CLAUDE_CODE_REMOTE=true guard the split layout expects — the previous script had none. Outside cloud sessions the script now exits immediately; trusted local machines load the declared marketplace and enabledPlugins from settings.json on 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.
  • Repo-root fallback adjusted from /../.. to /.. for the script's new depth (one level shallower); CLAUDE_PROJECT_DIR remains the preferred root source.
  • .claude/settings.json SessionStart hook (matcher startup|resume) repointed to bash "$CLAUDE_PROJECT_DIR/.claude/cloud-bootstrap.sh". No other hooks or settings touched.
  • No other references existed (docs, CI, scripts were searched for both session-start.sh and install-plugins).

Verification (in a cloud session)

  • bash -n: clean. shellcheck: not available in the session container.
  • Two runs with CLAUDE_CODE_REMOTE=true: both idempotent no-ops — 65 enabled, 0 newly installed, ~1.2 s each.
  • Run with CLAUDE_CODE_REMOTE unset: exits in ~3 ms, no output, claude plugin list state hash unchanged.
  • Root-fallback check with CLAUDE_PROJECT_DIR unset, invoked from outside the repo: resolves to the repo root (not its parent) and reads .claude/settings.json correctly.

Related

  • N/A

🤖 Generated with Claude Code

https://claude.ai/code/session_017ZRCysRUU7pARyJwcwg3TJ


Generated by Claude Code

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
@cursor

cursor Bot commented Aug 15, 2026

Copy link
Copy Markdown

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

claude Bot commented Aug 15, 2026

Copy link
Copy Markdown

Claude finished @kyle-sexton's task in 1m 55s —— View job


Code review (/review:code-review)

  • Gather context (PR diff, changed files, CLAUDE.md/REVIEW.md)
  • Invoke /review:code-review skill and follow its procedure
  • Check for stale references to the old hook path
  • Post summary review comment

Skip gate: did not apply — PR is open, not a draft, and the change (adding a CLAUDE_CODE_REMOTE guard) is a real behavioral change, not purely mechanical.

Scope: .claude/cloud-bootstrap.sh (new, via git mv), .claude/hooks/install-plugins.sh (deleted), .claude/settings.json.

Findings

None at the code-review bar (correctness/maintainability). Specifically checked and found sound:

  • Path-depth math: the repo-root fallback changed from dirname/../.. to dirname/.. (.claude/cloud-bootstrap.sh#L23). The script moved from .claude/hooks/ (two levels below root) to .claude/ (one level below root), so the shallower fallback is correct.
  • Body diff: the marketplace/install logic (.claude/cloud-bootstrap.sh#L29-63) is byte-for-byte identical to the deleted script apart from the cloud-bootstrap:/install-plugins: log-prefix rename — no logic drift introduced by the move.
  • Guard placement: [[ "${CLAUDE_CODE_REMOTE:-}" == "true" ]] || exit 0 (.claude/cloud-bootstrap.sh#L22) sits after set -euo pipefail and correctly no-ops under set -u for an unset var.
  • No stale references: searched the committed tree (git grep at HEAD) for install-plugins / hooks/install-plugins / session-start.sh — none found. .claude/settings.json#L86 is repointed to cloud-bootstrap.sh.
  • Syntax: manually read through the script; no unbalanced quoting/brackets/heredoc issues.

One item I can't independently verify and am flagging only for visibility, not as a finding: the guarded early-exit means the SessionStart hook now does nothing on local/trusted sessions (previously it ran unconditionally there too). The PR's rationale — that trusted local machines already load the declared marketplace/enabledPlugins from settings.json without this script — lives in the melodic-software/claude-code-plugins repo, outside this diff, so I'm noting it as author-claimed rather than something I confirmed.

@github-actions

Copy link
Copy Markdown

Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count.

@kyle-sexton
kyle-sexton merged commit 9e7d9c8 into main Aug 15, 2026
45 checks passed
@kyle-sexton
kyle-sexton deleted the claude/cloud-bootstrap-migration-xws8cg branch August 15, 2026 05:55
kyle-sexton added a commit that referenced this pull request Aug 16, 2026
## 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>
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