fix(deploy): fail fast on non-TTY confirmation; correct login and version guidance - #87
Merged
Merged
Conversation
Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
kylegani
force-pushed
the
kyle/phase0-agent-safe-cli
branch
from
August 4, 2026 09:53
cb5f401 to
28db3d1
Compare
kylegani
marked this pull request as ready for review
August 4, 2026 12:49
wesrobin
approved these changes
Aug 4, 2026
This was referenced Aug 23, 2026
kylegani
added a commit
that referenced
this pull request
Aug 24, 2026
**Stack position: 2 of 2.** Stacked on #102 — this diff is against it, not `main`. ## Problem Every request already carries `X-Source: cli` and `X-CLI-Version`, but nothing says *what invoked the CLI*. An agent-driven deploy and an engineer at a terminal are indistinguishable, so deploy-side agent metrics can only split on the calling principal: currently around 32% of deploying principals are automation against around 70% of build volume, and that gap is one undifferentiated blob of service accounts belonging to CI, to coding agents, and to internal tooling. ## Change `internal/clientenv` classifies the invoking environment into one of three shapes, emitted as `X-Client-Env`: - `agent:<name>` for a detected agent - `ci` for a CI system (`CI`, plus 13 vendor variables) - `interactive` otherwise Wired onto the JSON API client, the multipart `RunApp` path and both device-auth endpoints. Computed once per process. The agent matrix mirrors [vercel/detect-agent](https://github.com/vercel/detect-agent) `agents.json` (schema version 1): 20 agents, its identifiers verbatim (including its mixed `claude_code` and `cursor-cli` styles) and its array order, since order decides which of two simultaneous markers wins. `TestAgentDetectorsMatchRegistryOrder` pins both, so a drifting sync fails instead of silently reclassifying traffic. The registry promotes `AI_AGENT` as the cross-vendor self-declaration variable, which we honour. Two deliberate narrowings, both flagged in the code: - **`replit` is gated on the absence of a TTY.** `REPL_ID` is set for every process on Replit including a human in the editor, so bare `REPL_ID` evidences the host, not an agent. This is the registry's own stated reasoning for `kiro` ("set by both the IDE terminal and the CLI agent, so gate on `no_tty` to avoid misdetecting a human at the integrated terminal") applied to the variable with the same problem. Following the registry literally here would have put humans in the agent bucket, which is the exact overcount this header exists to avoid. - **`AI_AGENT` is evaluated first**, not as a fallback, so an operator wrapping a known agent can name their own harness and have that win. Detection never influences behaviour: no output default, no prompt suppression, nothing branches on it. That is why the non-TTY deploy guard from #87 keys off stdin rather than agent detection. `Detect` takes its environment, filesystem and TTY probes as parameters, so the matrix is tested without touching the real environment. `AI_AGENT` values are caller-supplied, so they are lowercased, restricted to `[a-z0-9._-]` and capped at 64 characters. A value that sanitizes to nothing becomes `agent:unknown` rather than degrading to `interactive`, keeping a declared-but-unparseable agent in the agent bucket. ## Not in scope The header is only *stored* once dashboard-backend #4132 lands; until then it is accepted and discarded. `X-` prefixes are discouraged for new headers by RFC 6648, but this joins an existing `X-Source` / `X-CLI-Version` / `X-Client-OS` family and consistency won. ## Test `go test ./internal/clientenv/... ./internal/api/... ./internal/auth/...` green: 58 detection cases (one per registry variable, both TTY branches for the two host-gated agents, the three ordering rules, the `AI_AGENT` sanitizer and the CI matrix), plus header-presence tests on the API client and both OAuth endpoints. `gofmt` clean. `golangci-lint run ./internal/clientenv/...` reports 0 issues; on the other touched packages the single finding (`nilerr` in `internal/auth/docker.go`) is pre-existing, verified by re-running against a stash. Repo-wide `make lint` and `go build ./...` fail on vendored `cortex/server/node_modules` AWS CDK templates, unrelated and also failing on `main`. 🤖 Generated with [Claude Code](https://claude.com/claude-code) --------- Co-authored-by: Claude Opus 5 <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Context
Part of Phase 0 of the Agentic Cerebrium initiative: making Cerebrium reliably drivable by coding agents. Agents and CI drive this CLI headlessly (piped stdin, no TTY). Today the deploy confirmation silently auto-confirms on stdin EOF and hangs forever on an open pipe, and two guidance messages point at an env var and a command that do not exist. Bad guidance is amplified with agents: they follow error messages literally.
This PR is layer 2 of a 2-PR chain (merge in order; the upper PR auto-retargets to main when the lower merges):
(Chained bases rather than a native GitHub stack: the stacked-PR public preview is not enabled on this repository.)
Phase 0 CLI track, layer 2: deploy confirmation hardening. Based on kyle/phase0-cli-guidance (#88), which carries the login/version guidance fixes that were previously part of this PR.
Changes
fmt.Scanlnmeant EOF on stdin silently auto-confirmed a deploy, and an open non-TTY pipe blocked forever. An agent or CI piping intocerebrium deploycould ship to prod by accident.StdinIsTTYadded toDisplayConfig(mattn/go-isatty, already a dependency, same pattern as the existing stdout detection). When stdin is not a TTY and confirmation was not disabled, deploy fails fast before any side effects:unable to prompt for confirmation: stdin is not a TTY. Re-run with -y/--yes to skip confirmation(clig.dev / Azure CLI convention).--no-coloron a real terminal still prompts.fmt.Scanlnreplaced byreadConfirmationResponse(io.Reader): EOF or read error is a decline, never consent. Plain Enter still defaults to yes on a live TTY.--yesadded as a visible alias of--disable-confirmation(-yshorthand preserved), resolved through a testableconfirmationDisabled(flags)helper.How to test this layer
go test ./internal/commands/... ./internal/ui/commands/...Test_validateConfirmationPrompt: TTY/non-TTY x confirmation required/disabled matrix, asserts the error names -y/--yes.Test_confirmationDisabled: 5 flag combos including -y and --yes.TestReadConfirmationResponse: 10 cases, EOF and read-error decline paths included.Full suite
go test ./...passes.go build -o /tmp/cerebrium-test ./cmd/cerebrium && echo | /tmp/cerebrium-test deployfails fast, exit 1, error names -y/--yes./tmp/cerebrium-test deploy < /dev/nullsame fail-fast (previously auto-confirmed)./tmp/cerebrium-test deploy -yand--yesand--disable-confirmationall skip the prompt.cerebrium deployin a real terminal still prompts, including with--no-color; Ctrl-D at the prompt aborts (previously confirmed);deploy --helpshows both --yes and --disable-confirmation.Notes
go mod tidypromoted spf13/pflag to a direct dependency (now imported by the deploy command).Stack position: 2 of 2 (kyle/phase0-cli-guidance -> kyle/phase0-agent-safe-cli). Merge #88 first, then retarget this PR to main (or let GitHub retarget on branch deletion).
🤖 Generated with Claude Code