Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
16 commits
Select commit Hold shift + click to select a range
b786006
docs: add implementation plan for gcloud-robot-obvious-path
danshapiro Sep 21, 2026
18d8956
docs: load-bearing stage corrections for gcloud-robot-obvious-path plan
danshapiro Sep 21, 2026
3bb72be
docs: plan-review round 1 remediation for gcloud-robot-obvious-path
danshapiro Sep 21, 2026
d7dbef8
docs: plan-review round 2 remediation for gcloud-robot-obvious-path
danshapiro Sep 21, 2026
e22d9c7
docs: plan-review round 3 remediation for gcloud-robot-obvious-path
danshapiro Sep 21, 2026
26b5bd1
test: repair cloud-exec-id-parse fake gcloud logging read stub (pre-e…
danshapiro Sep 21, 2026
0375524
feat(cloud): discover well-known gcloud-robot installs in the identit…
danshapiro Sep 21, 2026
675ff45
test: repair cloud-run-dockerfile reporter invocation (pre-existing r…
danshapiro Sep 21, 2026
f6fd5f2
feat(cloud): fail-fast identity for agent cloud lanes - TTY-gated pro…
danshapiro Sep 21, 2026
92dce2b
test: delete obsolete cloud-vitest-integration suite (pre-existing re…
danshapiro Sep 22, 2026
7e92df0
feat(cloud): report resolved identity/source and loud dirty-tree stat…
danshapiro Sep 22, 2026
0ac5566
docs(cloud): recommend GCLOUD_ROBOT_REQUIRE for agent gates; document…
danshapiro Sep 22, 2026
9394024
docs(cloud): qualify bootstrap-robot.sh pointer to the skill install …
danshapiro Sep 22, 2026
a5e9cfb
fix(cloud): loud dirty-tree warning on the direct build path (delta r…
danshapiro Sep 22, 2026
a3770a0
test: restore cloud-vitest-integration dispatch checks minus obsolete…
danshapiro Sep 22, 2026
1e00de9
docs(cloud): troubleshooting points at real observables for a failed …
danshapiro Sep 22, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
21 changes: 13 additions & 8 deletions AGENTS.md
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ Proposed package-manager migration: [pnpm migration plan](docs/plans/2026-09-19-
## Test Coordination
- Broad repo-supported test runs wait for the shared coordinator gate; if another agent holds it, wait rather than kill a foreign holder.
- Pre-worktree green-base checks (and any broad gate intended to validate `origin/main` itself, as opposed to a branch under test) go through `scripts/base-gate.sh` (e.g. `scripts/base-gate.sh test`), which runs the command from a clean scratch worktree at `origin/main`. The main checkout accumulates untracked litter; the cloud runners treat that as a non-addressable `-dirty` image and pay a ~13 min cold rebuild every time, whereas a clean worktree uses the content-addressed commit tag — built at most once per commit and shared by every later run.
- Agent-launched broad gates should export `GCLOUD_ROBOT_REQUIRE=1` (the recommended default): fail closed when no robot identity resolves, instead of silently running as a possibly-stale human identity. Machines with a standard gcloud-robot install don't need `GCLOUD_ROBOT_HOME` exported — the lanes probe the well-known install locations (`~/.codex/skills/gcloud-robot`, `~/.claude/skills/gcloud-robot`, `~/code/skill-gcloud-robot/gcloud-robot`) when it's unset, and non-TTY (agent) invocations disable gcloud prompts and preflight the credential so a dead identity fails in seconds instead of hanging. To guarantee the robot identity itself — rather than the selector's first passing candidate — export `GCLOUD_ROBOT_ACCOUNT=<robot>`: the selector probes that account first, so no other identity (including an ambient human with lane permissions) can win; for PTY-launched agent lanes (Freshell terminal panes, where prompts are deliberately NOT disabled) this also prevents the selector from ever minting the ambient human credential.
- Set `FRESHELL_TEST_SUMMARY` when you want holder/status output to show a human-meaningful reason for a broad run.
- Use `npm run test:status` to inspect the current holder, recent results, and any advisory reusable baseline.
- Use `npm run test:vitest -- ...` for a repo-owned direct Vitest path. Raw `npx vitest` is not a coordinated workflow.
Expand Down Expand Up @@ -180,10 +181,12 @@ npm run test:cloud:build # Build and push the Docker image to Artifact Regist

**Identity:** cloud lanes never require an interactive `gcloud auth login`.
They resolve a gcloud identity lazily, in this order: `--account=` flag >
`FRESHELL_GCP_ACCOUNT` > `GCLOUD_IDENT` > gcloud-robot probe (needs
`GCLOUD_ROBOT_HOME`, the installed gcloud-robot skill directory) > ambient
gcloud (with a one-line stderr note). Provisioning, rotation, and revocation
live in [docs/development/gcloud-robot.md](docs/development/gcloud-robot.md).
`FRESHELL_GCP_ACCOUNT` > `GCLOUD_IDENT` > gcloud-robot probe (via
`GCLOUD_ROBOT_HOME`, or the first well-known gcloud-robot skill install:
`~/.codex/skills/gcloud-robot`, `~/.claude/skills/gcloud-robot`,
`~/code/skill-gcloud-robot/gcloud-robot`) > ambient gcloud (with a one-line
stderr note). Provisioning, rotation, and revocation live in
[docs/development/gcloud-robot.md](docs/development/gcloud-robot.md).
`GCLOUD_ROBOT_REQUIRE=1` fails closed when no robot identity resolves.

### E2E Test Backend (Cloud Run Jobs)
Expand All @@ -204,10 +207,12 @@ npm run test:e2e:cloud # Force cloud

**Identity:** cloud lanes never require an interactive `gcloud auth login`.
They resolve a gcloud identity lazily, in this order: `--account=` flag >
`FRESHELL_GCP_ACCOUNT` > `GCLOUD_IDENT` > gcloud-robot probe (needs
`GCLOUD_ROBOT_HOME`, the installed gcloud-robot skill directory) > ambient
gcloud (with a one-line stderr note). Provisioning, rotation, and revocation
live in [docs/development/gcloud-robot.md](docs/development/gcloud-robot.md).
`FRESHELL_GCP_ACCOUNT` > `GCLOUD_IDENT` > gcloud-robot probe (via
`GCLOUD_ROBOT_HOME`, or the first well-known gcloud-robot skill install:
`~/.codex/skills/gcloud-robot`, `~/.claude/skills/gcloud-robot`,
`~/code/skill-gcloud-robot/gcloud-robot`) > ambient gcloud (with a one-line
stderr note). Provisioning, rotation, and revocation live in
[docs/development/gcloud-robot.md](docs/development/gcloud-robot.md).
`GCLOUD_ROBOT_REQUIRE=1` fails closed when no robot identity resolves.

## Architecture
Expand Down
52 changes: 48 additions & 4 deletions docs/development/gcloud-robot.md
Original file line number Diff line number Diff line change
Expand Up @@ -31,9 +31,12 @@ The repo is in exactly one of two states at any time:
committed, but the robot SA/key do not exist yet. Expected behavior: lanes
run exactly as before under ambient gcloud, with one quiet stderr note
(`gcloud-robot: no probed identity; using ambient gcloud` / `skill not
found ... — using ambient gcloud`). In this state `verify-as-robot.sh`
failing at the key/token-mint rung is the CORRECT result, not a
regression — do not debug it, provision.
found ... — using ambient gcloud`). On machines with a well-known-path
install this state resolves the robot via discovery instead: the selector
runs, and a second stderr note (`gcloud-robot: well-known install at ...
produced no identity`) can appear when the probe fails. In this state
`verify-as-robot.sh` failing at the key/token-mint rung is the CORRECT
result, not a regression — do not debug it, provision.
2. **provisioned and verified** — provisioning below completed and the
verification ladder passed as the robot.

Expand All @@ -49,7 +52,10 @@ fixed order:
no probe, no network.
4. gcloud-robot probe — `$GCLOUD_ROBOT_HOME/scripts/select-gcloud-identity.sh`
picks the first credentialed account passing the lane's live
`testIamPermissions` probe. The robot "just works" wherever its key is
`testIamPermissions` probe; when `GCLOUD_ROBOT_HOME` is unset the lanes
probe the first well-known skill install (`~/.codex/skills/gcloud-robot`,
`~/.claude/skills/gcloud-robot`, `~/code/skill-gcloud-robot/gcloud-robot`)
instead. The robot "just works" wherever its key is
activated; human accounts keep working untouched.
5. Ambient gcloud (default when nothing above resolves), announced once on
stderr. Set `GCLOUD_ROBOT_REQUIRE=1` to fail closed with guidance instead
Expand Down Expand Up @@ -89,6 +95,13 @@ Deliberately NOT brokered, so do not "fix" their absence:
machine refreshes through the same proxy; serving the broker's cached
token to their refresh POSTs would corrupt client credential state.

The identity preflight mints via `oauth2.googleapis.com`, which the gateway
deliberately does not broker: on brokered hosts, a lane whose resolved
identity has a dead LOCAL credential now fails fast at the preflight instead
of succeeding silently via brokered control-plane hosts. Keep the robot key
activated (`$GCLOUD_ROBOT_HOME/scripts/bootstrap-robot.sh`) or pin
`GCLOUD_IDENT` on such machines.

Consequence for operators: gcloud calls on a brokered host from
garageserver run as the robot regardless of the active account — including
admin calls. For IAM/admin operations on those hosts, bypass the gateway:
Expand Down Expand Up @@ -131,6 +144,10 @@ Facts on disk (garageserver):
export GCLOUD_ROBOT_ACCOUNT="gcloud-robot@misc-puttering-project.iam.gserviceaccount.com"
```

On machines with a standard install the `GCLOUD_ROBOT_HOME` export is
optional — the lanes probe the well-known locations in order when
`GCLOUD_ROBOT_HOME` is unset; an explicit export still wins.

### Provision (once per project, human-run — agents never run these)

All commands pin `--account="$GCLOUD_ROBOT_ADMIN_ACCOUNT"` to your operator
Expand Down Expand Up @@ -392,6 +409,33 @@ for immediacy.)
robot is not provisioned (or not activated) on this machine. Provision
(above) or re-login interactively; both work, the point is the robot
cannot be culled.
- A dead resolved identity now fails a lane in seconds at the identity
preflight with the observable signature `[vitest-cloud]/[e2e-cloud]
ERROR: identity preflight failed for <identity> (source: <rung>) - gcloud
auth print-access-token could not mint a token.` — the preflight swallows
gcloud's own output, so the historical raw signature (`There was a problem
refreshing your current auth tokens: Reauthentication failed. cannot
prompt during non-interactive execution`) no longer appears on the
preflight path; it is what the preflight replaced. A lane under a real TTY
with a reauth-required HUMAN credential still blocks interactively on
`Reauthentication required.` / `Please enter your password:` (the prompt
class that produced the multi-hour incident; discovery moves this blockage
EARLIER, inside the resolve, with the selector's output swallowed) — pin
`GCLOUD_ROBOT_ACCOUNT` (the selector probes it first and never mints the
human) or `GCLOUD_IDENT` on PTY-launched agent lanes. A
`gcloud-robot: well-known install at ... produced no identity` note means
a standard install exists but its probe failed. The lane swallows the
selector's own stderr when it runs it (`scripts/lib/gcp-identity.sh`
invokes it with `2>/dev/null`), so that one-line note is the only in-lane
observable; to see the selector's real guidance, run it manually with the
env the lane passes (use the install path from the note):

```bash
GCLOUD_ROBOT_HOME=<install path from the note> \
GCLOUD_ROBOT_PROJECT=misc-puttering-project \
GCLOUD_ROBOT_PROBE_PERMISSION=cloudbuild.builds.create \
bash "$GCLOUD_ROBOT_HOME/scripts/select-gcloud-identity.sh"
```

### CI

Expand Down
Loading
Loading