What a Documentation Fix Taught Me About Working With an AI Coding Sandbox
It started as a small task: get the GitHub CLI installed automatically for cloud coding sessions against a personal Next.js/Payload project, document why, and move on. It ended up surfacing a handful of lessons about cloud AI sandboxes, GitHub's proxy layer, and — more usefully — about how documentation drifts away from a codebase without anyone noticing. None of this required anything exotic; it just required paying attention when things behaved strangely instead of assuming they were fine.
A cloud sandbox is not one thing
The first surprise was realizing that "run Claude in the cloud" isn't a single product with one set of rules. A throwaway cloud session (the kind you get from a chat-style Cowork session) is fully ephemeral — nothing installed in it persists, and nothing about it can be pre-configured ahead of time. A Claude Code on the web session (claude.ai/code), by contrast, is built around a real, named, persistent Environment: a network access level, environment variables, and a setup script that runs once and gets cached for roughly a week. That's the actual place to put something like apt install -y gh if you want it to stick around.
The practical takeaway: if a cloud AI tool feels like it should support "install this once, keep it forever," check whether the product actually has a first-class concep t for that before assuming a throwaway session can be talked into behaving like one.
GitHub access through a proxy is not all-or-nothing
Cloud sessions get GitHub credentials injected automatically — no gh auth login required. But that convenience isn't uniform. REST API calls (gh api ...) and plain git clone/push over HTTPS went through fine, authenticated as the real account. GraphQL calls and gh auth status's own self-check did not — they hit a narrower, explicitly gated set of operations. And in one case, an entire repository was blocked outright, with an error message pointing at a remediation step (add_repo) that, per the platform's own open issue tracker, doesn't actually have a working implementation yet in that product surface.
The lesson here isn't "the tool is broken" so much as "verify the specific failure instead of generalizing from it." A blocked GraphQL call doesn't mean the whole integration is broken; a blocked repository doesn't mean the whole GitHub connection is broken. Each of those turned out to be a distinct, narrower constraint, and conflating them would have led to the wrong fix each time.
Two ways to bootstrap a session, two different costs
Once gh needed to be available automatically for future sessions, there were two honest options: a repo-level SessionStart hook (.claude/settings.json + a shell script) or an environment-level cached setup script. They look similar but trade off differently. A SessionStart hook lives in the repo, travels with it regardless of which environment starts the session, and is easy to review in a pull request — but it reruns, uninstalling nothing and reinstalling everything, at the start of every session, which cost about 15 seconds each time. A cached environment setup script only pays that cost once, but it lives outside the repo, tied to a specific environment configuration.
Neither is "correct" in the abstract. The right choice depends on whether portability (anyone who clones the repo and starts a session gets the behavior) matters more than raw startup latency. Worth deciding on purpose rather than by default.
The bridge between a cloud assistant and a real laptop has real edges
The most time-consuming part of this whole project wasn't the documentation — it was chasing what looked like a file-corruption bug that turned out not to exist. A file written from the cloud side to a local machine, through a bridge tool, appeared to have reverted itself afterward, not once but twice. The instinctive explanation — a stale editor buffer with autosave enabled clobbering the write — was plausible and worth checking, but wrong.
The actual cause was simpler and, in hindsight, should have been suspected sooner: the read-back used to verify the write was itself unreliable, serving a stale cached snapshot rather than the file's real current state. The fix wasn't a local investigation at all — it was comparing a SHA-1 hash computed independently on both sides. Once both hashes matched, the "bug" evaporated.
The general lesson: when a tool is used to both write something and verify that write, and the verification comes back wrong, don't assume the write failed. Consider that the verification path might be the unreliable half, and check via an independent, ground-truth method — a hash, a checksum, a second tool — before spending more time debugging a problem that might not exist.
Documentation drifts quietly, and it drifts in specific, findable ways
Underneath all of the tooling detours, the actual point of the exercise held up: a README that was accurate when written had quietly gone stale as the code moved on, in ways that were entirely findable by just reading the current source next to the current docs. A rendering approach had been fully replaced by a different one, but the README still described the old one in detail — including a whole paragraph explaining an implementation that no longer had any callers outside its own test file. A feature (on-demand cache revalidation) had been implemented, while the README confidently stated the opposite. A dependency (a cloud storage plugin) was already wired in and working, while the docs listed it under "not done yet."
None of these were subtle bugs. They were the ordinary, expected result of code changing faster than the documentation describing it — the kind of drift that accumulates invisibly because nothing forces a README to fail loudly when it stops being true. The fix isn't a one-time cleanup; it's treating "does this documentation still match this code" as a question worth asking again periodically, the same way a test suite gets rerun rather than trusted forever after it once passed.
The meta-lesson
Across all of it, the throughline was the same: verify claims — mine, a subagent's, a platform's error message, a tool's own read-back — against an independent source before acting on them further. A subagent's research got double-checked against the actual GitHub issue before being relayed as fact. A "file reverted" theory got tested against a hash comparison before triggering a third blind rewrite. A README's claims got checked against the actual, current source files rather than trusted as still accurate. None of that required special tooling — just a habit of treating a first plausible explanation as a hypothesis to test, not a conclusion to act on.