Skip to content

CI: track RAM (.data+.bss) in size reports and gate on per-env budgets - #10899

Merged
thebentern merged 2 commits into
developfrom
ci/ram-size-guardrails
Jul 6, 2026
Merged

CI: track RAM (.data+.bss) in size reports and gate on per-env budgets#10899
thebentern merged 2 commits into
developfrom
ci/ram-size-guardrails

Conversation

@thebentern

@thebentern thebentern commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Motivation

The 2.8.0 nRF52840 heap regression (field reports of 99% heap usage) shipped invisibly: CI tracks flash sizes, but nothing tracks RAM. On nRF52840 the heap arena is the linker gap after .bss, so every byte of static .data+.bss growth shrinks the usable heap 1:1. rak4631 is also at 95.8% of its app flash region (0x27000..0xEA000 = 798,720 B). This PR gives RAM the same CI guardrails flash already has, plus a hard budget gate for the tightest target.

What's added

1. RAM in the size manifests and PR size report

  • bin/platformio-custom.py now emits ram_bytes (.data + .bss summed from size -A on the ELF, via the toolchain's $SIZETOOL) into each .mt.json manifest. Heap/stack placeholder sections (e.g. the nRF52 .heap section) are deliberately excluded - the heap arena is the linker gap, not a static allocation.
  • bin/collect_sizes.py records {"flash_bytes": ..., "ram_bytes": ...} per env; bin/size_report.py grows RAM and RAM-delta columns. Older artifacts without ram_bytes (including legacy int-schema baseline artifacts from previous develop/master runs) degrade gracefully to n/a - never crash.

Sample output (synthetic manifests; heltec-v3's manifest lacks ram_bytes, baseline is mixed legacy/new schema):

2 targets | vs develop: 2 increased, net +2,528 (+2.5 KB)

Target Flash vs develop RAM RAM vs develop
heltec-v3 1,310,720 📈 +1,720 (+1.7 KB) n/a n/a
rak4631 766,000 📈 +808 111,460 📈 +512

Size budgets

Env Metric Measured Budget Used
rak4631 RAM (.data+.bss) 111,460 113,000 98.6%
rak4631 flash 766,000 786,000 97.5%

2. Budget gate (bin/ram_budgets.json + size-budget-gate job)

  • {"<env>": {"ram_bytes": <budget>, "flash_bytes": <budget>}}; enforced only for envs listed in the file (and only when that env was built in the run). "_comment" keys document the semantics.
  • Seeded with rak4631 only: RAM 113,000 (current 110,948 + ~2 KB slack), flash 786,000 (current 765,192 + ~20 KB; region cap is 798,720 and the image must also stay clear of the warm-store record-ring guard in extra_scripts/nrf52_warm_region.py).
  • A new size-budget-gate CI job runs size_report.py --budgets bin/ram_budgets.json --enforce-budgets and fails the build on violation - it is deliberately separate from firmware-size-report, which is informational and continue-on-error. That job now additionally renders the budget-usage table into the PR comment.
  • Failure output names the env, measured value, budget, and overage, and points at the fix:
RAM/flash budget check FAILED:
  - rak4631 RAM (.data+.bss): 115,200 bytes exceeds the budget of 113,000 bytes (over by 2,200)

On nRF52840 the heap arena is the linker gap after .bss, so every
byte of static RAM growth shrinks the usable heap 1:1. Budgets are
raised deliberately, never automatically: if this growth is intended
and reviewed, raise the env's limit in bin/ram_budgets.json in this
same PR and justify the increase in the PR description.

Budgets are raised deliberately, never automatically: bump the limit in bin/ram_budgets.json in the same PR that needs the headroom and justify it in the PR description.

3. Boot heap watermark flag (firmware, opt-in)

At the very end of setup(), under #ifdef MESHTASTIC_HEAP_WATERMARK_CHECK, log LOG_ERROR when less than 20% of the heap is free at boot. Off by default; CI/test builds enable with -DMESHTASTIC_HEAP_WATERMARK_CHECK. Platforms without heap accounting (getHeapSize() returning UINT32_MAX/0, see src/memGet.cpp) are skipped.

Verification

  • python3 bin/test_size_scripts.py: 23/23 pass, including new cases for ram_bytes collection, malformed/missing ram_bytes, legacy-schema baselines, the n/a fallback, and the budget gate (over-RAM, over-flash, under, env-not-built, missing-metric, render-only, --enforce-budgets without --budgets).
  • Section parser validated against real size -A shapes: nRF52 output yields exactly 110,948 (.data 2,504 + .bss 108,444, .heap/.stack_dummy excluded); ESP32 .dram0.data/.dram0.bss are picked up, .flash.rodata/debug sections excluded.
  • End-to-end sanity run of collect_sizes.pysize_report.py against a synthetic manifest pair (with/without ram_bytes) and a mixed-schema baseline - the tables above are that run's output; enforce mode verified exit 1 over budget, exit 0 under.
  • trunk fmt / trunk check clean on touched files (no new issues).
  • No firmware builds run locally; the workflow changes are exercised by this PR's own CI (this PR builds rak4631 in the matrix, so the new gate runs here).

Summary by CodeRabbit

  • New Features
    • Added size reporting with separate Flash and RAM visibility, including optional budget details in generated reports.
    • Introduced a CI gate that can enforce RAM/flash size budgets and fail the build when limits are exceeded.
  • Bug Fixes
    • Improved size summary robustness by supporting both new and legacy manifest formats and ignoring malformed RAM entries.
  • Changes
    • Added an optional startup guard to warn about low available heap during initialization (when enabled).

The 2.8.0 nRF52840 heap regression (99% heap in field reports) shipped
invisibly because CI only tracked flash. On nRF52840 the heap arena is the
linker gap after .bss, so every byte of static .data+.bss growth shrinks the
usable heap 1:1 - RAM needs the same guardrails flash already has.

What's added:

- bin/platformio-custom.py emits ram_bytes (.data + .bss from the ELF, via
  the toolchain size tool) into each .mt.json manifest. Heap/stack
  placeholder sections are deliberately excluded.
- bin/collect_sizes.py records {flash_bytes, ram_bytes} per env;
  bin/size_report.py grows RAM and RAM-delta columns in the PR size report.
  Older artifacts without ram_bytes (and legacy int-schema baselines)
  degrade to "n/a" instead of crashing.
- bin/ram_budgets.json: per-env RAM/flash budgets, enforced only for envs
  listed there. Seeded with rak4631: ram 113,000 (current 110,948 + ~2 KB
  slack), flash 786,000 (current 765,192 + ~20 KB; the app region is
  0x27000..0xEA000 = 798,720 and the image must stay clear of the
  warm-store ring guard in extra_scripts/nrf52_warm_region.py).
- New size-budget-gate CI job runs size_report.py --enforce-budgets and
  fails the build on violation; the informational firmware-size-report job
  now also renders budget usage into the PR comment.
- src/main.cpp: opt-in boot heap watermark (-DMESHTASTIC_HEAP_WATERMARK_CHECK)
  logs LOG_ERROR when less than 20% of the heap is free at the end of
  setup(). Off by default; skipped on platforms without heap accounting.

How budgets are raised: deliberately, never automatically. If a change needs
more headroom, bump the env's limit in bin/ram_budgets.json in the same PR
and justify the increase in the PR description.

Verified: python3 bin/test_size_scripts.py (23/23 pass, including ram_bytes
parsing, n/a fallback, and over/under/missing-env budget-gate cases).
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

⚡ Try this PR in the Web Flasher

Flash this PR in the Web Flasher

firmware commit boards expires

Warning

This is an automated, unreviewed CI test build. Back up your device configuration
before flashing, and only flash devices you are able to recover.

Supported boards built by this PR (26)
Device Board Platform
Crowpanel Adv 3.5 TFT elecrow-adv-35-tft esp32-s3
Heltec HT62 heltec-ht62-esp32c3-sx1262 esp32-c3
Heltec Mesh Node 096 heltec-mesh-node-t096 nrf52840
Heltec Mesh Node T1 heltec-mesh-node-t1 nrf52840
Heltec Mesh Node T114 heltec-mesh-node-t114 nrf52840
Heltec V3 heltec-v3 esp32-s3
Heltec V4 heltec-v4 esp32-s3
Raspberry Pi Pico pico rp2040
Raspberry Pi Pico W picow rp2040
RAK WisMesh Tag rak_wismeshtag nrf52840
RAK WisBlock 11200 rak11200 esp32
RAK WisBlock 11310 rak11310 rp2040
RAK3312 rak3312 esp32-s3
RAK WisBlock 4631 rak4631 nrf52840
Seeed SenseCAP Mesh-Tracker-X1 seeed_mesh_tracker_X1 nrf52840
Seeed Wio Tracker L1 seeed_wio_tracker_L1 nrf52840
Seeed Xiao NRF52840 Kit seeed_xiao_nrf52840_kit nrf52840
Seeed Xiao ESP32-S3 seeed-xiao-s3 esp32-s3
Station G2 station-g2 esp32-s3
Station G3 station-g3 esp32-s3
LILYGO T-Deck t-deck-tft esp32-s3
LILYGO T-Echo t-echo nrf52840
LILYGO T-Echo Plus t-echo-plus nrf52840
LILYGO T-Impulse Plus t-impulse-plus nrf52840
LilyGo T3-C6 tlora-c6 esp32-c6
Seeed SenseCAP T1000-E tracker-t1000-e nrf52840

Build artifacts expire on 2026-08-05. Updated for 337941e.

@github-actions github-actions Bot added needs-review Needs human review enhancement New feature or request labels Jul 6, 2026
@coderabbitai

coderabbitai Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

No actionable comments were generated in the recent review. 🎉

ℹ️ Recent review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: a508e301-eca9-4071-a73e-576c41af644c

📥 Commits

Reviewing files that changed from the base of the PR and between 337941e and 08ae317.

📒 Files selected for processing (5)
  • .github/workflows/main_matrix.yml
  • bin/platformio-custom.py
  • bin/size_report.py
  • bin/test_size_scripts.py
  • src/main.cpp
🚧 Files skipped from review as they are similar to previous changes (3)
  • src/main.cpp
  • bin/platformio-custom.py
  • .github/workflows/main_matrix.yml

📝 Walkthrough

Walkthrough

This PR adds static RAM measurement to firmware manifests, propagates RAM and flash sizes through size collection and reporting, adds budget data and CI enforcement, expands tests for the new size schema, and adds a boot-time heap watermark check in src/main.cpp.

Changes

Size Budget Tracking and CI Enforcement

Layer / File(s) Summary
RAM measurement in build manifest
bin/platformio-custom.py
Adds compute_ram_bytes(env) and writes ram_bytes into generated manifests when available.
Collect sizes schema update
bin/collect_sizes.py, bin/test_size_scripts.py
Changes collected output to {flash_bytes, ram_bytes} and updates tests for the new manifest/output shape.
size_report.py RAM columns and budget checking
bin/size_report.py
Normalizes legacy/current size schemas, renders separate Flash/RAM columns and deltas, and adds budget loading, checks, markdown, and CLI flags.
Budget-gate and RAM rendering tests
bin/test_size_scripts.py
Adds RAM-aware report tests and budget enforcement/report-only coverage, with JSON helpers for fixture setup.
CI budget gate job and budgets config
bin/ram_budgets.json, .github/workflows/main_matrix.yml
Adds budget limits and a CI job that enforces them, while the informational report step now includes budgets.

Boot-time Heap Watermark Check

Layer / File(s) Summary
Heap watermark guard
src/main.cpp
Conditionally includes memGet.h and logs an error in setup() when free heap is below 20% of total heap.

Estimated code review effort: 4 (Complex) | ~45 minutes

Poem

A bunny counted bytes so neat,
Flash and RAM marched hop by hop in feet.
Budgets glowed, and heap checks too,
Thump! CI watched the firmware through.
🐇

🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the main change: RAM tracking and per-environment budget enforcement in CI.
Description check ✅ Passed The description is detailed and covers motivation, changes, and verification, but it omits the repository's attestation checklist.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

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.

Actionable comments posted: 4

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
bin/size_report.py (1)

300-343: 🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

--enforce-budgets can silently pass when no sizes were collected at all.

if not new_sizes: return (pre-existing, Line 307-308) executes before budgets are ever loaded/checked. If collect_sizes.py's output is empty for this run — e.g. an artifact-glob/path misconfiguration in the new size-budget-gate CI job — main() returns with exit code 0 even though --enforce-budgets was requested. This is exactly the kind of silent-pass failure mode the budget gate is meant to prevent, and it's untested (no test in bin/test_size_scripts.py covers empty sizes + --enforce-budgets).

🛡️ Suggested fix
     new_sizes = load_sizes(args.new_sizes)
 
     # Silence output when no targets were built - repo maintainer choice
     if not new_sizes:
+        if args.enforce_budgets:
+            print(
+                "Error: --enforce-budgets requested but no sizes were found "
+                f"in {args.new_sizes}; refusing to silently pass the budget gate",
+                file=sys.stderr,
+            )
+            sys.exit(1)
         return
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@bin/size_report.py` around lines 300 - 343, `main()` in `bin/size_report.py`
returns early on empty `new_sizes`, which lets `--enforce-budgets` exit 0
without ever checking budgets. Move the empty-input handling to happen after the
`args.enforce_budgets`/budget logic, or make it fail fast when
`args.enforce_budgets` is set and `load_sizes(args.new_sizes)` returns nothing.
Add a test in `bin/test_size_scripts.py` covering empty collected sizes with
`--enforce-budgets` to ensure it fails instead of silently passing.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.github/workflows/main_matrix.yml:
- Around line 390-404: The size-budget-gate workflow can silently pass with
missing manifests because the download step in the manifest collection flow uses
continue-on-error and the later fallback creates an empty manifests directory.
Remove the fail-open behavior in the Download current manifests / Collect
current firmware sizes sequence so the job fails closed when manifests cannot be
fetched, and make size_report.py enforcement depend on a complete manifest set
for the gated envs rather than proceeding with missing data.

In `@bin/platformio-custom.py`:
- Around line 48-86: Section-name matching in compute_ram_bytes is too narrow
and target-agnostic. Update the section scan in compute_ram_bytes to explicitly
include RISC-V small-data sections like .sdata/.sbss for ESP32-C3/C6, while
excluding non-main-SRAM sections such as ESP-IDF RTC regions like
.rtc.data/.rtc.bss. Use target-aware logic based on the build environment or
platform so the RAM total reflects only heap-competing SRAM.

In `@bin/size_report.py`:
- Around line 246-269: In budget_markdown, guard the percentage calculation
against zero or negative budgets so a bad entry does not trigger a
ZeroDivisionError. Update the flow around check_budgets/load_budgets to reject
non-positive budget values with a clear validation error, and keep
budget_markdown resilient by handling unexpected invalid budgets when rendering
rows.

In `@src/main.cpp`:
- Around line 1161-1164: Trim the explanatory comment near the heap watermark
guardrail in main.cpp to no more than two lines while preserving the essential
intent. Keep only a concise summary around the heap watermark check and the
low-free-space warning, and remove the extra platform-specific explanation from
the surrounding comment block. Use the existing guardrail comment above the
setup() check as the target for the rewrite.

---

Outside diff comments:
In `@bin/size_report.py`:
- Around line 300-343: `main()` in `bin/size_report.py` returns early on empty
`new_sizes`, which lets `--enforce-budgets` exit 0 without ever checking
budgets. Move the empty-input handling to happen after the
`args.enforce_budgets`/budget logic, or make it fail fast when
`args.enforce_budgets` is set and `load_sizes(args.new_sizes)` returns nothing.
Add a test in `bin/test_size_scripts.py` covering empty collected sizes with
`--enforce-budgets` to ensure it fails instead of silently passing.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro Plus

Run ID: ec2ccc36-5899-42bf-9b31-9a1f8832c962

📥 Commits

Reviewing files that changed from the base of the PR and between 93250d8 and 337941e.

📒 Files selected for processing (7)
  • .github/workflows/main_matrix.yml
  • bin/collect_sizes.py
  • bin/platformio-custom.py
  • bin/ram_budgets.json
  • bin/size_report.py
  • bin/test_size_scripts.py
  • src/main.cpp

Comment thread .github/workflows/main_matrix.yml
Comment thread bin/platformio-custom.py
Comment thread bin/size_report.py
Comment thread src/main.cpp Outdated
@github-actions

github-actions Bot commented Jul 6, 2026

Copy link
Copy Markdown
Contributor

Firmware Size Report

22 targets | vs develop: 21 increased, 1 decreased, net +387,748 (+378.7 KB)

Target Flash vs develop RAM RAM vs develop
picow 1,271,976 📈 +27,096 (+26.5 KB) 152,368 n/a
pico2w 1,246,660 📈 +26,144 (+25.5 KB) 158,848 n/a
rak11310 831,256 📈 +25,832 (+25.2 KB) 86,180 n/a
pico 808,504 📈 +25,816 (+25.2 KB) 85,736 n/a
seeed_xiao_rp2040 806,704 📈 +25,800 (+25.2 KB) 85,728 n/a
Show 17 more target(s)
Target Flash vs develop RAM RAM vs develop
pico2 794,776 📈 +25,000 (+24.4 KB) 92,236 n/a
seeed_xiao_rp2350 792,920 📈 +24,984 (+24.4 KB) 92,228 n/a
t-deck-tft 3,832,064 📈 +21,392 (+20.9 KB) 107,868 n/a
heltec-vision-master-e213-inkhud 2,247,392 📈 +20,032 (+19.6 KB) 92,268 n/a
elecrow-adv-35-tft 3,435,520 📈 +18,896 (+18.5 KB) 94,500 n/a
seeed-xiao-s3 2,292,416 📈 +16,272 (+15.9 KB) 101,900 n/a
heltec-ht62-esp32c3-sx1262 2,150,576 📈 +15,584 (+15.2 KB) 111,864 n/a
heltec-v3 2,279,632 📈 +15,552 (+15.2 KB) 130,084 n/a
rak3312 2,287,600 📈 +15,360 (+15.0 KB) 101,796 n/a
rak11200 1,875,728 📈 +15,120 (+14.8 KB) 86,984 n/a
station-g3 2,281,088 📈 +14,736 (+14.4 KB) 101,708 n/a
station-g2 2,281,072 📈 +14,720 (+14.4 KB) 101,708 n/a
t-eth-elite 2,506,064 📈 +14,576 (+14.2 KB) 102,944 n/a
tlora-c6 2,382,272 📈 +13,968 (+13.6 KB) 102,640 n/a
heltec-v4 2,291,152 📈 +13,856 (+13.5 KB) 100,988 n/a
rak3172 182,492 📉 -3,804 (-3.7 KB) 24,992 n/a
wio-e5 239,444 📈 +816 26,632 n/a

Updated for 97d607b

- size-budget-gate workflow: drop continue-on-error on the manifest
  download and the empty-dir fallback, so the job fails when the data it
  gates on cannot be fetched.
- size_report.py --enforce-budgets now fails closed on every
  missing-data path instead of trivially passing: empty collected sizes,
  a budgeted env that was not built, or a manifest without the budgeted
  metric. Report-only mode keeps rendering those as n/a.
- load_budgets() rejects zero/negative/non-integer budgets with a clear
  error (a typo'd budget could previously crash budget_markdown with
  ZeroDivisionError or silently skip the check); the percentage render
  keeps a defensive guard for direct callers.
- compute_ram_bytes(): count RISC-V small-data sections (.sdata/.sbss)
  and exclude ESP-IDF .rtc.* sections, which live outside the
  heap-competing SRAM.
- Trim the heap-watermark comment in main.cpp to two lines.

bin/test_size_scripts.py: 27/27 - the two fail-open assertions are
flipped to fail-closed, with new report-only counterparts plus cases for
empty sizes under enforcement and malformed budgets.
@thebentern

Copy link
Copy Markdown
Contributor Author

All five review findings addressed in 08ae317:

  1. Gate fails closed — removed continue-on-error + the empty-dir fallback from size-budget-gate, and --enforce-budgets itself now fails on every missing-data path: empty collected sizes, a budgeted env absent from the run, or a manifest lacking the budgeted metric. Report-only mode still renders those as n/a without failing (the informational job keeps working on partial data).
  2. Empty-sizes silent pass — covered by the same change plus a dedicated test.
  3. RAM section matchingcompute_ram_bytes() now counts RISC-V small-data (.sdata/.sbss) and excludes ESP-IDF .rtc.* sections. Kept name-based rather than per-target: the ESP32-C3/C6 linker scripts actually fold small-data into .dram0.data, which was already matched, so the .sdata/.sbss names are a safety net for toolchains that emit them as separate output sections.
  4. Zero/negative budget crashload_budgets() rejects non-positive/non-integer budgets with a clear error (previously a 0 would have crashed the render with ZeroDivisionError, and a quoted "113000" would have been silently skipped — both fail loudly now); the percentage render keeps a defensive guard for direct callers.
  5. Comment length — trimmed the heap-watermark comment to two lines.

bin/test_size_scripts.py: 27/27 — the two tests that codified the fail-open behavior are flipped to assert fail-closed, with new report-only counterparts, plus cases for empty-sizes-under-enforcement and malformed budgets (0, -5, "113000", true).

@thebentern
thebentern merged commit 24c1dcc into develop Jul 6, 2026
82 checks passed
@caveman99
caveman99 deleted the ci/ram-size-guardrails branch August 26, 2026 19:40
Itzdavid01 pushed a commit to Itzdavid01/firmware that referenced this pull request Sep 5, 2026
meshtastic#10899)

* CI: track RAM (.data+.bss) in size reports and gate on per-env budgets

The 2.8.0 nRF52840 heap regression (99% heap in field reports) shipped
invisibly because CI only tracked flash. On nRF52840 the heap arena is the
linker gap after .bss, so every byte of static .data+.bss growth shrinks the
usable heap 1:1 - RAM needs the same guardrails flash already has.

What's added:

- bin/platformio-custom.py emits ram_bytes (.data + .bss from the ELF, via
  the toolchain size tool) into each .mt.json manifest. Heap/stack
  placeholder sections are deliberately excluded.
- bin/collect_sizes.py records {flash_bytes, ram_bytes} per env;
  bin/size_report.py grows RAM and RAM-delta columns in the PR size report.
  Older artifacts without ram_bytes (and legacy int-schema baselines)
  degrade to "n/a" instead of crashing.
- bin/ram_budgets.json: per-env RAM/flash budgets, enforced only for envs
  listed there. Seeded with rak4631: ram 113,000 (current 110,948 + ~2 KB
  slack), flash 786,000 (current 765,192 + ~20 KB; the app region is
  0x27000..0xEA000 = 798,720 and the image must stay clear of the
  warm-store ring guard in extra_scripts/nrf52_warm_region.py).
- New size-budget-gate CI job runs size_report.py --enforce-budgets and
  fails the build on violation; the informational firmware-size-report job
  now also renders budget usage into the PR comment.
- src/main.cpp: opt-in boot heap watermark (-DMESHTASTIC_HEAP_WATERMARK_CHECK)
  logs LOG_ERROR when less than 20% of the heap is free at the end of
  setup(). Off by default; skipped on platforms without heap accounting.

How budgets are raised: deliberately, never automatically. If a change needs
more headroom, bump the env's limit in bin/ram_budgets.json in the same PR
and justify the increase in the PR description.

Verified: python3 bin/test_size_scripts.py (23/23 pass, including ram_bytes
parsing, n/a fallback, and over/under/missing-env budget-gate cases).

* Address review: fail the budget gate closed, fix RAM section matching

- size-budget-gate workflow: drop continue-on-error on the manifest
  download and the empty-dir fallback, so the job fails when the data it
  gates on cannot be fetched.
- size_report.py --enforce-budgets now fails closed on every
  missing-data path instead of trivially passing: empty collected sizes,
  a budgeted env that was not built, or a manifest without the budgeted
  metric. Report-only mode keeps rendering those as n/a.
- load_budgets() rejects zero/negative/non-integer budgets with a clear
  error (a typo'd budget could previously crash budget_markdown with
  ZeroDivisionError or silently skip the check); the percentage render
  keeps a defensive guard for direct callers.
- compute_ram_bytes(): count RISC-V small-data sections (.sdata/.sbss)
  and exclude ESP-IDF .rtc.* sections, which live outside the
  heap-competing SRAM.
- Trim the heap-watermark comment in main.cpp to two lines.

bin/test_size_scripts.py: 27/27 - the two fail-open assertions are
flipped to fail-closed, with new report-only counterparts plus cases for
empty sizes under enforcement and malformed budgets.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

enhancement New feature or request needs-review Needs human review

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant