Skip to content

feat(stack): hand the stack's member list to GitHub's native UI - #1851

Merged
mergify[bot] merged 1 commit into
mainfrom
devs/jd/jd/mrgfy-9496-featcli-stop-posting-the-stack-comment-now-that-github/hand-stack-s-member-list-github-s-native-ui--4d272321
Sep 21, 2026
Merged

mergify[bot] merged 1 commit into
mainfrom
devs/jd/jd/mrgfy-9496-featcli-stop-posting-the-stack-comment-now-that-github/hand-stack-s-member-list-github-s-native-ui--4d272321

Conversation

@jd

@jd jd commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

mergify stack push posted a sticky "This pull request is part of a
Mergify stack" comment on every member, carrying a table of the whole
stack. GitHub's native Stacks UI now renders that list on the pull
request page itself, so the comment is a second copy of what the reader
is already looking at — one they have to reconcile against the first
whenever the two disagree.

So a push whose stack GitHub registered posts no table, and deletes the
one an earlier push left on each open member.

Keyed off the registration, not off the flag. This is the same
argument as the Depends-On: header one step up, and it gets the same
treatment for the same reason: native_stack::register is explicitly
allowed to do nothing — an older GitHub Enterprise, a repo without the
feature, a chain GitHub will not accept, or --no-github-native. Drop
the comment unconditionally and those pushes end up with no member list
anywhere, which is a worse place than where they started. A push that
did not register keeps posting and updating the table exactly as
before. The settling moves to the end of the push (new step 13), next
to the header restore it mirrors, because the registration's outcome is
only known there.

Comments already on live stacks are deleted, not left or rewritten.
Leaving them is the tempting option and the wrong one: nothing
refreshes a table we no longer write, so each would sit frozen at the
membership of the last pre-native push, under a live list that keeps
moving — stale next to accurate is worse than absent next to accurate.
A one-time rewrite to a tombstone would leave permanent noise on every
member of every stack ever pushed. Deleting costs nothing recoverable:
the comment was ours, bot-authored, and carried no reply thread. The
scan that finds it is the GET the upsert already did on every push, so
this is not a new request — it is the same one, ending in a DELETE
instead of a PATCH. Merged members are skipped, as the upsert always
skipped them.

Only the fallback upsert is gated on the stack having more than one
pull request, where the count saves a genuinely one-change stack a GET
that can only come back empty. The removal is not: a stack whose
members merge down to a single open pull request keeps its
registration, so that last member is still carrying the table it was
given while the stack had several — exactly the frozen list this is
taking down. The gate costs the removal nothing anyway, since GitHub
rejects a stack below two members and an unregistered stack never
reaches the removal.

The delete is best-effort: it changes nothing about how the stack
merges, so a push that has already done everything else reports it and
retries on the next push rather than failing. That is the opposite
policy from the header restore, which is fatal precisely because an
unregistered, unchained stack merges out of order.

The revision-history comment is untouched. GitHub renders nothing
like it, which is what the sticky-comment surface exists for now.

Also corrects a stale claim in stack_comment's docstring: it said
mergify stack checkout rebuilds a stack from the
<!-- mergify-stack-data: --> JSON marker. It does not, and nothing in
this repo reads that marker — checkout discovers a stack by chaining
each pull request's head.ref to the next one's base.ref, which has
the advantage of working on stacks this CLI never touched. The marker's
readers are out of tree, so its wire shape stays pinned.

MRGFY-9496

Co-Authored-By: Claude Opus 5 (1M context) noreply@anthropic.com

Copilot AI lite review requested due to automatic review settings September 21, 2026 10:28
@mergify
mergify Bot had a problem deploying to Mergify Merge Protections September 21, 2026 10:28 Failure
@jd
jd deployed to func-tests-live September 21, 2026 10:28 — with GitHub Actions Active

Copilot AI left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Copilot review overview

🟡 Changes recommended

Two moderate cleanup issues and two documentation nits remain unresolved.

Get a fresh assessment by requesting another Copilot review.

Review effort: Lite
Findings: 1 Medium severity

Open (1)
What changed in this PR

This PR uses GitHub’s native Stacks UI for registered stacks, removing legacy comments while preserving fallback behavior.

Changes:

  • Deletes legacy comments after successful native registration.
  • Retains comment updates when registration is unavailable.
  • Updates documentation and adds unit/integration coverage.
File Summary
skills/​mergify-stack/​SKILL.md Documents native and fallback stack-comment behavior.
crates/​mergify-stack/​src/​stack_comment.rs Corrects marker documentation; one nit requests clearer wording.
crates/​mergify-stack/​src/​lib.rs Updates module documentation; one nit notes registered cleanup also uses the recognizer.
crates/​mergify-stack/​src/​comment_upsert.rs Adds comment deletion support and tests.
crates/​mergify-stack/​src/​commands/​push.rs Coordinates native cleanup and fallback updates; two moderate issues remain around single-member cleanup and closed PR handling.
crates/​mergify-cli/​tests/​stack_push_github_native.rs Adds integration coverage for native stack behavior.

💡 Add a code-review agent skill or configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

Comment thread crates/mergify-stack/src/commands/push.rs Outdated
@mergify

mergify Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Merge Protections

🟢 All 6 merge protections satisfied — ready to merge.

Show 6 satisfied protections

🟢 🤖 Continuous Integration

  • all of:
    • check-success=ci-gate

🟢 👀 Review Requirements

  • any of:
    • #approved-reviews-by>=2
    • author = dependabot[bot]
    • author = mergify-ci-bot
    • author = renovate[bot]

🟢 Enforce conventional commit

Make sure that we follow https://www.conventionalcommits.org/en/v1.0.0/

  • title ~= ^(fix|feat|internal|docs|style|refactor|perf|test|build|ci|chore|revert|ui)(?:\(.+\))?!?:

🟢 🔎 Reviews

  • #changes-requested-reviews-by = 0
  • #review-requested = 0
  • #review-threads-unresolved = 0

🟢 📕 PR description

  • body ~= (?ms:.{48,})

🟢 🚦 Auto-queue

When all merge protections are satisfied, this pull request will be queued automatically.

@jd
jd marked this pull request as ready for review September 21, 2026 10:57
`mergify stack push` posted a sticky "This pull request is part of a
Mergify stack" comment on every member, carrying a table of the whole
stack. GitHub's native Stacks UI now renders that list on the pull
request page itself, so the comment is a second copy of what the reader
is already looking at — one they have to reconcile against the first
whenever the two disagree.

So a push whose stack GitHub registered posts no table, and deletes the
one an earlier push left on each open member.

**Keyed off the registration, not off the flag.** This is the same
argument as the `Depends-On:` header one step up, and it gets the same
treatment for the same reason: `native_stack::register` is explicitly
allowed to do nothing — an older GitHub Enterprise, a repo without the
feature, a chain GitHub will not accept, or `--no-github-native`. Drop
the comment unconditionally and those pushes end up with no member list
anywhere, which is a worse place than where they started. A push that
did not register keeps posting and updating the table exactly as
before. The settling moves to the end of the push (new step 13), next
to the header restore it mirrors, because the registration's outcome is
only known there.

**Comments already on live stacks are deleted, not left or rewritten.**
Leaving them is the tempting option and the wrong one: nothing
refreshes a table we no longer write, so each would sit frozen at the
membership of the last pre-native push, under a live list that keeps
moving — stale next to accurate is worse than absent next to accurate.
A one-time rewrite to a tombstone would leave permanent noise on every
member of every stack ever pushed. Deleting costs nothing recoverable:
the comment was ours, bot-authored, and carried no reply thread. The
scan that finds it is the GET the upsert already did on every push, so
this is not a new request — it is the same one, ending in a DELETE
instead of a PATCH. Merged members are skipped, as the upsert always
skipped them.

Only the fallback upsert is gated on the stack having more than one
pull request, where the count saves a genuinely one-change stack a GET
that can only come back empty. The removal is not: a stack whose
members merge down to a single open pull request keeps its
registration, so that last member is still carrying the table it was
given while the stack had several — exactly the frozen list this is
taking down. The gate costs the removal nothing anyway, since GitHub
rejects a stack below two members and an unregistered stack never
reaches the removal.

The delete is best-effort: it changes nothing about how the stack
merges, so a push that has already done everything else reports it and
retries on the next push rather than failing. That is the opposite
policy from the header restore, which is fatal precisely because an
unregistered, unchained stack merges out of order.

**The revision-history comment is untouched.** GitHub renders nothing
like it, which is what the sticky-comment surface exists for now.

Also corrects a stale claim in `stack_comment`'s docstring: it said
`mergify stack checkout` rebuilds a stack from the
`<!-- mergify-stack-data: -->` JSON marker. It does not, and nothing in
this repo reads that marker — `checkout` discovers a stack by chaining
each pull request's `head.ref` to the next one's `base.ref`, which has
the advantage of working on stacks this CLI never touched. The marker's
readers are out of tree, so its wire shape stays pinned.

MRGFY-9496

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Change-Id: I4d272321c1cfcb3b836a1fe1ac2ef0ce463ec550
@jd
jd force-pushed the devs/jd/jd/mrgfy-9496-featcli-stop-posting-the-stack-comment-now-that-github/hand-stack-s-member-list-github-s-native-ui--4d272321 branch from db4e03d to 2e66ef5 Compare September 21, 2026 11:09
@jd

jd commented Sep 21, 2026

Copy link
Copy Markdown
Member Author

Revision history

# Type Changes Reason Date
1 initial db4e03d 2026-09-21 11:08 UTC
2 content db4e03d → 2e66ef5 review: run the comment removal on a registered stack that has shrunk to one live PR (the count gate now guards only the fallback upsert), and fix a typo the CI typo checker flagged 2026-09-21 11:08 UTC

@jd
jd deployed to func-tests-live September 21, 2026 11:09 — with GitHub Actions Active
@jd

jd commented Sep 21, 2026 •

Copy link
Copy Markdown
Member Author

Re-pushed: db4e03d → 2e66ef5

Two things, one of them a real hole in what this PR promises:

The comment removal no longer depends on the member count. The gate was total_pulls > 1 around both branches; it is now stack_is_registered || total_pulls > 1, with the count still guarding the fallback upsert alone. A registered stack whose members merge down to one open pull request keeps its registration — appendable_tail finds nothing to add, so the stack stands — and that last member arrives here still carrying the table it was given while the stack had several. The old gate skipped it, which left a frozen list sitting under GitHub's live one: the exact thing this PR exists to remove. Lifting the gate costs nothing, because GitHub rejects a stack below two members, so a genuinely one-change stack is never registered and never reaches the removal.

New test a_registered_stack_down_to_one_live_pull_request_still_loses_its_comment pins it, and fails on the old gate.

I have deliberately not covered a stack that shrinks and loses its registration (GitHub dissolves a 2-member stack that drops to one). Finding that comment means a GET on every single-change push for ever, to clean an artifact only pre-native stacks can carry. The reasoning is in the code beside the gate; the gap already existed on main.

Typo checker. DELETEs in comment_upsert.rs — typos splits it at the case boundary and reads DELET. Reworded to "issues a DELETE for what it finds", which reads better anyway. That was the whole of the typos failure, and ci-gate was red only because it aggregates it.

@mergify
mergify Bot deployed to Mergify Merge Protections September 21, 2026 11:10 Active
@mergify
mergify Bot requested a review from a team September 21, 2026 11:17
@mergify
mergify Bot requested a review from a team September 21, 2026 12:16
@mergify

mergify Bot commented Sep 21, 2026 •

Copy link
Copy Markdown
Contributor

Merge Queue Status

  • ✅ Entered queue — 2026-09-21 12:37 UTC · Rule: default · triggered by merge protections
  • ✅ Checks skipped · PR is already up-to-date
  • ✅ Merged — 2026-09-21 12:38 UTC · at ee10228841eab68c9ed2029e21e793b9c2b6ed90 · squash

This pull request spent 56 seconds in the queue, including 1 second running CI.

Required conditions to merge

@mergify
mergify Bot merged commit ee10228 into main Sep 21, 2026
22 checks passed
@mergify
mergify Bot deleted the devs/jd/jd/mrgfy-9496-featcli-stop-posting-the-stack-comment-now-that-github/hand-stack-s-member-list-github-s-native-ui--4d272321 branch September 21, 2026 12:38

This branch was successfully deployed

2 active deployments
Mergify Merge Protections — 2e66ef54 Deployed Sep 21, 2026 by mergify[bot]
func-tests-live — 2e66ef54 Deployed Sep 21, 2026 by jd via live-tests #1941
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Development

Successfully merging this pull request may close these issues.

4 participants