Skip to content

diff (TTY): in-progress deployment warning is emitted while the Targets block is open and gets overwritten by its redraw #151

Description

@koh-sh

Summary

When apcdeploy diff runs from a terminal against an environment with an in-flight deployment, the "Deployment #N is currently DEPLOYING" warning is partially erased by the Targets renderer. With --parallel and multiple targets, warnings from concurrent goroutines interleave and the block's cursor math stays wrong for the rest of the run.

Details

diff.RunOnTarget calls displayDeploymentWarning after tr.Done(...) (internal/diff/executor.go:101,106) while the orchestrator's shared Targets handle is still open — it is closed only by the defer tg.Close() in internal/batch/orchestrator.go:93-94 after all targets finish.

displayDeploymentWarning (internal/diff/display.go:39-50) writes three raw lines directly to os.Stderr. The documented CONTRACT EXCEPTION in .claude/rules/output-contract.md justifies bypassing Reporter.Warn so the notice survives --silent; it does not account for the interaction with the live TTY renderer.

ttyTargets redraws with \033[<N>A\r\033[J (internal/cli/targets_tty.go:91-97), assuming the cursor sits directly below the block. The injected warning lines shift the real cursor, so:

  • Single target: the final redraw in Close() (targets_tty.go:205-228) moves up N rows into the warning text, clears to end of screen, and reprints the row — erasing the warning's last line ("The diff is calculated against the currently deploying version.") and duplicating the done-summary.
  • Multiple targets with --parallel: other targets' SetPhase/Done/animation ticks keep redrawing while the warning lines sit inside the block's assumed region, corrupting the entire block from that point on; concurrent warnings from separate goroutines can also interleave line-by-line (three separate unsynchronized Fprint calls).

Reproduction

From a TTY, start a slow deployment (e.g. a linear strategy), then run apcdeploy diff -c apcdeploy.yml while it is DEPLOYING.

Design considerations

Constraint to preserve: the warning must remain visible under --silent (that is the reason for the Reporter bypass).

  1. Defer emission until after tg.Close(): collect per-target warnings during the run (alongside batch.PayloadCollector, or in batch.Summary) and print them from cmd/diff.go after the orchestrator returns. Keeps content identical; changes ordering (warning no longer adjacent to its row during the run) and adds a small collection mechanism.
  2. Fold the warning into the Targets row itself (e.g. Done summary suffix — compared against in-flight deployment #N) plus a post-run stderr line for the full sentence. No raw writes while the block is open, but the --silent path still needs the separate post-run line.
  3. Teach the renderer to cooperate: add a Reporter primitive for "raw line emitted below an active Targets block" that repaints the block after writing. Most general (would also serve any future mid-run notices), highest implementation cost.

Option 1 is the smallest change that fixes both the overwrite and the multi-target interleaving.

Activity

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't working

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions