feat(claude): bootstrap the pinned Node toolchain in cloud sessions - #390
Conversation
|
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. |
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e9750d32f4
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
|
Claude finished @kyle-sexton's task in 3m 23s —— View job Claude finished the code review
|
|
Claude has reviewed this PR 1 time. The lane skips further automatic reviews after 5; deleting this comment resets the count. |
40e91ab to
0959182
Compare
Rebased onto the merged `.claude/cloud-bootstrap.sh` layout. The earlier version of this branch added a second script at `.claude/hooks/cloud-bootstrap.sh` with its own SessionStart registration, which duplicates what main already has. Main's bootstrap installs the plugin catalog and nothing else, so a cloud session still runs the component contract tests and lint gates on whatever Node the VM image ships, against missing node_modules (melodic-software/claude-code-plugins#2654). Inserted ahead of the plugin-CLI guards on purpose: those `command -v` checks exit 0 when `claude` or `jq` is missing, and the toolchain must not be collateral damage of an unrelated CLI being absent. Additive — 76 lines added, none removed. - Node from `.node-version` via the VM's nvm, exported through `$CLAUDE_ENV_FILE` (dedup-guarded, since SessionStart fires on resume). - `npm ci` per project, not just the root. The root package.json declares no workspaces, so a root install never reaches the independently-locked projects under components/ (concurrency-policy, dependabot-policy, pr-convention-policy, runner-policy) or distribution/ — their modules would be missing and `npm run test:runner-policy` would fail with ERR_MODULE_NOT_FOUND despite the bootstrap reporting success. ci.yml already works around this with its own `npm ci --prefix` steps; npm_ci_at() does the same while keeping the per-project freshness check, so a resumed session skips every tree already in sync. The subshell sets its own `set +e` and ends in an explicit `exit 0` rather than being wrapped in `|| true`: this file runs under `set -e`, and wrapping would put every call inside the subshell in an `||` context, which is exactly what .shellcheckrc's check-set-e-suppressed (SC2310) exists to flag. Verified: shfmt -d clean, shellcheck -x -P SCRIPTDIR clean under the repo rcfile, bash -n parses. Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx
0959182 to
e3d2952
Compare
## 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>

Summary
Cloud sessions (web,
claude --cloud, mobile, routines) start on a fresh Ubuntu VM shipping Node 20/21/22 with nonode_modules, so a session here runs the component contract tests and lint gates on the wrong Node against missing tooling. The fleet-wide verification run confirmed this class of failure (melodic-software/claude-code-plugins#2654).This is this repository as a consumer, and is deliberately separate from #388, which adds the shared-environment component this repository owns. Same rollout, opposite side of the boundary: #388 is the org's generic warm cache, this is the exact pin only a hook can read.
No linked issue
Changes
.claude/hooks/cloud-bootstrap.sh(new) — Node from.node-versionvia the VM's nvm, thennpm ciwhen the lockfile is newer than the installed tree, exported through$CLAUDE_ENV_FILE(dedup-guarded, since SessionStart fires on resume too)..claude/settings.json— registers it ahead of the existing plugin-install hook.Cloud-only (
CLAUDE_CODE_REMOTE=true), idempotent, warn-and-continue. Local sessions are unaffected — the guard exits 0 before the hook does anything.Verification
shellcheck -x -P SCRIPTDIRclean under this repo's.shellcheckrc.claude/settings.jsonparsesNot claimed: I have not run this hook inside a live cloud session. Its logic is the fleet template, exercised by a 12-case suite in the medley sibling PR (melodic-software/medley#1818); the Node and npm cases there cover exactly the two blocks in this copy.
Scope note: only the root lockfile is synced.
distribution/has its own lockfile and is left to its existingnpm ci --prefix distributionflow rather than being synced speculatively on every session.Related
🤖 Generated with Claude Code
https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx
Generated by Claude Code