flow run: a declined confirmation stops the flow; --apply --yes confirms write steps

confirm() prints 'declined: <reason>' and exits 3 whenever the answer is no or nobody can answer (not a
terminal without --yes, serve without the owner's dialog), so no command reports success after declining.
flow run stops at such a step with 'step N declined: <reason>' (status declined), never ok. flow run
--apply --yes passes --yes to write steps only; --yes without --apply is refused. Pitfall 24.
This commit is contained in:
CRTkafa 2026-10-12 08:44:56 +08:00
parent 5fbb9e63e3
commit 366362d62b
12 changed files with 116 additions and 19 deletions

View file

@ -46,7 +46,11 @@ once (the second build step prints "prebuild skipped (already ran in this flow r
changes. Show this output to the owner.
- `--dry`: the plan only, nothing runs.
- `--apply`: writes and shell steps run for real, **only after the owner agreed in chat**; each write command still
asks its own confirmation in the terminal. Never add `--yes` (flow validate refuses it).
asks its own confirmation in the terminal. Never add `--yes` to a step (flow validate refuses it).
- `--apply --yes`: the owner's approval of every write step of this run (they get `--yes`; reads, waits and owner
steps do not). Only when the owner said so in chat for this run, e.g. a background run with a log where no terminal
can answer. Without `--yes` and without a terminal, a confirming command declines: the flow stops there with
"step N declined: not a terminal and no --yes ...", never "ok" (status `declined` in `--json` and the state file).
- Owner steps never run in any mode: submit, release, phased release, rollout, sending review replies, price
imports, inviting testers, Beta App Review, starting experiments, offer codes (AGENTS.md rule 1). They print as
"YOUR CLICK" with the exact text, also when written as `run:`.

View file

@ -1,6 +1,6 @@
---
name: store-pitfalls
description: Known pitfalls with App Store Connect, Google Play and storebound itself, learned from real releases, and what to do about each - store lists that lag right after a create, dry runs that print a count instead of a plan, App Store names taken on a storefront, keyword limits in bytes, Data safety answers without code evidence, missing font glyphs in screenshots, console sign-in and sessions, Capacitor monorepo plugins, duplicates from retries, re-runs and parallel runs, App Store search ranks that are only a trend, Play without a search API, Play's Create app form (no API, opener and submit share a name, Free is final), App Privacy purposes over-mapped (Play advertising is Third-Party Advertising only), an unknown console page (bounded wait and a debug dump), a signed-in console read as a sign-in page (Google account widget frames), console forms driven agent-first, stale Capacitor web assets. Read it before any store task, and when a store command does something you did not expect.
description: Known pitfalls with App Store Connect, Google Play and storebound itself, learned from real releases, and what to do about each - store lists that lag right after a create, dry runs that print a count instead of a plan, App Store names taken on a storefront, keyword limits in bytes, Data safety answers without code evidence, missing font glyphs in screenshots, console sign-in and sessions, Capacitor monorepo plugins, duplicates from retries, re-runs and parallel runs, App Store search ranks that are only a trend, Play without a search API, Play's Create app form (no API, opener and submit share a name, Free is final), App Privacy purposes over-mapped (Play advertising is Third-Party Advertising only), an unknown console page, a signed-in console read as a sign-in page (Google account widget frames), console forms driven agent-first, stale Capacitor web assets, a declined flow step logged ok. Read it before any store task, and when a store command does something you did not expect.
---
# Store pitfalls
@ -283,3 +283,16 @@ that fails without the fix (see CONTRIBUTING.md).
once failed with "no Gradle project (settings.gradle) in android or .". The builds, the staleness check and `check`
now find the one Capacitor project one or two levels down (workspaces, apps/*, packages/*) and name it; several are
listed and need `play.build.dir` / `apple.build.workspace|project` (`capacitorProject` in `cli/lib/web-assets.ts`).
## 24. A flow step that declined its confirmation was logged "ok"
- **Symptom:** `flow run release --apply` ran `storebound apple upload --no-wait`, which printed "not a terminal:
re-run with --yes to confirm" and uploaded nothing, yet the flow logged "4/13 upload: ok" and went on to
`apple testflight wait` for a build that was never uploaded.
- **Cause:** a declined confirmation (`confirm()` in `cli/lib/ctx.ts`) returned from the command with exit 0, and
`flow run --yes` was not passed on to the steps, so a run without a terminal could never confirm.
- **Do:** read a step's output, not only its status. For a run without a terminal the owner can approve the writes
with `flow run <name> --apply --yes` (only on their word, per run); otherwise run the flow in a terminal.
- **Guard:** every declined or unanswerable confirmation prints `declined: <reason>` and exits 3 (`DECLINED_EXIT`);
`flow run` stops with "step N declined: <reason>" (status `declined`, never `ok`) and adds `--yes` to write steps
only with `--apply --yes` (`planStep` in `cli/lib/flow.ts`, `declinedReason` in `cli/flow.ts`).
`tests/flow.test.ts` (non-TTY flow with a confirming step and no `--yes` fails at that step with "declined").