Skip to content

Fix size-diff baseline comparison: compare against the PR's merge-base commit, not the branch tip - #11835

Merged
sensei-hacker merged 3 commits into
iNavFlight:release/9.1from
sensei-hacker:fix-size-baseline-commit-matching
Aug 29, 2026
Merged

Fix size-diff baseline comparison: compare against the PR's merge-base commit, not the branch tip#11835
sensei-hacker merged 3 commits into
iNavFlight:release/9.1from
sensei-hacker:fix-size-baseline-commit-matching

Conversation

@sensei-hacker

@sensei-hacker sensei-hacker commented Aug 29, 2026

Copy link
Copy Markdown
Member

Summary

The RAM/flash size-diff workflow compared PRs against the base branch's latest tip baseline, so when the base advanced after a PR forked, the delta included unrelated changes. Concrete case: PR #11785 showed a stale +9,788 B RAM figure that actually belonged to #11438 (merged to base after the PR forked); the PR's real marginal RAM was ~120 B.

This PR keys the comparison to the PR's true base commit instead.

Changes

  • publish-size-baseline.sh (new): publish-baseline now stores size-baseline-<COMMIT_SHA> (primary) in iNavFlight/pr-test-builds in addition to the existing size-baseline-<BRANCH> latest-tip pointer (kept for backward compatibility). Per-commit baselines are pruned to the newest 50 per branch (branch tracked via a machine-readable branch: line in the release notes) plus a global cap of 300, so the companion repo can't grow unbounded. All inputs (branch, SHA, report) are regex-validated before use.
  • fetch-size-baseline.sh (new): pr-comment computes the PR's merge-base via the compare API (compare/{base_ref}...{head_sha} — verified to resolve fork-PR head SHAs), fetches the exact per-commit baseline, and if absent walks the base branch's first-parent chain to the nearest nightly-built ancestor. The branch-tip baseline is deliberately not a fallback — comparing against it is the stale-delta bug being fixed. New SHA/ref inputs follow the existing regex-validation discipline.
  • size-diff-comment.js: comment header now names the baseline used ("vs. base commit `abcd123`"), with a note when a nearest-available baseline was used. The graceful "No size baseline is available yet…" path is unchanged.
  • size-diff-comment.test.js: tests for the new header/fallback note; also fixes two pre-existing tests whose notable-delta expectations predated NOISE_THRESHOLD_BYTES being raised to 256.
  • .github/workflows/README.md: documents the new behavior.

No ci.yml change needed: the PR head SHA comes from the workflow_run context, and the base ref is already carried in the base-ref artifact.

Testing

  • node --test .github/scripts/size-diff-comment.test.js — 23/23 pass (19 original + 4 new; the 2 pre-existing threshold-drift failures are fixed by this PR).
  • bash -n on both new scripts; workflow YAML parses cleanly.
  • fetch-size-baseline.sh smoke-tested end-to-end against the live repos (read-only): validates inputs, lists existing per-commit baseline tags, walks merge-base ancestors, and reports found=false gracefully when no baseline exists.
  • Pruning selection logic verified against a synthetic fixture: keeps the newest 50 per branch, deletes exactly the overflow entries, respects the global cap.
  • Merge-base API resolution verified live against fork PR Use Measurement For Fixed Wing Position PID D Term #11831 (breadoven fork).

Code Review

Reviewed with the inav-code-review agent: one CRITICAL finding (pruning branch-grouping jq capture missing the (?m) flag, which would have collapsed all branches into one prune bucket) plus robustness items — all addressed in d4d541e (see review comment); 23/23 tests pass and the prune selection is verified against multi-branch fixtures.

Related

  • Source review: review-pr11785-terrain-agl-ram (stale +9,788 B baseline finding)

@sensei-hacker sensei-hacker added this to the 10.0 milestone Aug 29, 2026
@qodo-code-review

Copy link
Copy Markdown
Contributor

ⓘ Qodo reviews are paused because the subscription is no longer active. Ask your workspace admin to reactivate the subscription to resume reviews. Manage billing

@qodo-free-for-open-source-projects

Copy link
Copy Markdown

PR Summary by Qodo

Compare size diffs against merge-base baselines

🐞 Bug fix ✨ Enhancement 🧪 Tests 📝 Documentation ⚙️ Configuration changes 🕐 40+ Minutes

Grey Divider

AI Description

• Stores bounded per-commit nightly baselines while retaining branch-tip pointers for compatibility.
• Compares PR reports against merge-base or nearest ancestor baselines, excluding unrelated branch
 changes.
• Identifies selected baseline commits in comments and covers rendering behavior with tests.
Diagram

graph TD
  N["Nightly Build"] --> P["Baseline Publisher"] --> R[("Commit Baselines")]
  Q["PR Build"] --> A["Compare API"] --> F["Baseline Fetcher"] --> C["Size Comment"]
  R --> F
Loading
High-Level Assessment

The following are alternative approaches to this PR:

1. Build the merge-base on demand
  • ➕ Guarantees an exact baseline even when no nightly artifact exists.
  • ➕ Avoids retaining hundreds of per-commit releases.
  • ➖ Adds substantial compute time and cost to every PR run.
  • ➖ Requires safely checking out and building historical commits in CI.
2. Use dedicated artifact storage
  • ➕ Supports explicit lifecycle policies and efficient commit-keyed lookup.
  • ➕ Avoids using GitHub releases as a report database.
  • ➖ Introduces new infrastructure, credentials, and operational ownership.
  • ➖ Complicates migration and access for fork-triggered workflows.

Recommendation: Keep the per-commit release approach: it reuses existing authenticated infrastructure, avoids rebuilding historical firmware, and preserves compatibility through branch-tip tags. The bounded retention and nearest-ancestor fallback are appropriate tradeoffs, provided pruning and ancestry fallback behavior remain well tested.

Files changed (6) +417 / -66

Bug fix (2) +123 / -3
fetch-size-baseline.shResolve and download merge-base size baselines +109/-0

Resolve and download merge-base size baselines

• Adds validated retrieval of a baseline keyed by the PR merge-base SHA. If the exact release is unavailable, it walks up to 30 first-parent commits for the nearest stored report and emits structured workflow outputs without falling back to the branch tip.

.github/scripts/fetch-size-baseline.sh

size-diff-comment.jsShow baseline provenance in size-diff comments +14/-3

Show baseline provenance in size-diff comments

• Extends comment rendering to name the baseline commit used for comparison. It also explains nearest-ancestor fallback selection while preserving legacy wording and the graceful missing-baseline path.

.github/scripts/size-diff-comment.js

Tests (1) +74 / -7
size-diff-comment.test.jsCover baseline headers and align threshold fixtures +74/-7

Cover baseline headers and align threshold fixtures

• Adds tests for exact, nearest, legacy, and missing baseline comment states. Existing notable-delta fixtures now exceed the current 256-byte noise threshold.

.github/scripts/size-diff-comment.test.js

Documentation (1) +22 / -13
README.mdDocument commit-based size baseline behavior +22/-13

Document commit-based size baseline behavior

• Documents dual baseline publication, retention limits, merge-base resolution, nearest-ancestor fallback, and the new helper scripts.

.github/workflows/README.md

Other (2) +198 / -43
publish-size-baseline.shPublish commit-addressed baselines with retention limits +130/-0

Publish commit-addressed baselines with retention limits

• Extracts baseline publishing into a validated script that maintains both backward-compatible branch tags and primary commit tags. It records branch metadata, retries safe asset replacement through existing releases, and adds per-branch and global pruning selection with dry-run support.

.github/scripts/publish-size-baseline.sh

ci-size-report.ymlWire merge-base baseline publishing and retrieval +68/-43

Wire merge-base baseline publishing and retrieval

• Updates nightly publishing to invoke the commit-aware publisher with the workflow head SHA. PR comments now resolve the merge-base through GitHub's compare API, fetch the matching baseline, and pass its commit and exactness into the renderer.

.github/workflows/ci-size-report.yml

@qodo-free-for-open-source-projects

qodo-free-for-open-source-projects Bot commented Aug 29, 2026

Copy link
Copy Markdown

Code Review by Qodo

🐞 Bugs (2) 📘 Rule violations (0) 📎 Requirement gaps (0) 🎨 UX issues (0) 🔗 Cross-repo conflicts (0) 📜 Skill insights (0)

Grey Divider


Action required

1. Publish script is unavailable 🐞 Bug ≡ Correctness
Description
The publish-baseline job invokes .github/scripts/publish-size-baseline.sh without first checking
out the repository, so every eligible nightly run fails with “No such file or directory” and
publishes no baseline. Because the job declares only actions: read, adding checkout also requires
contents: read.
Code

.github/workflows/ci-size-report.yml[R113-117]

+          bash .github/scripts/publish-size-baseline.sh \
+            iNavFlight/pr-test-builds \
+            "${BRANCH}" \
+            "${COMMIT_SHA}" \
+            size-report.json
Evidence
The publish job's steps only check the upstream job and download artifacts before directly invoking
the local script; unlike the PR-comment job, it has no checkout step. Its explicit permissions
contain only actions: read, while the invoked file exists solely in the repository.

.github/workflows/ci-size-report.yml[40-50]
.github/workflows/ci-size-report.yml[58-117]
.github/workflows/ci-size-report.yml[134-138]
.github/scripts/publish-size-baseline.sh[1-10]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The publish job invokes a repository-local script without checking out the repository, so baseline publishing fails.
## Issue Context
The separate PR-comment job checks out trusted default-branch content before using local scripts. The publish job also needs `contents: read` in its explicit permissions.
## Fix Focus Areas
- .github/workflows/ci-size-report.yml[48-50]
- .github/workflows/ci-size-report.yml[82-98]
- .github/workflows/ci-size-report.yml[113-117]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


2. Release notes evade pruning ✓ Resolved 🐞 Bug ☼ Reliability
Description
list_per_commit_baselines anchors its branch capture against the entire release body, but
publish_asset creates a two-line body, so normal per-commit releases produce no jq record and are
invisible to pruning. The documented per-branch and global caps therefore do not bound repository
growth.
Code

.github/scripts/publish-size-baseline.sh[R89-91]

+        --jq '.[] | select(.tag_name | test("^size-baseline-[0-9a-f]{40}$")) |
+              [.created_at, .tag_name,
+               ((.body // "") | capture("^branch: (?<b>[A-Za-z0-9._/-]+)$") | .b // "?")] | @tsv'
Evidence
The publisher writes branch:  followed by a second descriptive line, while the listing function
applies capture with ^...$ to .body as a whole. A nonmatching capture emits no value, so the
subsequent // "?" does not restore the omitted release.

.github/scripts/publish-size-baseline.sh[74-76]
.github/scripts/publish-size-baseline.sh[82-92]
.github/scripts/publish-size-baseline.sh[94-105]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pruning query attempts to match a one-line anchored regex against the complete multi-line release body, causing releases created by this script to be omitted.
## Issue Context
Extract and validate the first body line, and ensure malformed notes still emit a record under the fallback branch group rather than disappearing from the stream.
## Fix Focus Areas
- .github/scripts/publish-size-baseline.sh[74-76]
- .github/scripts/publish-size-baseline.sh[87-92]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


3. Old baselines are never deleted ✓ Resolved 🐞 Bug ☼ Reliability
Description
The first AWK pass stores only the newest 50 tags per branch in keepTag, and the END block
iterates only keepTag, so tags beyond the first 50 can never be printed for deletion. Even after
fixing release-note parsing, the per-branch cap is not enforced and old releases accumulate
indefinitely unless they happen to be selected for a separate global-cap deletion.
Code

.github/scripts/publish-size-baseline.sh[R117-119]

+        END {
+            for (t in keepTag) if (!keepFinal[t]) print t
+        }
Evidence
Lines 108-110 add only the first keep tags for each branch to keepTag; lines 117-119 then
iterate that same set exclusively. No collection contains or emits the older tags that failed the
count[$3] < keep condition.

.github/scripts/publish-size-baseline.sh[99-120]
.github/scripts/publish-size-baseline.sh[121-127]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The pruning AWK program only considers retained tags when producing deletions and loses every tag outside the per-branch keep set.
## Issue Context
Track every input tag, retain only the newest configured count per branch and then the global count, and print every input tag absent from the final keep set.
## Fix Focus Areas
- .github/scripts/publish-size-baseline.sh[99-120]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools



Remediation recommended

4. Ancestor search misses baselines 🐞 Bug ≡ Correctness
Description
The fallback stops after 30 commits even though publishing retains 50 baselines per branch, and
retained baselines are counted per nightly build rather than per intervening commit. A merge-base
can therefore have an available retained ancestor more than 30 commits back, yet
fetch-size-baseline.sh reports found=false and suppresses the size comparison.
Code

.github/scripts/fetch-size-baseline.sh[43]

+MAX_WALK=30
Evidence
Fetch walks at most 30 parent edges before emitting found=false, while publish keeps 50 baseline
releases per branch. Nightly publishing is path-filtered, so the distance in commits between
retained baselines is not bounded by the number of retained releases.

.github/scripts/fetch-size-baseline.sh[43-43]
.github/scripts/fetch-size-baseline.sh[95-109]
.github/scripts/publish-size-baseline.sh[35-36]
.github/workflows/nightly-build.yml[5-20]
.github/workflows/ci-size-report.yml[216-227]

Agent prompt
The issue below was found during a code review. Follow the provided context and guidance below and implement a solution

## Issue description
The fixed 30-commit ancestry limit can terminate before reaching a retained per-commit baseline.
## Issue Context
Retention counts published baselines, not commits, and the nightly workflow is path-filtered, so more than 30 commits may separate two baseline-producing pushes. Make the search bound consistent with actual retained history or traverse until an appropriate retained boundary.
## Fix Focus Areas
- .github/scripts/fetch-size-baseline.sh[43-43]
- .github/scripts/fetch-size-baseline.sh[95-109]
- .github/scripts/publish-size-baseline.sh[35-36]
- .github/workflows/nightly-build.yml[5-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools


Grey Divider

Tip of the day
💡 Did you know, you can group findings by type and pick your Finding display, from Minimal to Full

More tips ↗ | Customize Qodo ↗ | Qodo docs ↗

Grey Divider

Qodo Logo

Comment on lines +113 to +117
bash .github/scripts/publish-size-baseline.sh \
iNavFlight/pr-test-builds \
"${BRANCH}" \
"${COMMIT_SHA}" \
size-report.json

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Action required

1. Publish script is unavailable 🐞 Bug ≡ Correctness

The publish-baseline job invokes .github/scripts/publish-size-baseline.sh without first checking
out the repository, so every eligible nightly run fails with “No such file or directory” and
publishes no baseline. Because the job declares only actions: read, adding checkout also requires
contents: read.
Agent Prompt
## Issue description
The publish job invokes a repository-local script without checking out the repository, so baseline publishing fails.

## Issue Context
The separate PR-comment job checks out trusted default-branch content before using local scripts. The publish job also needs `contents: read` in its explicit permissions.

## Fix Focus Areas
- .github/workflows/ci-size-report.yml[48-50]
- .github/workflows/ci-size-report.yml[82-98]
- .github/workflows/ci-size-report.yml[113-117]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

Comment thread .github/scripts/publish-size-baseline.sh Outdated
Comment thread .github/scripts/publish-size-baseline.sh
MERGE_BASE_SHA=${4:?usage: fetch-size-baseline.sh <main-repo> <builds-repo> <base-ref> <merge-base-sha> <out-dir>}
OUT_DIR=${5:?usage: fetch-size-baseline.sh <main-repo> <builds-repo> <base-ref> <merge-base-sha> <out-dir>}

MAX_WALK=30

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Remediation recommended

4. Ancestor search misses baselines 🐞 Bug ≡ Correctness

The fallback stops after 30 commits even though publishing retains 50 baselines per branch, and
retained baselines are counted per nightly build rather than per intervening commit. A merge-base
can therefore have an available retained ancestor more than 30 commits back, yet
fetch-size-baseline.sh reports found=false and suppresses the size comparison.
Agent Prompt
## Issue description
The fixed 30-commit ancestry limit can terminate before reaching a retained per-commit baseline.

## Issue Context
Retention counts published baselines, not commits, and the nightly workflow is path-filtered, so more than 30 commits may separate two baseline-producing pushes. Make the search bound consistent with actual retained history or traverse until an appropriate retained boundary.

## Fix Focus Areas
- .github/scripts/fetch-size-baseline.sh[43-43]
- .github/scripts/fetch-size-baseline.sh[95-109]
- .github/scripts/publish-size-baseline.sh[35-36]
- .github/workflows/nightly-build.yml[5-20]

ⓘ Copy this prompt and use it to remediate the issue with your preferred AI generation tools

@sensei-hacker

Copy link
Copy Markdown
Member Author

Code review pass complete: the review flagged one CRITICAL issue (pruning's branch-grouping jq capture lacked the (?m) flag — verified: per-commit baselines would all collapse into one bucket and pruning would wipe the less-active branches' baselines) plus robustness items. All fixed in d4d541ee: (?m) flag, explicit all-tag set in the prune awk, exact-baseline retry inside the ancestor walk, and no contradictory comment header. Local verification re-run: 23/23 JS tests pass, prune fixture (3 branches, 52/3/3) deletes exactly the 2 oldest overflow entries, fetch smoke test clean.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

RAM / Flash usage vs. base branch — commit 6a0fb56

Target Flash Δ RAM Δ
MATEKF405 ±0 B (±0.00%) ±0 B (±0.00%)
MATEKF722 ±0 B (±0.00%) ±0 B (±0.00%)
MATEKF765 ±0 B (±0.00%) ±0 B (±0.00%)
MATEKH743 ±0 B (±0.00%) ±0 B (±0.00%)

See RAM/flash optimization guide for techniques to reduce usage.

@github-actions

github-actions Bot commented Aug 29, 2026

Copy link
Copy Markdown

Test firmware build ready — commit 6a0fb56

Download firmware for PR #11835

247 targets built. Find your board's .hex file by name on that page (e.g. MATEKF405SE.hex). Files are individually downloadable — no GitHub login required.

Development build for testing only. Use Full Chip Erase when flashing.

The size-diff workflow stored one baseline per branch, clobbered on every
push, so PRs were compared against the base branch's LATEST tip. When the
base advances after a PR forks, the delta includes unrelated changes (PR
iNavFlight#11785 showed a stale +9,788 B RAM figure that actually belonged to
iNavFlight#11438; the PR's real marginal RAM was ~120 B).

Publish per-commit baselines alongside the branch-tip pointer:
- publish-baseline now stores size-baseline-<COMMIT_SHA> (primary) in
  addition to size-baseline-<BRANCH> (backward-compat latest-tip
  pointer), and prunes per-commit baselines to the newest 50 per branch
  (plus a global cap) so iNavFlight/pr-test-builds can't grow unbounded.
  Per-commit releases carry a machine-readable branch marker in their
  notes so pruning can group them.

Resolve the PR's true base commit on the comparison side:
- pr-comment computes the merge-base of PR head and base ref via the
  compare API (verified to resolve fork-PR head SHAs), fetches the exact
  per-commit baseline, and falls back to the nearest ancestor commit that
  has one by walking the base branch's first-parent chain. The branch-tip
  baseline is deliberately NOT a fallback — comparing against it is the
  stale-delta bug this fixes. All new SHA/ref inputs are regex-validated
  before use in shell commands or release tags.
- The comment header now names the baseline commit used ("vs. base commit
  `abcd123`"), with a note when a nearest-available baseline was used;
  the graceful "no size baseline available" path is unchanged.

Also fixes two pre-existing size-diff-comment tests whose notable-delta
expectations predated NOISE_THRESHOLD_BYTES being raised to 256.
Pruning is housekeeping after the baseline publish; a prune failure must
not fail the nightly publish job (the baseline itself already landed).
Warn loudly instead.
…, header consistency

- publish-size-baseline: jq capture of the notes' `branch:` line needed
  the (?m) flag — without it, ^/$ anchored to the whole (multi-line)
  string, the capture never matched, and every per-commit baseline
  collapsed into the '?' bucket, so pruning treated all branches as one
  and deleted every baseline of the less-active branches (destructive
  data loss). Pruning now also tracks an explicit set of all tags
  instead of relying on awk's create-on-reference side effect.
- fetch-size-baseline: the exact merge-base baseline is now retried as
  the first step of the ancestor walk, so a transient download failure on
  the exact commit degrades to a nearest-ancestor baseline instead of
  skipping the exact commit for that run.
- size-diff-comment: only name the baseline commit in the header when a
  baseline report actually exists (no contradictory header), and reword
  the no-baseline note for accuracy during rollout (pre-existing PRs'
  merge-bases never advance; rebase refreshes them).
@sensei-hacker
sensei-hacker force-pushed the fix-size-baseline-commit-matching branch from d4d541e to 6a0fb56 Compare August 29, 2026 01:48
@sensei-hacker sensei-hacker modified the milestones: 10.0, 9.1 Aug 29, 2026
@sensei-hacker
sensei-hacker changed the base branch from maintenance-10.x to release/9.1 August 29, 2026 01:48
@sensei-hacker

Copy link
Copy Markdown
Member Author

Retargeted to release/9.1 (branch rebased onto release/9.1 — the workflow files are identical on both branches, so the change set is unchanged: the same 6 .github/ files). Milestone updated to 9.1. CI re-running against the new base.

@sensei-hacker
sensei-hacker merged commit 14e7ae9 into iNavFlight:release/9.1 Aug 29, 2026
23 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant