Skip to content

fix: align "part N of M" with <kbd> labels in stacked PR footers#13363

Draft
wmichelin wants to merge 3 commits intogitbutlerapp:masterfrom
wmichelin:fix/stacked-pr-footer-numbering
Draft

fix: align "part N of M" with <kbd> labels in stacked PR footers#13363
wmichelin wants to merge 3 commits intogitbutlerapp:masterfrom
wmichelin:fix/stacked-pr-footer-numbering

Conversation

@wmichelin
Copy link
Copy Markdown

@wmichelin wmichelin commented Apr 17, 2026

This bug may also exist on the Desktop version of the app, but I haven't tested that yet.

🧢 Changes

  • Change nth = stack_length - stack_index to nth = stack_index + 1 in generate_footer (Rust)
  • Apply same fix in TypeScript mirror (prFooter.ts), plus reverse iteration order to list top-first (matching Rust)
  • Update test expectation in test_generate_footer_large_stack

☕️ Reasoning

The <kbd> labels number the stack with 1 = base and highest = top, but the "part N of M" text used the opposite convention. This made the base PR say "part 3 of 3" while its <kbd> label said 1.

🎫 Affected issues

Fixes: #13362

wmichelin and others added 3 commits April 17, 2026 13:39
The `generate_footer` function computed `nth = stack_length - stack_index`,
counting from the top of the stack. But the `<kbd>` labels count from the
base (1 = base, highest = top). This made the base PR say "part 3 of 3"
while its <kbd> label said 1.

Change to `nth = stack_index + 1` so both numbering systems agree.

Also fix the TypeScript mirror in prFooter.ts: same nth formula, plus
reverse the iteration order to list top-first (matching Rust output).

Fixes: gitbutlerapp#13362
These tests previously only checked the <kbd> label and emoji, not the
"part N of M" text. Adding the assertion ensures the numbering stays
consistent and prevents the original bug from regressing.
@wmichelin wmichelin marked this pull request as ready for review April 17, 2026 17:51
@wmichelin
Copy link
Copy Markdown
Author

don't seem to be able to run CI myself, understandably 😄

@Byron
Copy link
Copy Markdown
Collaborator

Byron commented Apr 19, 2026

@codex review Also consider my message #13362 (comment) which seems to indicate that it works as expected, at least with the current Nightly build.

@Byron
Copy link
Copy Markdown
Collaborator

Byron commented Apr 19, 2026

Thanks a lot for taking a stab at this!

I definitely like the addition of the part assertion to the tests, which seems very valuable, given that it seems to be inverted sometimes the question is just why it seems to work for me. Please see this message for reference.

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Fixes inconsistent “part N of M” numbering in stacked PR footers so it matches the <kbd> stack labels (1 = base, M = top), addressing issue #13362.

Changes:

  • Adjust Rust footer generation to use nth = stack_index + 1 (base-first numbering).
  • Apply the same numbering fix in Desktop’s TypeScript mirror and reverse iteration to list top-of-stack first (matching Rust).
  • Update Rust test expectations to assert the corrected “part N of M” text.

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
crates/but-forge/src/review.rs Fixes stack “part N” calculation and updates tests to validate corrected numbering.
apps/desktop/src/lib/forge/shared/prFooter.ts Mirrors the corrected “part N” logic and aligns listing order with Rust by reversing iteration.

Comment on lines +146 to 149
[...allPrNumbers].reverse().forEach((prNumber, i) => {
const current = prNumber === forPrNumber;
footer += `- <kbd>&nbsp;${stackLength - i}&nbsp;</kbd> ${symbol}${prNumber} ${current ? "👈 " : ""}\n`;
});
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

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

This change updates footer ordering/numbering on Desktop, but generateFooter doesn’t appear to have unit tests. Adding a small Vitest suite for the header "part N of M" and the <kbd> numbering/order (mirroring the Rust tests) would help prevent regressions.

Copilot uses AI. Check for mistakes.
@@ -138,13 +138,13 @@ function clearFooter(body: string | undefined) {
function generateFooter(forPrNumber: number, allPrNumbers: number[], symbol: string) {
const stackLength = allPrNumbers.length;
const stackIndex = allPrNumbers.findIndex((number) => number === forPrNumber);
Copy link

Copilot AI Apr 19, 2026

Choose a reason for hiding this comment

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

allPrNumbers.findIndex(...) returns -1 when forPrNumber isn’t present. With nth = stackIndex + 1, that would render "part 0 of N". Consider handling the -1 case explicitly (e.g., fallback to 1, or return/throw) to avoid generating an invalid footer when inputs are inconsistent.

Suggested change
const stackIndex = allPrNumbers.findIndex((number) => number === forPrNumber);
const stackIndex = allPrNumbers.findIndex((number) => number === forPrNumber);
if (stackIndex === -1) {
throw new Error(`Pull request #${forPrNumber} is not part of the provided stack.`);
}

Copilot uses AI. Check for mistakes.
Copy link
Copy Markdown
Contributor

@chatgpt-codex-connector chatgpt-codex-connector Bot left a comment

Choose a reason for hiding this comment

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

💡 Codex Review

Here are some automated review suggestions for this pull request.

Reviewed commit: 2014f2d944

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

const stackLength = allPrNumbers.length;
const stackIndex = allPrNumbers.findIndex((number) => number === forPrNumber);
const nth = stackLength - stackIndex;
const nth = stackIndex + 1;
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

P1 Badge Derive part number from stack order used by desktop

generateFooter() now assumes allPrNumbers is base→top (stackIndex + 1), but desktop callers pass stack order as top→base: in the same file, updateStackPrs() builds allPrNumbers directly from branchDetails and iterates branchDetails from end to start to compute targetBase, which only works for top→base order. With that input, this change makes base PRs render as the highest part number (e.g. base becomes part 3 of 3), effectively reintroducing inverted stack numbering in desktop footers.

Useful? React with 👍 / 👎.

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

BranchDetails might also be ordered 'the other way around'. New data structures put the top-most stack into slot 0, to traversal is top-to-bottom, while older data structures do it the other way around.
Maybe this also plays a role.

@wmichelin
Copy link
Copy Markdown
Author

Thanks a lot for taking a stab at this!

I definitely like the addition of the part assertion to the tests, which seems very valuable, given that it seems to be inverted sometimes the question is just why it seems to work for me. Please see this message for reference.

I was using the CLI. Have you tried that? Maybe this bug only exists there?

I believe the logic is replicated between the typescript and rust codebases.

@Byron
Copy link
Copy Markdown
Collaborator

Byron commented Apr 19, 2026

Right, I think that's the core of the problem.

The graphical user interface already seems to be doing it correctly, so I'm not sure about the related changes in this PR, but the backend may have had its very own implementation. And maybe that's where it went wrong.

This bug may also exist on the Desktop version of the app, but I haven't tested that yet.

Since I tested that and wasn't able to reproduce it, it's probably not present there.

It definitely seems the auto review is worth investigating.

@Byron Byron marked this pull request as draft April 19, 2026 08:41
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.

Butler CLI - Stacked PR footer: "part N of M" numbering is inconsistent with <kbd> labels

3 participants