Skip to content

feat(cloud-environment): add canonical cloud environment setup script - #388

Merged
kyle-sexton merged 3 commits into
mainfrom
feat/cloud-environment-component
Aug 15, 2026
Merged

feat(cloud-environment): add canonical cloud environment setup script#388
kyle-sexton merged 3 commits into
mainfrom
feat/cloud-environment-component

Conversation

@kyle-sexton

@kyle-sexton kyle-sexton commented Aug 15, 2026

Copy link
Copy Markdown
Contributor

Summary

Admits the shared Claude Code cloud environment baseline as a component (components/cloud-environment/), so the claude.ai/code setup script lands here by reviewed PR instead of by hand-editing an account-scoped UI field. The environment's script field keeps only a three-line bootstrap that curls setup.sh from raw.githubusercontent.com (on the platform's default allowlist; this repo is public, so no credentials are involved).

No linked issue

Changes

  • components/cloud-environment/setup.sh — the canonical script: parallel tracks for gh + PowerShell (apt), .NET SDKs 10.0.302/10.0.400 (fleet global.json pins), and Node 24.18.0 (fleet .node-version pin), then bakes the checked-out repo's own SessionStart hook into the snapshot. Always exits 0; stays under the ~5-minute cache-build budget.
  • components/cloud-environment/README.md — bootstrap snippet, network prerequisite, verification stamp, update/rollback lifecycle (including the cache caveat: a merged change reaches environments only on a forced rebuild or ~7-day expiry).
  • components/cloud-environment/setup.test.sh — harness-discovered contract test: script parses, ends exit 0, stamp write is ordered after the wait barrier, and the README's documented stamp path + bootstrap URL match what the script implements.

Verification findings folded in (2026-08-14 live run)

From melodic-software/claude-code-plugins#2654:

  • Blocker 1 — Trusted network access 403-blocks the .NET installer redirect chain. Custom access (+ dot.net, aka.ms, builds.dotnet.microsoft.com, download.visualstudio.microsoft.com) is now documented as a hard prerequisite, not a fallback.
  • Blocker 2 — an interrupted cache build left no trace. The script now logs every step with a timestamp to /var/log/melodic-env-setup.log and writes /opt/melodic-env-setup.done (version + timestamp) strictly last, so verification can start at the stamp.

Admission evidence (per docs/component-lifecycle.md)

  • Live consumer: the Melodic environment at claude.ai/code; the consuming workflow is documented in claude-code-plugins docs/CLOUD-FLEET-SETUP.md, whose Step 1 and verification checklist reference this exact path (guide update in flight on that repo).
  • Owner and outcome: @kyle-sexton; problem is unreviewed, unversioned environment setup; acceptance is the verification checklist passing with the stamp present; rollback is reverting the commit and forcing a cache rebuild (environments keep booting from their cached snapshot until then).
  • Delivery boundary: no sync manifest entry — the consumer is the environment UI field via the raw URL, not a repository file materialization.

Test plan

  • shellcheck -x -P SCRIPTDIR clean on both scripts under the repo rcfile
  • harness/shell/run-tests.sh components/cloud-environment/setup.test.sh — 1 passed
  • markdownlint-cli2 — 0 issues

Related

🤖 Generated with Claude Code

https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx

Admit the shared Claude Code cloud environment baseline as a component so
the claude.ai/code setup script lands here by reviewed PR instead of by
hand-editing an account-scoped UI field. The environment field keeps only a
three-line bootstrap that curls this script from raw.githubusercontent.com
(default allowlist; public repo, no credentials).

Folds in the 2026-08-14 live verification findings
(melodic-software/claude-code-plugins#2654):

- Blocker 1: document Custom network access (+ dot.net, aka.ms,
  builds.dotnet.microsoft.com, download.visualstudio.microsoft.com) as a
  hard prerequisite — Trusted 403-blocks the .NET installer redirect.
- Blocker 2: timestamped step logging to /var/log/melodic-env-setup.log and
  a completion stamp (/opt/melodic-env-setup.done) written strictly last, so
  an interrupted cache build is detectable instead of silent.

Admission evidence: live consumer is the Melodic environment plus
claude-code-plugins docs/CLOUD-FLEET-SETUP.md (bootstrap + verification
checklist reference this path); acceptance is the stamp check in that
checklist; rollback is reverting and forcing a cache rebuild (README).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx
@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 2m 56s —— View job


Code review complete

  • Gather context (PR diff, CLAUDE.md, REVIEW.md)
  • Review components/cloud-environment/setup.sh
  • Review components/cloud-environment/setup.test.sh
  • Review components/cloud-environment/README.md
  • Post findings

Scope note: melodic-software/standards has no claude-security-review.yml workflow, so per REVIEW.md this lane also applied the security-scope checks (secrets/credentials, injection, authz/tenancy). Nothing in this PR touches those — the script has no secrets, and its one dynamic input (ubuntu_ver, parsed from the local /etc/os-release) isn't attacker-controlled.

Findings

One nit posted inline on setup.sh L115-121: the repo-SessionStart-hook-baking step is the only one of the script's four steps with no else log line, so a missing "hook baked" line in $LOG can't be told apart from "CWD wasn't the repo checkout" — the exact kind of silent-failure ambiguity this PR's Blocker 2 fix (timestamped logging for every step) was meant to close.

Otherwise the script held up well under a correctness read:

  • The parallel-track / wait / stamp-write ordering is correct — the stamp write genuinely can't happen before all three tracks finish, and setup.test.sh's line-number-based check for this is sound.
  • set -u (not set -e) plus the unconditional trailing exit 0 correctly implements the "always exit 0, cache build must not fail" contract even when individual tracks fail.
  • The set +u inside the Track C subshell for nvm.sh is correctly scoped — it's a subshell, so it can't leak nounset-off into tracks A/B.
  • README/script drift (stamp path, bootstrap URL) is guarded by the test rather than hardcoded, so it won't rot silently.
  • setup.test.sh's use of the shared harness/shell/lib.sh assertion helpers matches their documented argument order.

I wasn't able to execute harness/shell/run-tests.sh or shellcheck myself in this session (no tool access to run them) — the PR body's shellcheck/test-suite/markdownlint-cli2 results are author-reported and unverified by me, not independently confirmed.

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 270ba5c703

ℹ️ 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".

Comment thread components/cloud-environment/setup.test.sh
Comment thread components/cloud-environment/setup.sh Outdated
@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 added a commit to melodic-software/claude-code-plugins that referenced this pull request Aug 15, 2026
#2668)

No linked issue

## Summary

Folds the 2026-08-14 live verification of the Melodic cloud environment
(#2654) into `docs/CLOUD-FLEET-SETUP.md`, and swaps the guide's embedded
environment setup script for a three-line bootstrap that curls the
canonical script from the new `cloud-environment` component in standards
(melodic-software/standards#388) — so environment behavior changes land
by reviewed PR instead of hand-edits to an account-scoped UI field.

## Fix

- **Step 1 network access**: the Custom allowlist (default package
managers + `dot.net`, `aka.ms`, `builds.dotnet.microsoft.com`,
`download.visualstudio.microsoft.com`) is now documented as the
requirement, not a fallback — #2654 Blocker 1 reproduced the .NET
installer's `403` under Trusted, overturning the 2026-08-13 probe-based
optimism.
- **Setup script section**: embedded script replaced by the bootstrap +
pointers to the standards component and its README; documents the
per-step timestamped log (`/var/log/melodic-env-setup.log`), the
completion stamp (`/opt/melodic-env-setup.done`, written strictly last),
and the cache-rebuild lifecycle (a merged standards change reaches
environments only on a forced rebuild or ~7-day expiry).
- **Verification checklist**: new item 0 checks the stamp first — a
missing stamp is the #2654 Blocker 2 signature (cache build interrupted
~13 s in; PowerShell and the baked-in bootstrap never ran). The .NET
item now points at confirming the allowlist rather than "applying the
fallback".
- **Findings + audit table**: the "bootstrap is unwired" finding is
marked resolved — #2631 registered the SessionStart hook and enabled the
65-plugin catalog, #2657 closed the cp311 pyyaml hash gap the hook died
on (verified against PyPI's published digests; coverage gap, not
tampering). Remaining #2654 actions are environment-side (apply the
allowlist, rebuild the cache, re-run the checklist).

## Verification

- `markdownlint-cli2 docs/CLOUD-FLEET-SETUP.md` — 0 issues
- All referenced anchors
(`#step-1--the-shared-environment-claudeai-ui-one-time`,
`#verification-checklist`) unchanged and resolvable
- Claims cross-checked against the primary sources: #2654 (live run
forensics), #2631 / #2657 diffs on `main`, and the component content in
melodic-software/standards#388

## Related

- Refs #2654 — the verification report this folds in (left open: the
environment-side actions — Custom allowlist + cache rebuild + checklist
re-run — are still pending)
- Refs #2631, #2657 — the repo-side fixes the Findings section now
records
- Refs melodic-software/standards#388 — the canonical setup script this
guide now bootstraps from
- Refs #2613 — the original fleet guide PR this follows up

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx

---
_Generated by [Claude
Code](https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx)_

Co-authored-by: Kyle Sexton <kyle-sexton@users.noreply.github.com>
…skip case

Addresses both review findings on this PR:

- Codex P2: the contract test was not executed by CI — every run-tests.sh
  invocation in ci.yml names explicit files, so a stamp-ordering or
  bootstrap-URL regression could merge green. Adds a cloud-environment lane
  that runs it, wired into the ci-status aggregate gate (both the needs list
  and the results expression, per actionlint).
- Claude review nit: the hook-baking step was the one step with no else
  branch, so a missing "baked" line could not be told apart from the CWD not
  being the repo checkout. Logs the skip explicitly, matching the script's
  every-step-logs contract.

Verified: shellcheck -x -P SCRIPTDIR clean, run-tests.sh 1 passed, actionlint
clean on ci.yml.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx
…ugins (#391)

Run the checked-out repo's committed .claude/cloud-bootstrap.sh as the
setup script's final repo step (CLAUDE_CODE_REMOTE=true,
CLAUDE_PROJECT_DIR at the checkout root; one name, no fallback paths),
then a generic data-driven plugin stage: register extraKnownMarketplaces
entries and install enabledPlugins entries from the checkout's
.claude/settings.json, best-effort, skipping cleanly when claude/jq or
plugin keys are absent. Claude Code builds its plugin registry at
process start and never re-reads it, so installs must be snapshot-baked
to load at a session's first turn. The README documents the frozen
calling contract: exact entry-point path and env vars, best-effort and
exit-0 semantics, and the warm-cache vs correctness-guarantee division.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01UHbdXDm8B4yj81uQBy8M3M
@kyle-sexton
kyle-sexton merged commit 912fda4 into main Aug 15, 2026
45 checks passed
@kyle-sexton
kyle-sexton deleted the feat/cloud-environment-component branch August 15, 2026 05:57
kyle-sexton added a commit that referenced this pull request Aug 15, 2026
…390)

## Summary

Cloud sessions (web, `claude --cloud`, mobile, routines) start on a
fresh Ubuntu VM shipping Node 20/21/22 with no `node_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-version` via the VM's nvm, then `npm ci` when 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 SCRIPTDIR` clean under this repo's `.shellcheckrc`
- `.claude/settings.json` parses

Not 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 existing `npm ci --prefix distribution`
flow rather than being synced speculatively on every session.

## Related

- Refs #388 — the shared cloud-environment component this repository
owns (the other half of the same rollout)
- Refs melodic-software/claude-code-plugins#2654 — the live verification
run this responds to
- Refs melodic-software/claude-code-plugins#2668 — the fleet guide whose
hook template this follows
- Refs melodic-software/medley#1818 — the sibling rollout carrying the
tested reference implementation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx

---
_Generated by [Claude
Code](https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx)_

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
kyle-sexton added a commit to melodic-software/ci-workflows that referenced this pull request Aug 15, 2026
No linked issue

## Summary

Cloud sessions (web, `claude --cloud`, mobile, routines) start on a
fresh Ubuntu VM with **no .NET at all**, so a session here cannot build
or analyze the .NET surface these actions target. The fleet-wide
verification run confirmed this class of failure
(melodic-software/claude-code-plugins#2654).

This is the last of six repos in that rollout: the shared environment
installs a generic warm cache, and each repo owns its exact pins in a
committed, cloud-guarded SessionStart hook — the environment cannot read
a repo it isn't running in, so only the hook can honor `global.json`.

## Changes

- **`.claude/hooks/cloud-bootstrap.sh`** (new) — the exact `global.json`
SDK (10.0.400) into a repo-local `.dotnet`, with `DOTNET_ROOT` and PATH
exported through `$CLAUDE_ENV_FILE` (dedup-guarded, since SessionStart
fires on resume as well as startup).
- **`.claude/settings.json`** — registers it ahead of the existing
plugin-install hook.
- **`.gitignore`** — `.dotnet/`, so the install never appears as a large
untracked tree.

Cloud-only (`CLAUDE_CODE_REMOTE=true`), idempotent, warn-and-continue.
**Local sessions are unaffected** — the guard exits 0 before the hook
does anything.

Two hardening details are carried over from review of the sibling PRs
rather than rediscovered here:

- the download is validated with a size **and shebang** check before it
is executed, because the cloud egress proxy can return an error body
with HTTP 200 from `dot.net` and `curl -f` only trips on HTTP ≥ 400;
- the installed-version check uses `grep -qF` — the version is a literal
whose dots would otherwise act as regex wildcards.

A failed fetch names the four hosts an environment must allowlist: that
install is 403-blocked under Trusted network access (#2654 Blocker 1).

## Verification

- `shellcheck -x -P SCRIPTDIR` clean under this repo's `.shellcheckrc`
- `shfmt -d` clean under this repo's `.editorconfig` (note: this repo's
continuation style is the opposite of medley's, which sets
`binary_next_line` — formatted per this repo)
- `.claude/settings.json` parses; `git check-ignore` confirms `.dotnet/`
is ignored

Not claimed: I have not run this hook inside a live cloud session on
this repo. Its logic is the fleet template, exercised by a 12-case suite
in melodic-software/medley#1818 — including the SDK install-and-export
path and the blocked-download warn path. This repo has no shell test
harness to host that suite.

## Related

- Refs melodic-software/claude-code-plugins#2654 — the live verification
run this responds to
- Refs melodic-software/claude-code-plugins#2668 — the fleet guide whose
hook template this follows
- Refs melodic-software/standards#388 — the canonical shared-environment
setup script this pairs with
- Refs melodic-software/medley#1818 — the sibling rollout carrying the
tested reference implementation

🤖 Generated with [Claude Code](https://claude.com/claude-code)

https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx

---
_Generated by [Claude
Code](https://claude.ai/code/session_01AXaqhrFZeGTzbCJun12Ngx)_

Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
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.

1 participant