# Session 0044.0 — Transcript > Date: 2026-05-29 > Goal: Isolate this machine's multiple Google Cloud stacks so OHM/flotilla > gestures can never accidentally drive the wrong account/project — first as > operator discipline (a named gcloud config + CLAUDE.md instructions), then > as an enforced guard in flotilla itself. > > Outcome: **Shipped flotilla v1.4.0 (gcloud config guard).** Created an > inactive `wiggleverse` gcloud config; added the `CLOUDSDK_ACTIVE_CONFIG_NAME` > discipline to CLAUDE.md; added a per-deployment `gcloud_config` record field > (migration 006) + `ohm_rfc_app_flotilla/gcloud.py` guard enforced at both > `run_deploy` entry and `ssh.run_remote`; set the live OHM record to > `gcloud_config=wiggleverse`. 214 tests pass. Merged to main, tagged v1.4.0, > pushed. No OHM app redeploy (operator-tool code only). > > Highlights / §19.2 candidates surfaced: > - flotilla relies on env *inheritance* for the config pin — it doesn't set > `CLOUDSDK_ACTIVE_CONFIG_NAME` itself. Candidate: have it assert-or-set. > - `gcloud compute ssh` pins `--project` but not `--account`. Candidate: > store the operator account in the record and pass `--account` too. > - Secret Manager calls use ADC, which `CLOUDSDK_ACTIVE_CONFIG_NAME` does not > select — the guard is scoped to the gcloud-subprocess path by design. > > Timing: the session ran ~30 minutes (start ~07-27, end 07-57 PST). The > exact start minute was not auto-recorded; 07-27 is derived from the > operator-confirmed ~30-minute duration against the real 07-57 end. --- ## Pre-session state - **flotilla** (`ohm-rfc-app-flotilla`): `main` at `242bb3a` (= `origin/main`), last release tag `v1.3.0` (`fb9bd04`). Working tree clean. 203 tests green. - **OHM live**: unchanged this session — no deploy performed. The local deployment record (`~/.ohm-rfc-app-flotilla`) had one row, `ohm-rfc-app`, `target_vm_project=wiggleverse-ohm`, `gcloud_config=NULL` (column did not exist yet). - **gcloud configs on the machine** at open: - `benstull-infra` (ACTIVE) → `ben.stull@wiggleverse.org` / `wiggleverse-ohm` - `benstullbets` → `ben.stull@benstullbets.com` / `benstullbets` - `default` → `ben@wiggleverse.org` / `wiggleverse-rfc` - **In-flight sessions** per the shared repo at claim time: `0026.0` and `0027.0` (both `--INPROGRESS` from 2026-05-28T13-46, ~18h stale; neither touches flotilla, so the shared checkout was safe to work in directly). --- ## Turn-by-turn arc ### Arc 1 — The original ask: a named config + instruction discipline The operator runs more than one Google Cloud stack on this machine and wanted process-scoped isolation: always select the config via `CLOUDSDK_ACTIVE_CONFIG_NAME=xyz` (which never touches the shared on-disk active-config pointer) plus belt-and-suspenders explicit `--project`/`--account`. Inspected the existing configs/accounts and how flotilla invokes gcloud: `ssh.py` already passes `--project` from the deployment record; Secret Manager uses ADC; nothing pinned the account or a config. So the env-var discipline was exactly the missing identity pin. The two named stacks didn't map cleanly to the existing config names, so I asked the operator (AskUserQuestion) for the account+project mapping per config. Answers: **wiggleverse** → `ben.stull@wiggleverse.org` / `wiggleverse-ohm`; **benstull** → *"don't worry about this one — I'm setting it up in a different session."* So this session created only the `wiggleverse` config. Created it inactive and populated it without disturbing the active pointer: ``` gcloud config configurations create wiggleverse --no-activate CLOUDSDK_ACTIVE_CONFIG_NAME=wiggleverse gcloud config set account ben.stull@wiggleverse.org CLOUDSDK_ACTIVE_CONFIG_NAME=wiggleverse gcloud config set project wiggleverse-ohm CLOUDSDK_ACTIVE_CONFIG_NAME=wiggleverse gcloud config set disable_usage_reporting true ``` Verified `benstull-infra` stayed `IS_ACTIVE=True`. Added an "Always pin the gcloud config" section to `CLAUDE.md` codifying the discipline (env var on every gcloud call and on `flotilla` invocations so subprocesses inherit it; hard "never mutate the shared active config" rule; read-back + `--no-activate` escape hatches). Did **not** rewrite the operator-guide's many gcloud commands (they already pin `--project`); flagged that as optional. ### Arc 2 — The operator opted into the enforced guard I noted flotilla doesn't *assert* the config, only inherits it, and offered a code-level guard. Operator: **"yes."** Key design decision (two-layer rule): the config name `wiggleverse` is an OHM-specific value, so it must live in the §13 deployment record, **not** be hardcoded in the CLI. Implementation: - **Migration `006_gcloud_config.sql`** — nullable `gcloud_config TEXT` on `deployments`. `NULL` = guard off (backward compatible; generic / flotilla-core deployments unaffected). - **`ohm_rfc_app_flotilla/gcloud.py`** — `check_active_config(expected, env)`: no-op when `expected` falsy; else raises `GcloudConfigError` (with the exact `CLOUDSDK_ACTIVE_CONFIG_NAME= ` remediation) on mismatch *or unset*. Failing on unset is deliberate — forces the explicit per-process pin. - **Two-layer enforcement**: `run_deploy` checks at gesture entry (before a `deploys` row opens → clean refusal, no orphan row) and re-raises as `DeployError`; `ssh.run_remote` re-checks per call (the literal `gcloud compute ssh` chokepoint) via a new `gcloud_config` field on `SshTarget`. `--dry-run` (which goes through `assemble_plan`, not `run_deploy`) stays unguarded. - **registry**: `Deployment.gcloud_config` field, row mapping, `add_deployment` param, `UPDATABLE_FIELDS`. **cli**: `deployment add/update --gcloud-config` (empty string clears to NULL), and `gcloud_config` added to `deployment show`. Micro-corrections mid-flight (honest record): - Importing the `gcloud` module into `ssh.py` and `deploy.py` shadowed local variables named `gcloud` (the gcloud *binary* path). Renamed those locals to `gcloud_bin` in both files. - Initially numbered the new SPEC section §11.5 — but §11.5 is already the v1.3.0 webhook-reads boundary. Renumbered the guard to **§11.6** across code comments, the migration, and SPEC; left the genuine webhook §11.5 refs alone. Tests: new `tests/test_gcloud.py` (5), plus guard cases in `test_ssh.py`, `test_deployment.py`, and `test_deploy.py` (refuse-before-row-opens + proceed-when-matches). Full suite **214 passed**. CLI smoke test on a fresh temp DB confirmed add/show/update-clear round-trips. ### Arc 3 — Coherence, live record, release - **SPEC**: §5.5 (record field), §11.6 (the guard mechanism + ADC caveat + belt-and-suspenders framing), §13.9 (`gcloud_config: wiggleverse` for OHM), and the §12 verb-surface line. - **CHANGELOG** 1.4.0 with an RFC-2119 upgrade-steps block; **VERSION** + **pyproject** → 1.4.0; **register-ohm.sh** now sets `--gcloud-config wiggleverse` on bring-up; **operator-guide** got a §11.6 callout box. - **Live OHM record**: `deployment update ohm-rfc-app --gcloud-config wiggleverse` (migration 006 applied automatically on the read). The guard is now active for OHM — future deploys MUST be prefixed. - **Release**: branched `session-0044/flotilla-gcloud-config-guard`, committed, fast-forwarded `main`, tagged `v1.4.0`, pushed main + tag + branch to origin. --- ## Cut state (end of session) | | | | --- | --- | | flotilla | `5f1689f` on `main` (= `origin/main`), tag **`v1.4.0`**; branch `session-0044/flotilla-gcloud-config-guard` also pushed | | OHM live | **unchanged** — no deploy this session; still on its prior `deploys.id`/version | | ohm-rfc pin | unchanged | | local deployment record | `ohm-rfc-app` now carries `gcloud_config=wiggleverse` (operator-machine state, not a repo change) | | gcloud configs | added inactive `wiggleverse` (`ben.stull@wiggleverse.org` / `wiggleverse-ohm`); shared active pointer untouched (`benstull-infra`) | | Ledger | Status | | --- | --- | | gcloud config isolation — named config + CLAUDE.md discipline | ✅ shipped | | flotilla gcloud-config guard (v1.4.0) | ✅ shipped (merged, tagged, pushed) | | `benstull` gcloud config | ⏸ deferred — operator handling in a separate session | --- ## §19.2 candidates surfaced 1. **flotilla self-asserting vs. self-setting the config.** The guard relies on the *parent* process exporting `CLOUDSDK_ACTIVE_CONFIG_NAME` (gcloud subprocesses inherit it). flotilla could instead *set* it from the record when it shells out — but that would mask the guard's intent (the point is to force the operator/assistant to be explicit). Worth a deliberate call. 2. **`--account` pin on `gcloud compute ssh`.** Today only `--project` is passed; the config name carries the account. Storing the operator account in the record and passing `--account` would be true belt-and-suspenders. 3. **ADC identity isolation.** `secret set`/`bind` and deploy-time secret reads use ADC, which `CLOUDSDK_ACTIVE_CONFIG_NAME` does not select. If multi-stack ADC ever becomes a real hazard, that needs its own mechanism; for now the project is always passed explicitly on Secret Manager calls. --- ## What lands on the operator's plate 1. **Set up the `benstull` gcloud config** in the other session (deferred here by operator request). Mirror the gesture used for `wiggleverse`: `gcloud config configurations create benstull --no-activate` then populate with env-scoped `gcloud config set …`. 2. **Every OHM deploy/ssh gesture now MUST be prefixed** `CLOUDSDK_ACTIVE_CONFIG_NAME=wiggleverse …`, or it refuses fast (no orphan `deploys` row). This is the intended behavior, but it changes the muscle memory. 3. (Optional) Wire a Claude Code `SessionStart`/env hook to export the var for OHM sessions automatically, if hand-prefixing proves annoying. --- ## Prompt the operator can paste into the next Claude Code session ``` You're an OHM driver session anchored in ohm-rfc-app-flotilla. Claim your own session ID first (~/git/ohm-infra/scripts/claim-session-id.sh --start ); the number in this prompt is advisory. Context from session 0044: flotilla v1.4.0 shipped a gcloud config guard. The OHM deployment record now carries gcloud_config=wiggleverse, so EVERY deploy/ssh gesture against OHM must be prefixed CLOUDSDK_ACTIVE_CONFIG_NAME=wiggleverse — otherwise flotilla refuses before opening a deploys row. An inactive `wiggleverse` gcloud config (ben.stull@wiggleverse.org / wiggleverse-ohm) exists; the shared on-disk active pointer (benstull-infra) was left untouched. flotilla is at main=5f1689f, tag v1.4.0, pushed to origin. 214 tests green. No OHM app redeploy was needed. Open items you might pick up: - The `benstull` gcloud config still needs creating (operator was handling it separately — confirm before assuming it's done). - §19.2 candidates from 0044: (a) should flotilla self-set CLOUDSDK_ACTIVE_ CONFIG_NAME rather than only assert it? (b) pin --account on gcloud compute ssh, not just --project? (c) ADC identity isolation for Secret Manager calls. - Otherwise consult /Users/benstull/projects/wiggleverse/ohm-rfc/ROADMAP.md for the next substantive item. Don't paste secret bytes into the conversation (SPEC §3 invariant 1 / CLAUDE.md). ```