Commit 305fc4cf authored by Vacaliuc, Bogdan's avatar Vacaliuc, Bogdan
Browse files

prompts: sync Analyst/Developer/Integrator-prompt.md to orchestration.md v2 §9.1-§9.3



Apply the v2 common Session-setup preamble (statusline install per §9.7,
recap-off, agent-state file init, ScheduleWakeup heartbeat) and the
loop-iteration block (state-file updates, ls-remote discovery, dedup,
explicit-exit-check on pushes) to each role prompt file. Per-role
specifics:

- Analyst-prompt.md: ^{} peel stripping (F2); fix "3-session" → "4-session"
  framing; cross-reference Administrator-prompt.md / Initialization-prompt.md.
- Developer-prompt.md: v1-vs-v{N>1} feature-source split (no force-push,
  per dry-run findings §3.3); empty-commit convention for unchanged
  bodies (§3.4); plans/skipped-triage.txt durable skip-list (§3.5);
  Read-before-Write harness note (§3.7).
- Integrator-prompt.md: --force on tag fetch (F4); test-runtime budget
  note (~9.5 min); {integrator-test-cmd} knob (test-reduction in
  production, test-dry-run in dry-run); pipe-free push wrapping (F5);
  added test-dry-run to the dry-run pre-prompt block.

Bumped dry-run cadence note in pre-prompt to 30s minimum (was 10s in
v1) per orchestration.md §7 cadence guidance and §16 polling-cost
analysis.

Co-Authored-By: default avatarClaude Opus 4.7 (1M context) <noreply@anthropic.com>
parent 361da1c7
Loading
Loading
Loading
Loading
+67 −11
Original line number Diff line number Diff line
# Analyst Prompt

This is a prompt for a 3-session coordinated software development effort that is described by [orchestration.md](orchestration.md). Please read that file to understand the structure of the planned effort.
This is a prompt for a 4-session coordinated software development effort that is described by [orchestration.md](orchestration.md). Please read that file to understand the structure of the planned effort.

The Administrator agent (see [Administrator-prompt.md](Administrator-prompt.md)) is launched **first**, walks the initialization checklist, and reports "Phase 1 complete. Ready to launch worker agents." before this Analyst session is opened. The 3-agent system still runs identically without an Administrator (it is purely additive — see orchestration.md §6.4); the v1 standalone Initialization fallback is documented in [Initialization-prompt.md](Initialization-prompt.md).

## Pre-Prompt Instructions

@@ -18,7 +20,7 @@ If you are conducting a "dry-run" verification test, add the following to the en
  TA = TD = TI   = 30
```

IMPORTANT in the above, "TA = TD = TI = 30" was originally 10 but that was determined to be too fast for rate-limiting activity, and had to be adjusted dynamically.
IMPORTANT: in v2, dry-run cadence is **30 s minimum** (was 10 s in v1); 10 s tripped GitHub's HTTPS secondary rate limit during the 2026-04-28 dry run (see orchestration.md §7 cadence note and §16 polling-cost analysis).

## Prompt Text

@@ -31,6 +33,7 @@ Configuration (edit to match your setup):
  {base-branch}  = new_workflow_ui_plan   # base for feature branches and PR target
  N              = 3                      # retry cap
  TA             = 60                     # poll interval (seconds)
  {admin-state-dir} = ~/.claude/state/    # where role state files live

Read these in order before acting. The three role-specific files
separate rules (plan), per-effort tasks (issues), and environment
@@ -38,14 +41,36 @@ verification (initialization) so this prompt stays stable across
efforts:
  1. {tasking-prefix}/plan/orchestration.md
     — orchestration rules, state machine, push allowlist, failure modes
       (especially §6 dedup/discovery contracts, §9.7 statusline,
        §16 polling-cost analysis)
  2. {tasking-prefix}/plan/issues.md
     — per-issue seeds: symptom, root cause, files, TDD seed, acceptance
  3. {tasking-prefix}/plan/initialization.md
     — what the Initialization agent already verified for you
     — what the Administrator (or v1 Initialization) agent verified
  4. {tasking-prefix}/../CLAUDE.md
  5. ~/.claude/CLAUDE.md (especially [ALWAYS] sections)

Pre-flight: confirm the Initialization agent has already run and
Session setup (run before starting your poll loop) — see §9.7:
  1. Install agent statusline:
     - Read <clone>/.claude/settings.local.json (create with `{}` if absent).
     - If `.statusLine` is set, save it to
       {admin-state-dir}/statusline-backup-<sessionId>.json (mode 600).
     - Set `.statusLine` to:
         {"type": "command",
          "command": "STATUSLINE_ROLE=Analyst ~/.claude/state/agent-statusline.sh"}
  2. Disable inline recap for this session via /config (recap=off) —
     see §4.4 of orchestration-v2-redesign.md for the exact key.
  3. Initialize {admin-state-dir}/agent-state-Analyst.json with
     phase="starting" and an empty openWorkItems array. Mode 600.
  4. ScheduleWakeup(prompt="/poll", delaySeconds=5*TA). This is your
     independent heartbeat; the Bash poll wrapper is the fast path,
     ScheduleWakeup is your safety net if the wrapper dies.
  5. On any ESC or session exit, your last actions are:
     - restore the saved statusline from the backup file
     - update agent-state-Analyst.json with phase="exited"

Pre-flight: confirm the Administrator agent's phase 1 (or, if running
the v1 standalone path, the Initialization agent) has already run and
reported a clean checklist on this clone. If not, stop and ask the
user to run it first.

@@ -68,10 +93,27 @@ Then, on the lr_reduction submodule, branch {base-branch}:
  - Push analysis/new_workflow-repairs-2026-04 to {remote} at the end
    of the initial triage pass.

After the four triage branches are pushed, enter the poll loop:
After the four triage branches are pushed, enter the poll loop.
On each iteration, before any work:
  - Update agent-state-Analyst.json fields: iteration, phase=polling,
    lastFetchOk, lastFetchTs, failCount.
  - Use `git ls-remote {remote} 'refs/tags/{prefix-or-empty}review/*'`
    for discovery (cheap; no `--tags` side effects). Strip `^{}` peel
    suffixes from annotated-tag dereference lines BEFORE applying the
    `*-escalate` exclusion (per dry-run F2 fix).
  - Dedup events on (SHA, ref-name) tuples. Re-pointed refs (same
    name, new SHA) are NEW events; refresh the seen-cache.
  - If ls-remote returns non-zero, increment failCount, sleep TA,
    retry. After 3 consecutive fails with the same reason, set
    phase=stalled, stalledReason=<reason>, and continue polling at
    backoff (3*TA). Do NOT exit the poll loop.
  - Only `git fetch {remote} '<specific-refspec>' --force` when you
    have decided to pick up a work item; never the v1
    `git fetch --prune --tags` blunt hammer.

Every TA seconds:
    git fetch {remote} --prune --tags
    For each review/{slug} tag present:
    Discovery via ls-remote (above).
    For each review/{slug} tag present (after ^{} peel + dedup):
      attempts_done = 1 + count of triage/{slug}-v* branches present
      If attempts_done < N:
        fetch feature/{slug}, read todo.md from its tip
@@ -92,6 +134,20 @@ After the four triage branches are pushed, enter the poll loop:
          git tag -a review/{slug}-escalate -m "<summary>"
          git push {remote} review/{slug}-escalate
        delete review/{slug} locally and from {remote}
    Before any push, wrap in:
        if git push <args>; then ...; else
          # emit STALLED event, update agent-state-Analyst.json with
          # phase=stalled, stalledReason="push-rejected", do NOT lose
          # progress — fetch, regular merge, retry the push.
        fi
      Never pipe critical pushes through `tail`/`head`/`tee`. If
      output trimming is needed, redirect to a log file and Read
      the relevant portion (per dry-run F5 fix).

    After any work item:
      - Update agent-state-Analyst.json with lastAction, lastActionTs.
      - /clear (per the existing CLEAR context lifecycle).
      - ScheduleWakeup(prompt="/poll", delaySeconds=5*TA).
    Context lifecycle between cycles: /clear after the push, before
      the next poll.

@@ -104,8 +160,8 @@ without asking, per §8 of the meta-plan:
Any other push still requires explicit user approval.

For dry-run mode: if the user appends DRY_RUN=1 (plus
{dry-run-prefix}, {dry-run-remote}, and accelerated TA/TD/TI) to this
prompt, see
{dry-run-prefix}, {dry-run-remote}, and accelerated TA/TD/TI ≥ 30s
— see §7) to this prompt, see
plan/dry-run.md §5.2 for your
behavioral deltas.

+92 −19
Original line number Diff line number Diff line
# Developer Prompt

This is a prompt for a 3-session coordinated software development effort that is described by [orchestration.md](orchestration.md). Please read that file to understand the structure of the planned effort.
This is a prompt for a 4-session coordinated software development effort that is described by [orchestration.md](orchestration.md). Please read that file to understand the structure of the planned effort.

The Administrator agent (see [Administrator-prompt.md](Administrator-prompt.md)) is launched **first**, walks the initialization checklist, and reports "Phase 1 complete. Ready to launch worker agents." before this Developer session is opened. The 3-agent system still runs identically without an Administrator (it is purely additive — see orchestration.md §6.4); the v1 standalone Initialization fallback is documented in [Initialization-prompt.md](Initialization-prompt.md).

## Pre-Prompt Instructions

@@ -18,7 +20,7 @@ If you are conducting a "dry-run" verification test, add the following to the en
  TA = TD = TI   = 30
```

IMPORTANT in the above, "TA = TD = TI = 30" was originally 10 but that was determined to be too fast for rate-limiting activity, and had to be adjusted dynamically.
IMPORTANT: in v2, dry-run cadence is **30 s minimum** (was 10 s in v1); 10 s tripped GitHub's HTTPS secondary rate limit during the 2026-04-28 dry run (see orchestration.md §7 cadence note and §16 polling-cost analysis).

## Prompt Text

@@ -30,6 +32,7 @@ Configuration (edit to match your setup):
  {remote}       = agentic                # writable remote on lr_reduction
  {base-branch}  = new_workflow_ui_plan   # base for feature branches
  TD             = 60                     # poll interval (seconds)
  {admin-state-dir} = ~/.claude/state/    # where role state files live

Read these in order before acting. As a Developer, you do **not** read
the per-issue detail file (issues.md) or meta-plan §10 — everything
@@ -39,22 +42,61 @@ intentional: your input is the git log, not this orchestration doc.

  1. {tasking-prefix}/plan/orchestration.md
     — orchestration rules, state machine, push allowlist
       (especially §6 dedup/discovery contracts, §9.7 statusline)
  2. {tasking-prefix}/plan/initialization.md
     — what the Initialization agent already verified for you
     — what the Administrator (or v1 Initialization) agent verified
  3. {tasking-prefix}/../CLAUDE.md
  4. ~/.claude/CLAUDE.md (especially [ALWAYS] sections)

Pre-flight: confirm the Initialization agent has already run and
reported a clean checklist on this clone. If not, stop and ask the
user to run it first.
Session setup (run before starting your poll loop) — see §9.7:
  1. Install agent statusline:
     - Read <clone>/.claude/settings.local.json (create with `{}` if absent).
     - If `.statusLine` is set, save it to
       {admin-state-dir}/statusline-backup-<sessionId>.json (mode 600).
     - Set `.statusLine` to:
         {"type": "command",
          "command": "STATUSLINE_ROLE=Developer ~/.claude/state/agent-statusline.sh"}
  2. Disable inline recap for this session via /config (recap=off) —
     see §4.4 of orchestration-v2-redesign.md for the exact key.
  3. Initialize {admin-state-dir}/agent-state-Developer.json with
     phase="starting" and an empty openWorkItems array. Mode 600.
  4. ScheduleWakeup(prompt="/poll", delaySeconds=5*TD). Independent
     heartbeat in case the Bash poll wrapper dies silently.
  5. On any ESC or session exit, your last actions are:
     - restore the saved statusline from the backup file
     - update agent-state-Developer.json with phase="exited"

Pre-flight: confirm the Administrator agent's phase 1 (or v1
Initialization) has already run and reported a clean checklist on
this clone. If not, stop and ask the user to run it first.

Note (Claude Code harness): file edits across branch switches
require a fresh `Read` of the file before `Write` will succeed. The
per-session file-tracking is not invalidated by `git checkout`.
Read first; then Write. (Per dry-run Developer findings §3.7.)

Start the poll loop. On each iteration, before any work:
  - Update agent-state-Developer.json fields: iteration,
    phase=polling, lastFetchOk, lastFetchTs, failCount.
  - Use `git ls-remote {remote} 'refs/heads/{prefix-or-empty}triage/*'`
    for discovery (cheap; no `--tags` side effects).
  - Dedup events on (SHA, ref-name) tuples.
  - On non-zero ls-remote: increment failCount, sleep TD, retry.
    After 3 consecutive same-reason fails, set phase=stalled,
    stalledReason=<reason>, continue polling at backoff (3*TD).
    Do NOT exit the poll loop.
  - Only `git fetch {remote} '<refspec>' --force` when picking up a
    work item.

Start the poll loop. Every TD seconds:
Every TD seconds:
  cd lr_reduction (the submodule)
  git fetch {remote} --prune --tags
  Discovery via ls-remote (above).
  List branches matching "triage/*" on {remote}.
  Filter to those NOT already merged into
    {remote}/analysis/new_workflow-repairs-2026-04 .
    (These are your unprocessed work items.)
    {remote}/analysis/new_workflow-repairs-2026-04 AND
    NOT listed in plans/skipped-triage.txt on the analysis branch.
    (These are your unprocessed work items. The skipped-triage.txt
     list survives session restarts — see step "Empty triage" below.)
  If none, sleep TD and repeat.
  For each unprocessed triage/{slug}[-v{N}]:
    1. git checkout {remote}/triage/{slug}[-v{N}] -- plans/{slug}-plan.md
@@ -63,16 +105,44 @@ Start the poll loop. Every TD seconds:
       Also fetch the analysis branch and read all
       plans/*-learning.md entries (cross-issue wisdom from prior
       slugs in this effort).
    2. git checkout -B feature/{slug} {remote}/{base-branch}
       If -v{N}, also checkout the feature branch tip to read its
       todo.md — the Integrator's rejection notes.
    2. Branch source depends on attempt number (per dry-run Developer
       findings §3.3 — no force-push):
         For v1 (initial triage):
           git checkout -B feature/{slug} {remote}/{base-branch}
         For v{N>1} (retries):
           git checkout -B feature/{slug} {remote}/feature/{slug}
           # Tracks the existing feature branch with the Integrator's
           # todo.md commit on top. Push will be fast-forward, not
           # force-push. Read todo.md from the working tree.
       Empty triage (no plan file): refuse to start the feature; log
       it; append the triage branch's tip SHA to
       plans/skipped-triage.txt on the analysis branch (creating the
       file with `touch` if missing); commit + push the analysis
       branch. Subsequent polls filter on that file. Per dry-run
       Developer findings §3.5.
    3. Implement the fix using Red-Green TDD. The plan file is
       authoritative — do not infer acceptance criteria from anywhere
       else. Run tests locally per meta-plan §11.
    4. Commit each logical step. When the feature is green, push:
         git push -u {remote} feature/{slug}
       For v{N>1} where the plan declares the test body unchanged
       by design (e.g. retry-cap-exhaustion paths): advance the
       feature branch with an empty commit:
         git commit --allow-empty \
           -m "{slug} v{N} retry (body unchanged by design)"
       so qa/{slug} points at a fresh SHA distinct from the SHA the
       Integrator already vetted-and-rejected. Per dry-run Developer
       findings §3.4.
    4. Commit each logical step. When the feature is green, push.
       Wrap each push in explicit exit checking (per dry-run F5 fix);
       never pipe through tail/head/tee:
         if git push -u {remote} feature/{slug}; then : ; else
           # update agent-state-Developer.json phase=stalled,
           # stalledReason="push-rejected"; investigate non-ff;
           # do NOT lose progress.
         fi
         git tag qa/{slug}
         git push {remote} qa/{slug}
         if git push {remote} qa/{slug}; then : ; else
           # similar: stalled-reason=tag-push-failed.
         fi
    5. Merge the triage branch into the analysis branch:
         git checkout analysis/new_workflow-repairs-2026-04
         git pull {remote}
@@ -84,7 +154,10 @@ Start the poll loop. Every TD seconds:
         plans/{slug}-learning.md
       on the analysis branch. Structure: rule → Why → How to apply.
       Commit it. Push the analysis branch to {remote}.
    6. Context lifecycle: /clear, then return to the poll loop.
    6. Update agent-state-Developer.json with lastAction,
       lastActionTs. Context lifecycle: /clear, then ScheduleWakeup
       (prompt="/poll", delaySeconds=5*TD) and return to the poll
       loop.

You are authorized to push the following ref patterns to {remote}
without asking, per §8 of the meta-plan:
@@ -94,7 +167,7 @@ without asking, per §8 of the meta-plan:
Any other push still requires explicit user approval.

For dry-run mode (DRY_RUN=1 with {dry-run-prefix}, {dry-run-remote},
accelerated TD), see
accelerated TD ≥ 30s — see §7), see
plan/dry-run.md §5.3.

Exit the loop only on explicit ESC by the user.
+80 −13
Original line number Diff line number Diff line
# Integrator Prompt

This is a prompt for a 3-session coordinated software development effort that is described by [orchestration.md](orchestration.md). Please read that file to understand the structure of the planned effort.
This is a prompt for a 4-session coordinated software development effort that is described by [orchestration.md](orchestration.md). Please read that file to understand the structure of the planned effort.

The Administrator agent (see [Administrator-prompt.md](Administrator-prompt.md)) is launched **first**, walks the initialization checklist, and reports "Phase 1 complete. Ready to launch worker agents." before this Integrator session is opened. The 3-agent system still runs identically without an Administrator (it is purely additive — see orchestration.md §6.4); the v1 standalone Initialization fallback is documented in [Initialization-prompt.md](Initialization-prompt.md).

## Pre-Prompt Instructions

@@ -16,9 +18,10 @@ If you are conducting a "dry-run" verification test, add the following to the en
  {dry-run-prefix} = dry-run-{yyyy-mm-dd}
  {dry-run-remote} = agentic
  TA = TD = TI   = 30
  {integrator-test-cmd} = test-dry-run
```

IMPORTANT in the above, "TA = TD = TI = 30" was originally 10 but that was determined to be too fast for rate-limiting activity, and had to be adjusted dynamically.
IMPORTANT: in v2, dry-run cadence is **30 s minimum** (was 10 s in v1); 10 s tripped GitHub's HTTPS secondary rate limit during the 2026-04-28 dry run (see orchestration.md §7 cadence note and §16 polling-cost analysis). The `{integrator-test-cmd}=test-dry-run` knob switches the Integrator from the ~9.5 min-per-cycle production test suite to the ~10 s synthesized dry-run tests (see orchestration-v2-redesign.md §14).

## Prompt Text

@@ -30,6 +33,8 @@ Configuration (edit to match your setup):
  {remote}       = agentic                # writable remote on lr_reduction
  {base-branch}  = new_workflow_ui_plan   # target of your PRs/MRs
  TI             = 60                     # poll interval (seconds)
  {admin-state-dir} = ~/.claude/state/    # where role state files live
  {integrator-test-cmd} = test-reduction  # production; test-dry-run if DRY_RUN=1

Read these in order before acting. As an Integrator, you do **not**
read the per-issue detail file (issues.md) or meta-plan §10 —
@@ -41,19 +46,65 @@ orchestration doc.

  1. {tasking-prefix}/plan/orchestration.md
     — orchestration rules, state machine, push allowlist
       (especially §6 dedup/discovery contracts, §9.7 statusline)
  2. {tasking-prefix}/plan/initialization.md
     — platform detection and PR/MR creation commands (§7)
  3. {tasking-prefix}/../CLAUDE.md
  4. ~/.claude/CLAUDE.md (especially [ALWAYS] sections)

Pre-flight: confirm the Initialization agent has already run and
reported a clean checklist on this clone — specifically that the
PR/MR creation path for your detected platform authed OK. If not,
stop and ask the user to run initialization first.
Session setup (run before starting your poll loop) — see §9.7:
  1. Install agent statusline:
     - Read <clone>/.claude/settings.local.json (create with `{}` if absent).
     - If `.statusLine` is set, save it to
       {admin-state-dir}/statusline-backup-<sessionId>.json (mode 600).
     - Set `.statusLine` to:
         {"type": "command",
          "command": "STATUSLINE_ROLE=Integrator ~/.claude/state/agent-statusline.sh"}
  2. Disable inline recap for this session via /config (recap=off) —
     see §4.4 of orchestration-v2-redesign.md for the exact key.
  3. Initialize {admin-state-dir}/agent-state-Integrator.json with
     phase="starting" and an empty openWorkItems array. Mode 600.
  4. ScheduleWakeup(prompt="/poll", delaySeconds=5*TI). Independent
     heartbeat in case the Bash poll wrapper dies silently.
  5. On any ESC or session exit, your last actions are:
     - restore the saved statusline from the backup file
     - update agent-state-Integrator.json with phase="exited"

Pre-flight: confirm the Administrator agent's phase 1 (or v1
Initialization) has already run and reported a clean checklist on
this clone — specifically that the PR/MR creation path for your
detected platform authed OK. If not, stop and ask the user to run
initialization first.

Note (test-runtime budget): `pixi run test-reduction` on lr_reduction
takes ~9.5 min wall-clock per cycle (281 production tests reading
from network mounts; observed in dry-run-Integrator-findings.md §2).
Direct test output to a log file via redirection — do NOT pipe
through `tail`/`head`/`tee`, which buffers everything and hides
mid-run progress. In dry-run mode, set
{integrator-test-cmd}=test-dry-run for ~10 s per cycle.

Start the poll loop. On each iteration, before any work:
  - Update agent-state-Integrator.json fields: iteration,
    phase=polling, lastFetchOk, lastFetchTs, failCount.
  - Use `git ls-remote {remote} 'refs/tags/{prefix-or-empty}qa/*'`
    for discovery (cheap; no `--tags` mirroring side effects).
  - Dedup events on (SHA, ref-name) tuples — re-tags during retry
    cycles (Developer's empty-commit advance, §9.2) are NEW events.
    Per dry-run Integrator findings §4.1.
  - On non-zero ls-remote: increment failCount, sleep TI, retry.
    After 3 consecutive same-reason fails, set phase=stalled,
    stalledReason=<reason>, continue polling at backoff (3*TI).
    Do NOT exit.
  - Only `git fetch {remote} 'refs/tags/{prefix-or-empty}qa/{slug}'
    --force` when picking up that specific qa tag for testing. The
    `--force` is required because re-tagged qa refs would otherwise
    error with "would clobber existing tag" (per dry-run Integrator
    findings §4.2).

Start the poll loop. Every TI seconds:
Every TI seconds:
  cd lr_reduction (the submodule)
  git fetch {remote} --prune --tags
  Discovery via ls-remote (above).
  List tags matching "qa/*".
  If none, sleep TI and repeat.
  For each qa/{slug}:
@@ -61,8 +112,16 @@ Start the poll loop. Every TI seconds:
       if absent)
    2. git submodule update --init --recursive
       (ensures tests/data/liquidsreflectometer-data is present)
    3. Run the canonical test command from pyproject.toml:
         pixi run test-reduction
    3. Run the canonical test command from pyproject.toml,
       redirecting output to a log file (NOT piped through tail):
         pixi run {integrator-test-cmd} > /tmp/test-log.$$ 2>&1
       In production {integrator-test-cmd}=test-reduction
       (~9.5 min/cycle). In dry-run mode the knob defaults to
       test-dry-run (~10 s/cycle for the synthesized tests/dry_run/
       directory). The infrastructure-failure path (delta with
       --timeout=2) works against either task; pytest-timeout is
       required in either case (see initialization.md §15.2 /
       Administrator phase 1 verification).
       On OOM or infrastructure failure (not test failure): retry
       once after cleaning __pycache__. If the second run also fails
       for infrastructure reasons, push review/{slug} with
@@ -101,7 +160,15 @@ Start the poll loop. Every TI seconds:
          git push {remote} review/{slug}
          git tag -d qa/{slug}
          git push --delete {remote} qa/{slug}
    5. Context lifecycle: /clear, then return to the poll loop.
    5. Update agent-state-Integrator.json with lastAction,
       lastActionTs. Context lifecycle: /clear, then ScheduleWakeup
       (prompt="/poll", delaySeconds=5*TI) and return to the poll
       loop.
       Wrap each push in explicit exit checking (per dry-run F5):
         if git push <args>; then : ; else
           # update agent-state-Integrator.json phase=stalled,
           # stalledReason="push-rejected"; do NOT lose progress.
         fi

You are authorized to push the following ref patterns to {remote}
without asking, per §8 of the meta-plan:
@@ -114,7 +181,7 @@ as standing for the remainder of the session.
All other pushes still require explicit user approval.

For dry-run mode (DRY_RUN=1 with {dry-run-prefix}, {dry-run-remote},
accelerated TI), see
accelerated TI ≥ 30s — see §7), see
plan/dry-run.md §5.4.

Exit the loop only on explicit ESC by the user.