11 KiB
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
wiggleversegcloud config; added theCLOUDSDK_ACTIVE_CONFIG_NAMEdiscipline to CLAUDE.md; added a per-deploymentgcloud_configrecord field (migration 006) +ohm_rfc_app_flotilla/gcloud.pyguard enforced at bothrun_deployentry andssh.run_remote; set the live OHM record togcloud_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_NAMEitself. Candidate: have it assert-or-set.gcloud compute sshpins--projectbut not--account. Candidate: store the operator account in the record and pass--accounttoo.- Secret Manager calls use ADC, which
CLOUDSDK_ACTIVE_CONFIG_NAMEdoes 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):mainat242bb3a(=origin/main), last release tagv1.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-ohmbenstullbets→ben.stull@benstullbets.com/benstullbetsdefault→ben@wiggleverse.org/wiggleverse-rfc
- In-flight sessions per the shared repo at claim time:
0026.0and0027.0(both--INPROGRESSfrom 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— nullablegcloud_config TEXTondeployments.NULL= guard off (backward compatible; generic / flotilla-core deployments unaffected). ohm_rfc_app_flotilla/gcloud.py—check_active_config(expected, env): no-op whenexpectedfalsy; else raisesGcloudConfigError(with the exactCLOUDSDK_ACTIVE_CONFIG_NAME=<name> <command>remediation) on mismatch or unset. Failing on unset is deliberate — forces the explicit per-process pin.- Two-layer enforcement:
run_deploychecks at gesture entry (before adeploysrow opens → clean refusal, no orphan row) and re-raises asDeployError;ssh.run_remotere-checks per call (the literalgcloud compute sshchokepoint) via a newgcloud_configfield onSshTarget.--dry-run(which goes throughassemble_plan, notrun_deploy) stays unguarded. - registry:
Deployment.gcloud_configfield, row mapping,add_deploymentparam,UPDATABLE_FIELDS. cli:deployment add/update --gcloud-config(empty string clears to NULL), andgcloud_configadded todeployment show.
Micro-corrections mid-flight (honest record):
- Importing the
gcloudmodule intossh.pyanddeploy.pyshadowed local variables namedgcloud(the gcloud binary path). Renamed those locals togcloud_binin 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: wiggleversefor 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 wiggleverseon 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-forwardedmain, taggedv1.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
- 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. --accountpin ongcloud compute ssh. Today only--projectis passed; the config name carries the account. Storing the operator account in the record and passing--accountwould be true belt-and-suspenders.- ADC identity isolation.
secret set/bindand deploy-time secret reads use ADC, whichCLOUDSDK_ACTIVE_CONFIG_NAMEdoes 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
- Set up the
benstullgcloud config in the other session (deferred here by operator request). Mirror the gesture used forwiggleverse:gcloud config configurations create benstull --no-activatethen populate with env-scopedgcloud config set …. - Every OHM deploy/ssh gesture now MUST be prefixed
CLOUDSDK_ACTIVE_CONFIG_NAME=wiggleverse …, or it refuses fast (no orphandeploysrow). This is the intended behavior, but it changes the muscle memory. - (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 <now>);
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).