Skip to content

feat(github): name every rollout member unambiguously - #1272

Draft
aparajon wants to merge 3 commits into
armand/multi-target-pullfrom
armand/multi-target-member-naming
Draft

feat(github): name every rollout member unambiguously#1272
aparajon wants to merge 3 commits into
armand/multi-target-pullfrom
armand/multi-target-member-naming

Conversation

@aparajon

@aparajon aparajon commented Sep 3, 2026

Copy link
Copy Markdown
Collaborator

Why this matters

A rollout member is its deployment and its target together, but the deployment alone is what every operator-facing surface shows. Once one deployment can address several targets, two members render under the same name — and a label that points at another member ("waiting for X", "halted by X") stops identifying one of them at all.

The lookups behind those surfaces were already keyed by that name, which is a defect that predates multi-target: a keyed or sharded apply has several operations per deployment today, and a by-name lookup returns whichever one it finds first.

What it does

routing.DisplayNames becomes the single naming rule every surface uses:

member is named by                 when
─────────────────────────────────  ────────────────────────────────────────────
deployment                         its deployment addresses one target
deployment/target                  its deployment addresses 2+ distinct targets

Keying on distinct targets rather than member count is what keeps a keyed or sharded apply — several operations of one deployment against the same target — named by the deployment, where the extra half would be noise that still did not tell the operations apart.

presentation.Derive resolves each member's name once and returns one entry per input operation in input order, so every consumer pairs by index instead of looking a name up:

operations[i] ──► presentation.Derive ──► deployments[i]  (name resolved once)
                                             │
        ┌────────────────┬───────────────────┼──────────────────┬────────────────┐
   plan comment    check summary       apply comment       CLI progress      watch TUI

The apply comment's per-member details move from a name-keyed map to a positional slice, and the CLI progress and watch-TUI by-name lookups become index lookups. An errored member's wording also follows the contract it was planned under: a mirrored member "could not verify" (its diff could not be confirmed against the reviewed plan), an independent member "could not plan" (it has no plan at all).

Plan comment — an independent member that could not be planned

Schema Change Plan — Production

Database: testapp | Type: MySQL

Started at 2026-09-03 18:00:00 UTC

⚠️ Some targets could not be planned — every target must have a plan before an apply can run, so the plan check is failing closed:

  • primary/testapp-001 (primary) ✅ planned against its own schema
  • primary/testapp-002 ❌ could not plan — diff failed; see server logs
  • eu-west ✅ planned against its own schema
ALTER TABLE `users` ADD COLUMN `email` varchar(255);

📋 Plan: 1 DDL statement


▶️ To apply all schema changes from this PR, comment:

schemabot apply -e production
Apply status comment — three members, one deployment addressing two targets

Schema Change Status — Production

Apply ID: apply-7f3a

Started at 2026-09-03 18:00:00 UTC

Deployments: 1 completed, 1 running, 1 waiting

  • ✅ primary/testapp-001 — completed
  • 🔄 primary/testapp-002 — running table copy
  • ⏳ eu-west — waiting for primary/testapp-002
✅ primary/testapp-001 — completed

Database: testapp_001

Started at 2026-09-03 18:00:00 UTC

Status: Applied

🔄 primary/testapp-002 — running table copy

Database: testapp_002

Started at 2026-09-03 18:00:00 UTC

Status: In Progress

users: 🟦🟦🟦🟦🟦🟦🟦🟦⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 42.00%

  • Rows: 420,000 / 1,000,000

To stop this schema change:

schemabot stop  -e 
⏳ eu-west — waiting for primary/testapp-002

No details available yet.

Last updated: 2026-09-03 18:00:00 UTC (2026-09-03 18:00:00 UTC)

CLI progress — members named in full, next-action pointing at a specific member
┌─────────────────────────────────────────────────────┐
│  Apply ID:     apply-7f3a                           │
│  Environment:  production                           │
│  State:        running                              │
│  Deployments:  1 completed · 1 running · 1 waiting  │
└─────────────────────────────────────────────────────┘

✅ primary/testapp-001 — completed
  External operation ID: op-001
  External apply ID: psdb-1a2b

     ~ users: 🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩🟩 ✓ Complete


🔄 primary/testapp-002 — running table copy

     ~ users: 🟦🟦🟦🟦🟦🟦🟦🟦⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜⬜ 42.00%
       • Rows: 420,000 / 1,000,000


⏳ eu-west — waiting for primary/testapp-002 (orders-eu)

How it moves us toward the northstar

A4a's multi-target rejection was as much a UX constraint as a routing one: a rollout the operator cannot read is not one we can let them run. This is the presentation half — every surface can now name each member of a ResolveTargets result unambiguously, using one rule rather than five copies of a convention.

Opened by Claude (Fable 5).

A rollout member is identified by its deployment and target together, but
the deployment alone is what the plan comment, check summary, apply comment
and CLI progress have been showing. Once one deployment can address several
targets, two members render under the same name, and a label that points at
another member ("waiting for X", "halted by X") no longer identifies one.

routing.DisplayNames is the single naming rule every surface now uses: a
member is named by its deployment alone unless that deployment addresses
more than one distinct target in the rollout, in which case every member of
that deployment is named deployment/target. Keying on distinct targets is
what keeps a keyed or sharded apply — several operations of one deployment
against the same target — named by the deployment, where the extra half
would be noise that still did not tell the operations apart.

presentation.Derive resolves each member's name once and hands it to every
consumer, including the labels that reference a sibling and the suggested
next action. The apply comment's per-member detail bodies move from a
name-keyed map to a slice paired positionally with the model, since a map
collapses two members of one deployment onto one body.

The independent-planning contract also reaches the wording: an errored
member reads "could not plan" where targets hold their own schemas and
"could not verify" where they are expected to mirror each other.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant