Skip to content

fix: seq-number persistence rework (#461) + survive comms buffer-pool exhaustion (#471) - #473

Open
Mikefly123 wants to merge 19 commits into
mainfrom
fix/461-seq-persistence
Open

fix: seq-number persistence rework (#461) + survive comms buffer-pool exhaustion (#471)#473
Mikefly123 wants to merge 19 commits into
mainfrom
fix/461-seq-persistence

Conversation

@Mikefly123

Copy link
Copy Markdown
Contributor

Two linked reliability fixes for large UART file transfers, both verified on V5e flight hardware. This PR also pins lib/fprime / lib/fprime-extras to OSSF fork fix branches so CI integration tests run against the new topology/telemetry schema — companion PRs: Open-Source-Space-Foundation/fprime#4, Open-Source-Space-Foundation/fprime-extras#1.


Part 1 — write-ahead batched sequence-number persistence (fixes #461)

TcSecurityDeframer persisted its authentication sequence number to SD synchronously on every accepted command. That per-command write races other components' filesystem operations on the shared FatFs mount, corrupting in-flight file writes. A/B was decisive: writer enabled 1/10 5-chunk uplinks passed; writer disabled 10/10, 20/20, and a 204KB stress passed.

Replaced with write-ahead batched persistence:

  • Persist a high-water mark a stride (100) ahead: zero steady-state FS writes per command; on reboot the counter resumes from the written-ahead value (replayed sequence numbers stay rejected; ground resyncs forward).
  • Torn-write-safe on-disk record (U64 {value, ~value}); invalid/legacy records never block commanding — fall back to 0 with SequenceNumberRecordInvalid WARNING_HI.
  • Fail-safe on persist failure: high-water mark advances only on OP_OK; bounded 10-frame retry backoff + throttled SequenceNumberPersistFailed.

Verified (clean flash + power cycle): 33/33 CRC-value-compared uplinks, 204KB round trip byte-identical at 8.1/7.2 KB/s, zero persist failures, stride boundary crossed cleanly, reboot-replay and invalid-record recovery verified in isolation.

Part 2 — survive buffer-pool exhaustion during SD-stalled uplinks (fixes #471)

An SD write stall mid-uplink backed buffers up until the comms pool exhausted, then a FATAL assert cascade (BufferCollector map overflow → FileUplink queue-full → CmdSequencer schedIn queue-full, 660 asserts/run) stopped the watchdog and left the board byte-silent until power cycle. Instrumentation showed the stalls are card-intrinsic FTL pauses (~1% of writes >50ms, max ~190–250ms observed; rate identical with fsSpace statvfs polling disabled) — unbounded and not removable in firmware, so the fix is headroom + never-FATAL degradation:

This repo:

  • commsFileBuffCount 5→20 (measured HiBuffs 10/10 saturation with old pool on a single 204KB uplink; 12/25 peak now)
  • FileHandling fileUplink queue 10→30 — must hold the entire pool or queue-full FW_ASSERTs
  • ComCcsds tlm queue depth 1→8 — depth 1 silently dropped any second telemetry packet (why buffer health never reached ground)
  • Wire ComCcsdsUart.commsBufferManager.schedIn to rateGroup1Hz; move its TotalBuffs/CurrBuffs/HiBuffs from the packet omit list into the Health packet
  • New acceptance test: 3 consecutive 204KB uplinks asserting the pool returns to baseline with headroom; test_large_round_trip xfail removed

Via submodule pins (this PR):

  • fprime: SpacePacketFramer drops frames (throttled FrameDropped event + substitute comStatus) instead of asserting on alloc failure; CmdSequencer schedIn/pingIn and ActiveRateGroup PingIn get drop semantics
  • fprime-extras: BUFFER_FANOUT_MAX_BUFFERS_IN_FLIGHT 10→40 (must cover the pool)

Verified: full UART file-transfer integration suite — 9 tests including two 204KB round trips — green from one boot; induced pool exhaustion degrades to FrameDropped/NoBuffsAvailable/HLTH_PING_LATE warnings with the board fully commandable throughout. RAM cost ≈ 36KB (~7% of RP2350 SRAM).


🤖 Generated with Claude Code

https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo

Mikefly123 and others added 10 commits July 19, 2026 22:40
Reproduces UART file uplink/downlink failures for multi-chunk transfers:
uplink corrupts silently above ~3-4 chunks (separate, unresolved bug),
and downlink hangs indefinitely on any transfer (root-caused to
downlinkRepeater's BufferRepeater fanning out to a disabled-by-default
LoRa channel that never returns its buffer). Oracle is fileManager's
on-board CalculateCrc vs local zlib.crc32^0xFFFFFFFF for uplink, and
byte-for-byte comparison against the GDS's own downlinked bytes for
downlink. Includes a 1/3/5-chunk parametrized suite plus slow-marked
1000-chunk uplink and downlink stress cases (downlink builds its large
source file via on-board AppendFile rather than the still-broken large
uplink path).

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019gMrPNe6LwGtBS6Y7B5bo8
#457

Root cause (confirmed via HIL A/B testing): Utilities::BufferRepeater only
returns a downlink buffer to fileDownlink once every enabled+connected
multiOut channel has independently returned it. BufferRepeater's own
CHANNEL_ENABLED default is all-channels-ENABLED, but lora.TRANSMIT
defaults to DISABLED on every flight reset, so the LoRa channel's
comQueue never drains its FILE buffer -> fileDownlink wedges
permanently on the very first downlink attempt (matches #457 and #344).

Fix: force downlinkRepeater.CHANNEL_ENABLED to [ENABLED, DISABLED,
DISABLED] (UART-only) on every boot, right after loadParameters() in
ReferenceDeploymentTopology.cpp, mirroring the existing
lora.start(..., TransmitState::DISABLED) override pattern already used
in this file for project-specific boot-time defaults.
paramSet_CHANNEL_ENABLED() is private on the generated component base
(command-dispatch only), so the override drives the real command path:
build a Fw::CmdArgBuffer and invoke downlinkRepeater's cmdIn port
directly with opcode getIdBase() + OPCODE_CHANNEL_ENABLED_SET (0x0).

This is a safe-default fix only -- it does not change BufferRepeater's
fan-out logic. Re-enabling LoRa downlink requires explicitly setting
CHANNEL_ENABLED[1]=ENABLED in lockstep with lora.TRANSMIT ENABLED; HIL
testing showed enabling TRANSMIT alone does not recover an
already-wedged transfer and even fresh transfers only drain slowly
against downlinkDelay's configured cadence.

Verified on hardware (PROVES V5e flight controller): downlink 1 and 3
chunk cases pass cleanly from a cold boot with no manual PRM_SET (302B
and 612B transfers land correctly and instantly). Uplink 1/3 chunks and
downlink-of-a-5-chunk-file remain red -- separate, pre-existing uplink
corruption bug above ~3-4 chunks, out of scope for this fix.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_019gMrPNe6LwGtBS6Y7B5bo8
…tion (issue #457)

Bumps the fprime-zephyr submodule pin to Open-Source-Space-Foundation/fprime-zephyr
branch fix/457-uart-rx-drain (commit 09fdccb), which fixes the UART RX driver's
silent byte-drop-on-full-ring-buffer behavior:

- schedIn_handler now drains the RX ring buffer in a bounded loop each tick
  (capped at ring-capacity/read-size iterations) instead of taking a single
  64-byte bite, so a backlog can be fully drained in one tick instead of
  trickling out while more bytes queue up and overrun.
- serial_cb (the RX ISR) now checks ring_buf_space_get() before reading a
  byte out of the hardware FIFO. Once the ring is full, it disables its own
  RX interrupt (uart_irq_rx_disable) instead of reading-and-dropping. For
  this board's USB-CDC-backed UART, that makes the underlying stack NAK the
  host's bulk-OUT endpoint, so unread bytes queue up in the *host's* USB
  stack (true end-to-end flow control) instead of being silently lost.
  schedIn_handler re-enables RX once draining frees ring space.
- RxRingBufferOverrun (throttled warning event) + RxOverrunCount (telemetry)
  surface any residual drops, which should no longer occur in normal
  operation.

RAM-neutral: RING_BUF_SIZE stays 1024 bytes; SERIAL_BUFFER_SIZE (64->248)
draws from commsBufferManager's existing 1024B-per-buffer pool, adding no
new allocation.

Verified on hardware (PROVES V5e, RP2350/Zephyr): uplink 1/3/4/5/6/8/10
chunks and 5x5-chunk repeats pass reliably; previously uplinks above 3-4
chunks corrupted ~100% of the time. Zero RX ring-buffer overrun or CCSDS
frame-desync events observed across all post-fix test sessions.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
…461)

TcSecurityDeframer::dataIn_handler previously persisted the accepted sequence
number to a file on every single accepted command frame. This synchronous,
per-command filesystem write raced FileUplink/FileManager/FileDownlink/PrmDb's
own filesystem operations on the shared SD-card-backed FatFs mount, causing
intermittent ENOENT on immediate follow-up file access (root cause of #461,
confirmed via extensive HIL A/B testing: 1/10 pass rate with the writer active
vs 10/10 disabled, plus a captured SWD ring-buffer trace showing the writer's
full open/write/sync/close cycle executing inside FileUplink's own open-file
window on a different thread).

Fix: write-ahead batched persistence. The in-RAM sequence number remains
authoritative for runtime acceptance decisions (unchanged). Persistence is
now bounded to at most once every SEQ_NUM_PERSIST_STRIDE (100) accepted
frames, writing accepted + STRIDE as the new high-water mark -- so the
persisted value is always >= any legitimately-accepted sequence number,
preserving the anti-replay guarantee (#426) across an unexpected power loss
while eliminating per-command filesystem contention.

Also adds torn-write safety to the persisted record (value + complement
checksum) and a new SequenceNumberRecordInvalid event: if the record fails
validation on boot (or any non-DOESNT_EXIST read error), the component starts
UNARMED -- rejecting all frames -- rather than defaulting to a low sequence
number that could reopen the anti-replay window. Ground re-arms via the
existing SET_SEQ_NUM command/seq-sync procedure.

No unit tests exist for this component; verified via HIL (SWD reflash +
GDS): 20/20-run 5-chunk uplink campaign, 10/10 1000-byte A/B uplinks (writer
enabled, batching active), reboot-replay protection intact, and 1000-chunk
(204KB) uplink + downlink stress both passing.
…rsist fix

Caught during HIL re-verification: the previous commit's "unarmed" state
rejected ALL frames -- including SET_SEQ_NUM itself -- whenever the persisted
sequence-number record failed validation. Since SET_SEQ_NUM is itself an
authenticated command frame processed through the same dataIn_handler path,
this was a self-inflicted deadlock with no ground recovery possible.

Corrected: an invalid/unreadable persisted record now falls back to sequence
number 0 (identical to the genuine-first-boot path) and command capability
is never blocked. SequenceNumberRecordInvalid still fires so the anomaly is
visible, and ground can still use SET_SEQ_NUM to fast-forward past any
previously-used sequence numbers if the real last-used value is known, but
this is no longer required to restore basic command capability.
…rite failure

Caught during HIL re-verification: writeAheadPersistIfNeeded() previously left
m_persistedHighWater unchanged when writeSequenceNumber() failed, reasoning
that "the next frame retries, no more often than every STRIDE frames" -- that
was wrong. Once the high-water mark is stuck below the ever-advancing accepted
sequence number, EVERY subsequent accepted frame re-satisfies the persist
condition and retries the write, degrading the fix back into a
persist-on-every-frame race (the original #461 bug) for the rest of the boot,
permanently, after a single transient filesystem hiccup. This is the likely
explanation for the "one success then a permanent wall" pattern observed
across multiple HIL campaigns today.

Fix: advance m_persistedHighWater unconditionally, whether or not the write
succeeded. Bounds the retry to the next stride boundary instead of every
frame. Tradeoff: a failed write means the on-disk value can be stale by up to
~2x SEQ_NUM_PERSIST_STRIDE instead of 1x after a power loss -- still bounded,
and the anti-replay invariant still holds since the mark only ever advances
forward.
…-water mark

Security regression in 362209f: advancing m_persistedHighWater
unconditionally -- including on a write FAILURE -- broke the write-ahead
invariant this fix depends on. If persist writes were failing, the on-disk
value would stay stale/low while accepted sequence numbers kept advancing
past it with no bound, so a reboot during a failure streak could reopen a
replay window for the ENTIRE gap since the last successful write, not bounded
by SEQ_NUM_PERSIST_STRIDE at all.

Corrected: m_persistedHighWater now only advances on a CONFIRMED successful
write (Os::File::OP_OK). On failure, a bounded retry-backoff counter
(SEQ_NUM_PERSIST_RETRY_BACKOFF = 10 accepted frames) is set instead of either
(a) advancing anyway (the regression just fixed) or (b) retrying on every
subsequent frame (degrades back to the original #461 persist-per-frame race).
A new throttled SequenceNumberPersistFailed event fires on every retry
attempt, carrying the raw fs status, so a sustained failure streak stays
visible rather than silent.

Invariant restated precisely: whenever a persist has ever succeeded, the
value on disk is always >= the highest sequence number legitimately accepted
at that time. Between successful persists, the gap is bounded by
SEQ_NUM_PERSIST_STRIDE (steady state) or unresolved-and-loudly-flagged
(failure streak) -- never silently unbounded.
…round-trip test

Add a board_only pytest marker so CI can split integration tests across
assets by hardware requirement, and mark uart_file_transfer_test.py with it
(needs only a bare flight controller).

Replace the AppendFile-based test_downlink_large with a direct 204KB
uplink+downlink round trip: the AppendFile workaround predates the large-
uplink fixes and its send_and_assert_command false-fails on out-of-order
board EVR timestamps. The round trip is xfail pending issue #471 (comms
buffer pool leak FATALs the board on the second large uplink of a boot);
removing the xfail is the #471 acceptance gate.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
…(issue #471)

An SD write stall during a large UART file uplink backed buffers up until
the pool exhausted, then a FATAL assert cascade (BufferCollector map
overflow, FileUplink queue-full, CmdSequencer schedIn queue-full) left the
board byte-silent with the watchdog stopped. Project-side fixes:

- commsFileBuffCount 5->20: headroom over the fileUplink queue so routine
  transfers never saturate (measured HiBuffs 10/10 with old pool, 12/25 now)
- FileHandling fileUplink queue 10->30: must hold the entire pool or
  queue-full FW_ASSERTs (FATAL) during a stall
- ComCcsds tlm queue depth 1->8: depth 1 silently dropped any second
  telemetry packet, which is why buffer-pool health never reached ground
- wire ComCcsdsUart.commsBufferManager.schedIn to rateGroup1Hz and move its
  TotalBuffs/CurrBuffs/HiBuffs out of the packet omit list into Health
- acceptance test: 3 consecutive 204KB uplinks asserting pool returns to
  baseline with headroom; remove xfail from test_large_round_trip; retry
  CRC oracle (transient FatFs OTHER_ERROR) and re-uplink once on silent
  frame loss (no ARQ on the link)

Requires companion lib/fprime + lib/fprime-extras changes (submodule pin
bumps pending): SpacePacketFramer drop-instead-of-assert on alloc failure,
BUFFER_FANOUT_MAX_BUFFERS_IN_FLIGHT 10->40, CmdSequencer schedIn/pingIn and
ActiveRateGroup PingIn queue-full 'drop' semantics.

Verified: full UART file-transfer suite (9 tests incl. two 204KB round
trips) green from one boot; overload now surfaces as FrameDropped/
NoBuffsAvailable/HLTH_PING_LATE warnings with the board fully responsive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
…es (issue #471)

- lib/fprime -> Open-Source-Space-Foundation/fprime fix/471-never-fatal-comms
  (SpacePacketFramer drop-instead-of-assert; CmdSequencer schedIn/pingIn and
  ActiveRateGroup PingIn queue-full drop semantics) — OSSF/fprime#4
- lib/fprime-extras -> Open-Source-Space-Foundation/fprime-extras
  fix/471-fanout-map-capacity (BUFFER_FANOUT_MAX_BUFFERS_IN_FLIGHT 10->40)
  — OSSF/fprime-extras#1

Together with the previous commit these make comms buffer-pool exhaustion
survivable instead of FATAL.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
@coderabbitai

coderabbitai Bot commented Jul 24, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@Mikefly123, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 28 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Organization UI

Review profile: ASSERTIVE

Plan: Pro Plus

Run ID: d4991eb6-066c-40f0-a2bc-0f02daccbd68

📥 Commits

Reviewing files that changed from the base of the PR and between 65c068b and c40790f.

📒 Files selected for processing (1)
  • PROVESFlightControllerReference/test/int/uart_file_transfer_test.py
📝 Walkthrough

Walkthrough

Changes

Reboot notifications now fan out to all connected listeners and trigger sequence persistence in UART and LoRa security deframers. UART buffering, telemetry, repeater startup configuration, submodule references, and integration tests were also updated.

Reboot and sequence persistence

Layer / File(s) Summary
Reboot notification fan-out
PROVESFlightControllerReference/Components/ResetManager/*, PROVESFlightControllerReference/Components/Watchdog/*, PROVESFlightControllerReference/ReferenceDeployment/Top/topology.fpp
ResetManager and Watchdog notify all connected ports, while topology forwards reboot signals to both security deframers.
Sequence persistence strategy
PROVESFlightControllerReference/Components/TcSecurityDeframer/*
Sequence records now use validation, write-ahead persistence, bounded retries, and exact planned-reboot persistence.
UART buffering and deployment wiring
PROVESFlightControllerReference/project/config/*, PROVESFlightControllerReference/ReferenceDeployment/Top/*
UART queues and pools are resized, buffer telemetry is exposed, the buffer manager is scheduled, and repeater channels are forced at boot.
UART transfer and integration validation
PROVESFlightControllerReference/test/int/*, pytest.ini
Tests cover CRCs, large transfers, round trips, repeated uplinks, downlinks, sequence resynchronization, safe-mode events, and RTC directory coordination.
Submodule source references
.gitmodules, lib/fprime, lib/fprime-extras, lib/fprime-zephyr
Submodule remotes and pinned commits are updated.

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

Sequence Diagram(s)

sequenceDiagram
  participant ResetManager
  participant Watchdog
  participant ModeManager
  participant TcSecurityDeframer
  ResetManager->>ModeManager: notify prepareForReboot
  Watchdog->>ModeManager: notify prepareForReboot
  ModeManager->>TcSecurityDeframer: forward reboot signal
  TcSecurityDeframer->>TcSecurityDeframer: persist accepted sequence number
Loading

Possibly related issues

Possibly related PRs

Suggested reviewers: ineskhou

Poem

I twitch my nose as signals fly,
Three reboot bells ring through the sky.
Sequence marks are tucked away,
UART hares race through night and day.
Hop, hop, the tests all learn!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Description check ⚠️ Warning The description covers the main changes and testing, but it doesn't follow the required template or include several requested sections. Reformat it to match the template and add explicit sections for Related Issues, How Has Been Tested, Checklist, and Further Notes.
✅ Passed checks (4 passed)
Check name Status Explanation
Title check ✅ Passed The title clearly summarizes the two main fixes: sequence-number persistence rework and buffer-pool exhaustion resilience.
Docstring Coverage ✅ Passed Docstring coverage is 90.91% which is sufficient. The required threshold is 80.00%.
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.

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

Mikefly123 and others added 4 commits July 23, 2026 19:15
# Conflicts:
#	PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi
#	PROVESFlightControllerReference/project/config/ComCcsdsConfig.fpp
#	lib/fprime
#	lib/fprime-extras
#	lib/fprime-zephyr
…ount omit entry

Merge leftover: main removed the ReferenceDeployment. prefix from instance
references; this omit-list line came from the branch side without a
conflict hunk.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
…-rx-drain pin

Keeps the issue-#457 RxOverrunCount channel (referenced by the packet set)
while carrying main's upstream-sync; the plain main pin dropped the channel
and broke dictionary generation in CI.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
… tests off the radio job

Two fixes for the PR-473 integration failures:

1. integration-uart cascade: the #461 write-ahead persistence means the
   board resumes AFTER a reboot expecting a sequence number up to a stride
   ahead of the ground counter (observed: Received=199, LastAccepted=202
   right after the safe-mode reboot in mode_manager tests), so every
   command is rejected until ground burns through the gap -- fixtures time
   out first and every later test file errors in setup. Fix: fan
   prepareForReboot out to both TcSecurityDeframer instances, which now
   persist the EXACT current sequence number on planned reboots (reset
   commands, safe-mode watchdog stop). Ground stays aligned across orderly
   reboots; unplanned resets still resume from the write-ahead mark, which
   remains the security-conservative direction. Watchdog::stop_handler is
   now the single notification point so ModeManager's stopWatchdog port
   path is covered, not just the STOP_WATCHDOG command.

2. integration-radio: uart_file_transfer_test.py was only marked
   board_only, so all its large-file UART tests (204KB transfers) ran over
   the LoRa link in the radio job and accounted for 9 of its 10 failures.
   Mark it uart_only, which the radio job's FILTER already excludes.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo

@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: 8

🤖 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 @.gitmodules:
- Line 3: Update the Makefile’s submodule initialization flow to run git
submodule sync --recursive before git submodule update --init --recursive,
ensuring existing checkouts adopt the current URLs for lib/fprime, submodules,
and lib/fprime-extras.

In `@lib/fprime`:
- Line 1: Update the lib/fprime submodule pointer to a reachable valid F´ commit
available in the repository/refspecs, preferably a valid OSSF fprime commit, and
verify that include(lib/fprime/cmake/FPrime.cmake) and requirement.txt
resolution work with the updated reference.

In
`@PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer.cpp`:
- Around line 259-276: Bound the synchronous persistence triggered by
TcSecurityDeframer::prepareForReboot_handler so watchdog.stop_handler cannot be
stalled by SD-card latency. Replace the direct blocking writeSequenceNumber call
with a bounded/non-blocking mechanism or queue it with an application-level
timeout, while preserving the planned-reboot sequence semantics and updating
persistence state only after a successful write. In Watchdog.cpp lines 49-58,
retain the existing stop fan-out but ensure its prepareForReboot invocation is
no longer blocked by this persistence operation.

In `@PROVESFlightControllerReference/Components/Watchdog/Watchdog.cpp`:
- Around line 49-58: Update Watchdog::stop_handler so the hardware reset
countdown begins immediately by setting m_run = false before the
prepareForReboot notification fan-out. Keep the existing prepareForReboot_out
calls for all connected ports, but ensure their potentially blocking work cannot
delay the watchdog stop.

In `@PROVESFlightControllerReference/project/config/ComCcsdsConfig.fpp`:
- Around line 46-50: Update the comment above commsFileBuffCount in
ComCcsdsConfig.fpp to reference the current fileUplink queue size and accurately
describe the dependency with FileHandlingConfig.fpp, including the active
commsBuffCount and commsFileBuffCount values. Remove the stale claim that
fileUplink is sized at 10 and ensure both cross-referencing comments
consistently explain the queue sizing relationship.

In
`@PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp`:
- Around line 150-157: The channel-enable command invocation currently hardcodes
opcode 0x0; replace OPCODE_CHANNEL_ENABLED_SET with the generated
CHANNEL_ENABLED_SET opcode constant from Utilities::BufferRepeater, preserving
the existing command invocation and arguments.
- Around line 146-159: Replace the locally hardcoded OPCODE_CHANNEL_ENABLED_SET
value in the downlinkRepeater command-dispatch block with the generated
BufferRepeater opcode constant. Import or reference the generated symbol
directly, preserving the existing invoke call while ensuring interface changes
produce a compile-time failure.

In `@PROVESFlightControllerReference/test/int/uart_file_transfer_test.py`:
- Around line 210-215: Replace direct accesses to
downlinker._FileDownlinker__directory in test_large_round_trip and
test_downlink_n_chunks with a small shared _downlink_path(downlinker, name)
helper. Use a public IntegrationTestAPI/FileDownlinker download-directory
accessor if available; otherwise centralize the existing path construction
inside the helper and update all diagnostic references to use it.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: 1e8c57a1-8197-42af-a147-d1bc546f441f

📥 Commits

Reviewing files that changed from the base of the PR and between 24968fa and b0bc46d.

📒 Files selected for processing (18)
  • .gitmodules
  • PROVESFlightControllerReference/Components/ResetManager/ResetManager.cpp
  • PROVESFlightControllerReference/Components/ResetManager/ResetManager.fpp
  • PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer.cpp
  • PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer.fpp
  • PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer.hpp
  • PROVESFlightControllerReference/Components/Watchdog/Watchdog.cpp
  • PROVESFlightControllerReference/Components/Watchdog/Watchdog.fpp
  • PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentPackets.fppi
  • PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp
  • PROVESFlightControllerReference/ReferenceDeployment/Top/topology.fpp
  • PROVESFlightControllerReference/project/config/ComCcsdsConfig.fpp
  • PROVESFlightControllerReference/project/config/FileHandlingConfig.fpp
  • PROVESFlightControllerReference/test/int/uart_file_transfer_test.py
  • lib/fprime
  • lib/fprime-extras
  • lib/fprime-zephyr
  • pytest.ini

Comment thread .gitmodules
[submodule "lib/fprime"]
path = lib/fprime
url = https://github.com/nasa/fprime.git
url = https://github.com/Open-Source-Space-Foundation/fprime.git

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.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== files =="
git ls-files | sed -n '1,120p'

echo
echo "== .gitmodules =="
cat -n .gitmodules | sed -n '1,80p'

echo
echo "== Makefile submodule references =="
if [ -f Makefile ]; then
  cat -n Makefile | sed -n '1,80p'
else
  fd -a 'Makefile$|makefile$' . | while read -r f; do
    echo "--- $f"
    cat -n "$f" | sed -n '1,120p'
  done
fi

echo
echo "== submodule sync/update references =="
rg -n "submodule|git submodule|sync|--init|--recursive|url =|github.com/Open-Source-Space-Foundation" -S . --glob '!build/**' --glob '!node_modules/**' --glob '!dist/**' --glob '!*.lock' || true

echo
echo "== changed files/stat =="
git diff --stat || true
git diff -- .gitmodules Makefile 2>/dev/null | sed -n '1,220p' || true

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 50406


🌐 Web query:

git submodule sync --recursive updates .git/config cached URL documentation

💡 Result:

The git submodule sync command is used to synchronize the remote URL configuration of your submodules with the values defined in your.gitmodules file [1][2]. When you run git submodule sync --recursive, this synchronization process is applied to all registered submodules and recursively to any nested submodules within them [1][3]. The command works by updating the submodule URL settings stored in your local.git/config file, ensuring they match the URLs currently specified in the.gitmodules file [1][4]. It only affects submodules that have already been initialized (i.e., those that already have a URL entry in.git/config) [1][5][3]. This command is necessary when the upstream URL of a submodule has changed (for example, if a project moves to a different hosting platform) [1][2]. If you pull updates that include a new URL in.gitmodules but do not sync, your local repository may attempt to fetch from the old, potentially obsolete URL, which can cause git submodule update to fail [2]. After running git submodule sync --recursive, you typically need to follow up with git submodule update --init --recursive to actually fetch and check out the commits from the new, synchronized URL [2][6].

Citations:


Synchronize submodule URLs for existing checkouts.

lib/fprime, submodules, and lib/fprime-extras now point to Open-Source-Space-Foundation, but Makefile only runs git submodule update --init --recursive. Existing initialized checkouts keep the cached old URLs in .git/config and can fail to fetch if the old remotes are unavailable.

Add git submodule sync --recursive before submodule updates, or document the one-time migration command.

🤖 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 @.gitmodules at line 3, Update the Makefile’s submodule initialization flow
to run git submodule sync --recursive before git submodule update --init
--recursive, ensuring existing checkouts adopt the current URLs for lib/fprime,
submodules, and lib/fprime-extras.

Comment thread lib/fprime
@@ -1 +1 @@
Subproject commit 8a62e455a90b6d4f498c332d45d65a2a819988d8
Subproject commit fc2d7aa8aaaf22419848f2af3aae63c5a0c325dc

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.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

old_commit="$(git rev-parse HEAD^:lib/fprime)"
new_commit="$(git rev-parse HEAD:lib/fprime)"

git -C lib/fprime cat-file -e "${new_commit}^{commit}"
git -C lib/fprime diff --stat "$old_commit" "$new_commit"
git -C lib/fprime diff "$old_commit" "$new_commit" -- \
  cmake/FPrime.cmake requirements.txt

make generate
make build
make fmt

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 364


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== git repo =="
git rev-parse --show-toplevel
git rev-parse --is-shallow-repository || true
git rev-parse --is-bare-repository || true

echo "== lib/fprime index refs =="
git ls-files -s | grep -E ' lib/fprime$|^lib/fprime/' | head -50 || true
python3 - <<'PY'
import subprocess
for obj in ['HEAD', 'HEAD^', 'HEAD~2']:
    try:
        print(obj, subprocess.check_output(['git','rev-parse','--verify','--quiet','%s:lib/fprime' % obj], text=True, stderr=subprocess.DEVNULL).strip())
    except Exception as e:
        print(obj, "missing", type(e).__name__, str(e))
PY

echo "== submodule config =="
git config --file .gitmodules --list 2>/dev/null || true
git config --local --file .gitmodules --list 2>/dev/null || true
cat .git/config 2>/dev/null | sed -n '/\[submodule "lib\/fprime"\]/,/]\n\]/p' || true

echo "== repo refs possibly mentioning fprime commit or related =="
git rev-parse --verify fc2d7aa8aaaf22419848f2af3aae63c5a0c325dc^{} 2>/dev/null || true
git cat-file -t fc2d7aa8aaaf22419848f2af3aae63c5a0c325dc 2>/dev/null || true
git for-each-ref --format='%(refname:short) %(objectname)' | grep -F fc2d7aa8aaaf22419848f2af3aae63c5a0c325dc || true
git log --oneline --all --grep='fprime\|FPrime\|lib/fprime' -20 2>/dev/null || true

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 2423


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== current diff stat for submodule path =="
git diff --stat HEAD^..HEAD -- lib/fprime || true
git diff -- lib/fprime | head -80 || true

echo "== repository files that reference lib/fprime =="
rg -n "lib/fprime|FPrime\.cmake|lib/fprime/requirements" -S --glob '!*.png' --glob '!*.jpg' . || true

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 1694


Fix or drop the F´ submodule reference.

lib/fprime is now a submodule pointer, but fc2d7aa8aaaf22419848f2af3aae63c5a0c325dc is not present in the repository/refspecs, and HEAD/HEAD^ both point at the same missing ref. As-is, anyone fetching and building this branch will fail at include(lib/fprime/cmake/FPrime.cmake) and requirement.txt resolution. Rebase to a reachable OSSF fprime commit, or fix the submodule to a valid commit before merging.

🤖 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 `@lib/fprime` at line 1, Update the lib/fprime submodule pointer to a reachable
valid F´ commit available in the repository/refspecs, preferably a valid OSSF
fprime commit, and verify that include(lib/fprime/cmake/FPrime.cmake) and
requirement.txt resolution work with the updated reference.

Comment on lines +259 to 276
void TcSecurityDeframer ::prepareForReboot_handler(FwIndexType portNum) {
// Planned reboot: persist the EXACT current sequence number, not the write-ahead
// high-water mark. On the next boot the counter resumes at precisely the last
// accepted value, so ground (at lastAccepted + 1) stays inside the acceptance
// window with no resync needed. Unplanned reboots (crash/power loss) still resume
// from the write-ahead mark -- that direction is the security-conservative one.
Os::ScopeLock lock(this->m_sequenceNumberLock);
const Os::File::Status status = this->writeSequenceNumber(this->m_sequenceNumber);
if (status == Os::File::OP_OK) {
// Disk now equals lastAccepted: the next accepted frame is at/above the mark,
// which re-triggers a normal write-ahead persist after the reboot.
this->m_persistedHighWater = this->m_sequenceNumber;
this->m_persistRetryBackoff = 0;
}
// On failure writeSequenceNumber already emitted SequenceNumberWriteFailed; the
// stale (higher) write-ahead record stays on disk, which is safe -- it just means
// ground must resync forward after this reboot, same as before this handler existed.
}

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.

🩺 Stability & Availability | 🔴 Critical | 🏗️ Heavy lift

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== repo files matching relevant names =="
git ls-files | rg '(^|/)(TcSecurityDeframer\.(fpp|cpp|hpp)|Watchdog\.(fpp|cpp|hpp)|\.*)(\.fpp|\.cpp|\.hpp)$' | sed -n '1,120p'

echo
echo "== target snippets =="
fd -a 'TcSecurityDeframer\.cpp|Watchdog\.cpp' . | while read -r f; do
  echo "--- $f"
  wc -l "$f"
done

echo
echo "== TcSecurityDeframer prepareForReboot/sequence write area =="
path=$(git ls-files | rg 'TcSecurityDeframer\.cpp$' | head -n1 || true)
if [ -n "${path:-}" ]; then
  nl -ba "$path" | sed -n '220,305p'
fi

echo
echo "== Watchdog stop handler area =="
path=$(git ls-files | rg 'Watchdog\.cpp$' | head -n1 || true)
if [ -n "${path:-}" ]; then
  nl -ba "$path" | sed -n '1,120p'
fi

echo
echo "== prepareForReboot references =="
rg -n "prepareForReboot|stopWatchdog|fatalHandler|modeManager|stop_handler|prepareForReboot_handler|writeSequenceNumber" -S .

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 229


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== top-level directory =="
pwd
ls -la | sed -n '1,80p'

echo
echo "== tracked files by root =="
git ls-files | sed -n '1,200p'

echo
echo "== search relevant filenames =="
git ls-files | rg -i 'TcSecurityDeframer|Watchdog|SecurityDeframer|Security' | sed -n '1,200p'

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 17554


🏁 Script executed:

#!/bin/bash
set -euo pipefail

deframer=$(git ls-files | rg 'PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer\.cpp$' | head -n1)
deframer_h=$(git ls-files | rg 'PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer\.hpp$' | head -n1)
watchdog=$(git ls-files | rg 'PROVESFlightControllerReference/Components/Watchdog/Watchdog\.cpp$' | head -n1)

echo "--- $deframer ($(wc -l < "$deframer") lines) ---"
nl -ba "$deframer" | sed -n '220,310p'

echo
echo "--- tdeframer relevant symbols ---"
rg -n "writeSequenceNumber|prepareForReboot_handler|prepareForReboot|Fp|Fw|invoke_|dispatch|" "$deframer" "$deframer_h" | sed -n '1,200p'

echo
echo "--- $watchdog ($(wc -l < "$watchdog") lines) ---"
nl -ba "$watchdog" | sed -n '1,120p'

echo
echo "--- watchdog relevant stop/fan-out code ---"
rg -n "stopHandler|stop_watchdog|prepareForReboot_out|prepareForReboot|getNum_prepareForReboot_OutputPorts|isConnected_prepareForReboot_OutputPort" "$watchdog" -S

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 333


🏁 Script executed:

#!/bin/bash
set -euo pipefail

deframer=$(git ls-files | rg 'PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer\.cpp$' | head -n1)
deframer_h=$(git ls-files | rg 'PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer\.hpp$' | head -n1)
watchdog=$(git ls-files | rg 'PROVESFlightControllerReference/Components/Watchdog/Watchdog\.cpp$' | head -n1)

echo "--- $deframer ($(wc -l "$deframer" | awk '{print $1}') lines) ---"
sed -n '220,310p' "$deframer" | cat -n

echo
echo "--- tdeframer relevant symbols ---"
grep -n -E "writeSequenceNumber|prepareForReboot_handler|prepareForReboot|Fp|Fw|invoke_|dispatch|osFile_write|Os::File::write" "$deframer" "$deframer_h" | sed -n '1,200p' || true

echo
echo "--- $watchdog ($(wc -l "$watchdog" | awk '{print $1}') lines) ---"
sed -n '1,120p' "$watchdog" | cat -n

echo
echo "--- watchdog relevant stop/fan-out code ---"
grep -n -E "stopHandler|stop_watchdog|prepareForReboot_out|prepareForReboot|getNum_prepareForReboot_OutputPorts|isConnected_prepareForReboot_OutputPort|waitCommandQueue|Task" "$watchdog" -S || true

echo
echo "--- global prepareForReboot references ---"
grep -R -n "prepareForReboot" PROVESFlightControllerReference/components PROVESFlightControllerReference/Components PROVESFlightControllerReference/test -S | sed -n '1,240p' || true

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 15541


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "--- FileHelper implementation/declaration candidates ---"
git ls-files | rg 'FileHelper\.(hpp|cpp)|Utilities|File' | sed -n '1,240p'

echo
echo "--- FileHelper content ---"
f=$(git ls-files | rg 'FileHelper\.hpp$|FileHelper\.cpp$' | head -n1 || true)
if [ -z "${f:-}" ]; then
  f1=$(git ls-files | rg 'FileHelper\.hpp$' | head -n1 || true)
  f2=$(git ls-files | rg 'FileHelper\.cpp$' | head -n1 || true)
  for f in "${f1:-}" "${f2:-}"; do
    [ -n "$f" ] || continue
    echo "+++ $f ($(wc -l "$f" | awk '{print $1}') lines) +++"
    sed -n '1,220p' "$f" | cat -n
  done
fi

echo
echo "--- watchdog prepareForReboot references ---"
grep -R -- 'prepareForReboot\|stopWatchdog\|fatalHandler' PROVESFlightControllerReference/Components/Watchdog PROVESFlightControllerReference/Components/ModeManager PROVESFlightControllerReference -g '*.fpp' -g '*.cpp' -g '*.hpp' | sed -n '1,240p'

echo
echo "--- deframer fprime component interfaces ---"
sed -n '1,130p' PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer.fpp | cat -n
sed -n '1,120p' PROVESFlightControllerReference/Components/Watchdog/Watchdog.fpp | cat -n

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 9132


🌐 Web query:

Fprime sync input port invocation model timeout receive queue asynchronous port timeout-less

💡 Result:

In the F´ (F Prime) framework, port invocation behavior is determined by the port's kind, which dictates whether it operates synchronously or asynchronously [1][2][3]. Synchronous (sync) Input Ports: These ports operate like a standard function call [1][4]. When a component invokes a synchronous output port connected to a sync input port, the code runs immediately in the execution context of the invoking component (i.e., on the invoker's thread) [1][2][5]. Because these are direct function calls, they are inherently "timeout-less" at the framework level; there is no queue or underlying mechanism that manages timeouts for these invocations [1][6][5]. If the invoked handler hangs or takes a long time, the caller is blocked for that duration [4]. Asynchronous (async) Input Ports: These ports are backed by a message queue in the receiver component [4]. When an output port invokes an async input port, the invocation (message) is placed into the receiver's queue, and the caller returns immediately [4][7]. The actual function handler is later dispatched by the receiver's own thread when it processes its queue [1][2][6]. Because the invocation is decoupled from the caller, the framework does not implement a "timeout" for individual asynchronous port calls [1][4]. Timeouts in F´: The F´ framework does not provide a built-in timeout mechanism for general port invocations. Timeouts are typically managed at the application level through design patterns [7][8]. For example, the Svc::Health component monitors component responsiveness by periodically sending "ping" messages via asynchronous ports and tracking the time taken for a response to return via a separate port [9][8]. If a response does not arrive within a configured number of cycles (rate group ticks), the health service flags a timeout and can trigger events (e.g., WARNING or FATAL) [9][8]. Summary of invocation models: - Sync Input: Direct function call, runs on the caller's thread, no queue, no framework-level timeout [1][6][5]. - Async Input: Queued, runs on the receiver's thread, requires an active/queued component, no framework-level timeout [1][4][5]. - Timeouts: Explicitly managed by application-level logic (e.g., Svc::Health), not by the port invocation itself [9][8].

Citations:


🌐 Web query:

FprimeExtras Utilities FileHelper writeToFile FPrime FileHelper.cpp source

💡 Result:

There is no standard or official component named "FprimeExtras" or "FileHelper" in the core F Prime (F´) framework that provides a writeToFile utility [1][2]. File operations in F Prime are handled by the Operating System abstraction layer, specifically the Os::File class [3][4][5]. If you are looking for how to write data to a file in F Prime, you should use the Os::File class. The standard interface for writing data is the write method, which is defined in the Os::File header [4][5][6]: Status write(const U8* buffer, FwSizeType& size); Status write(const U8* buffer, FwSizeType& size, WaitType wait) override; The Os::File::write method writes data from a buffer to the file [4][5]. When using the overloaded version with a WaitType parameter, you can specify whether the call should block (WAIT) until the data is written to disk or return immediately (NO_WAIT) after the data is handed off to the operating system [4][5]. If you encountered a reference to a "FileHelper" class, it may be part of a custom project-specific utility or an external library not included in the core F Prime framework (e.g., Grid Solutions Framework uses a FileHelper class, but it is unrelated to F Prime) [7]. Top Results: [3] https://fprime.jpl.nasa.gov/latest/docs/reference/api/cpp/html/_os_2_file_8cpp_source.html [4] https://fprime.jpl.nasa.gov/v3.6.0/docs/reference/api/cpp/html/class_os_1_1_file.html [5] https://fprime.jpl.nasa.gov/devel/Os/File.hpp [6] https://fprime.jpl.nasa.gov/v4.0.0/docs/reference/api/cpp/html/_file_8hpp_source.html

Citations:


🌐 Web query:

"FileHelper.hpp" "writeToFile" "Os::File"

💡 Result:

The F' (F Prime) flight software framework provides the Os::File class for file operations, defined in the header file Os/File.hpp [1][2]. The Os::File class does not typically contain a helper class named FileHelper.hpp with a static writeToFile method. Instead, file writing is performed directly through the member functions of the Os::File class [3][4]. To write to a file using Os::File, you must: 1. Instantiate an Os::File object [3]. 2. Open the file using the open method, specifying the file path and the desired mode (e.g., Os::File::OPEN_WRITE or Os::File::OPEN_CREATE) [1][3]. 3. Use the write member function to transfer data from a buffer to the file [1][4]. The Os::File::write method is overloaded [3][4]: - Status write(const U8* buffer, FwSizeType& size): Writes data from the buffer up to the specified size. This version blocks until the requested data has been written [1][4]. - Status write(const U8* buffer, FwSizeType& size, WaitType wait): Allows specifying whether to wait for the write to complete (WAIT) or return once the data is sent to the OS (NO_WAIT) [3][4]. If you are looking for a higher-level "writeToFile" utility, it is not part of the standard F' Os::File API and would need to be implemented as a custom helper function in your application code using the provided Os::File interface [1][4]. Note: The Go programming language standard library also features an os package with functions like os.WriteFile, which is distinct from the F' C++ framework [5]. Ensure you are referencing the correct documentation for your specific environment.

Citations:


Bound the SD-card persist inside the synchronous reboot fan-out. watchdog.stop_handler() invokes all prepareForReboot receivers on the stop path before m_run = false, and TcSecurityDeframer::prepareForReboot_handler() persists m_sequenceNumber synchronously via writeSequenceNumber(). A stalled SD-card persist can therefore delay every receiver in the failsafe reboot chain, including resetting m_run. Use a bounded/non-blocking persist, or queue this persist with an application-level timeout so the watchdog-stops are no longer blocked by SD-card latency.

🧰 Tools
🪛 Clang (14.0.6)

[warning] 266-266: variable 'status' is not initialized

(cppcoreguidelines-init-variables)

📍 Affects 2 files
  • PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer.cpp#L259-L276 (this comment)
  • PROVESFlightControllerReference/Components/Watchdog/Watchdog.cpp#L49-L58
🤖 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
`@PROVESFlightControllerReference/Components/TcSecurityDeframer/TcSecurityDeframer.cpp`
around lines 259 - 276, Bound the synchronous persistence triggered by
TcSecurityDeframer::prepareForReboot_handler so watchdog.stop_handler cannot be
stalled by SD-card latency. Replace the direct blocking writeSequenceNumber call
with a bounded/non-blocking mechanism or queue it with an application-level
timeout, while preserving the planned-reboot sequence semantics and updating
persistence state only after a successful write. In Watchdog.cpp lines 49-58,
retain the existing stop fan-out but ensure its prepareForReboot invocation is
no longer blocked by this persistence operation.

Comment on lines +49 to +58
// Stopping the watchdog leads to a hardware reset once petting ceases, so this
// IS the planned-reboot notification point for every stop path (ground command
// via STOP_WATCHDOG and ModeManager's safe-mode stopWatchdog port alike).
for (FwIndexType i = 0; i < this->getNum_prepareForReboot_OutputPorts(); i++) {
if (this->isConnected_prepareForReboot_OutputPort(i)) {
this->prepareForReboot_out(i);
}
}

// Stop the watchdog

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.

🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift

Failsafe stop path now gated behind reboot-notification fan-out.

stop_handler is reachable from modeManager.stopWatchdog (autonomous command-loss recovery, per topology.fpp) and CdhCore.fatalHandler.stopWatchdog, not just the ground STOP_WATCHDOG command. Setting m_run = false — which starts the hardware-watchdog reset countdown — is now delayed by the full prepareForReboot_out fan-out, including TcSecurityDeframer's file write. See the root-cause comment on TcSecurityDeframer.cpp's prepareForReboot_handler.

🤖 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 `@PROVESFlightControllerReference/Components/Watchdog/Watchdog.cpp` around
lines 49 - 58, Update Watchdog::stop_handler so the hardware reset countdown
begins immediately by setting m_run = false before the prepareForReboot
notification fan-out. Keep the existing prepareForReboot_out calls for all
connected ports, but ensure their potentially blocking work cannot delay the
watchdog stop.

Comment on lines +46 to +50
# issue #471: must exceed FileHandling fileUplink queue size (10) plus
# in-pipeline slack, or a stalled SD write exhausts the pool mid-uplink
# and the AllocationError FATALs the board (HiBuffs measured at 10/10
# during a single 204KB uplink with the old count of 5).
constant commsFileBuffCount = 20

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Comment cites a stale fileUplink queue size.

This comment says commsFileBuffCount must exceed "FileHandling fileUplink queue size (10)", but FileHandlingConfig.fpp (same PR) raises fileUplink to 30, and its own comment describes the reverse dependency (fileUplink sized to hold commsBuffCount + commsFileBuffCount = 25). The two comments cross-reference stale/inconsistent numbers, which will mislead the next engineer who has to re-tune these constants.

✏️ Suggested comment fix
-        # issue `#471`: must exceed FileHandling fileUplink queue size (10) plus
-        # in-pipeline slack, or a stalled SD write exhausts the pool mid-uplink
-        # and the AllocationError FATALs the board (HiBuffs measured at 10/10
-        # during a single 204KB uplink with the old count of 5).
+        # issue `#471`: sized together with FileHandlingConfig.QueueSizes.fileUplink
+        # (30) -- commsBuffCount(5) + commsFileBuffCount(20) = 25 buffers must all
+        # fit in the fileUplink queue with slack, or a stalled SD write exhausts
+        # the pool mid-uplink and the AllocationError FATALs the board (HiBuffs
+        # measured at 10/10 during a single 204KB uplink with the old count of 5).
         constant commsFileBuffCount    = 20
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
# issue #471: must exceed FileHandling fileUplink queue size (10) plus
# in-pipeline slack, or a stalled SD write exhausts the pool mid-uplink
# and the AllocationError FATALs the board (HiBuffs measured at 10/10
# during a single 204KB uplink with the old count of 5).
constant commsFileBuffCount = 20
# issue `#471`: sized together with FileHandlingConfig.QueueSizes.fileUplink
# (30) -- commsBuffCount(5) + commsFileBuffCount(20) = 25 buffers must all
# fit in the fileUplink queue with slack, or a stalled SD write exhausts
# the pool mid-uplink and the AllocationError FATALs the board (HiBuffs
# measured at 10/10 during a single 204KB uplink with the old count of 5).
constant commsFileBuffCount = 20
🤖 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 `@PROVESFlightControllerReference/project/config/ComCcsdsConfig.fpp` around
lines 46 - 50, Update the comment above commsFileBuffCount in ComCcsdsConfig.fpp
to reference the current fileUplink queue size and accurately describe the
dependency with FileHandlingConfig.fpp, including the active commsBuffCount and
commsFileBuffCount values. Remove the stale claim that fileUplink is sized at 10
and ensure both cross-referencing comments consistently explain the queue sizing
relationship.

Comment on lines +146 to +159
{
// BufferRepeater's paramSet_CHANNEL_ENABLED() is private (only the command-dispatch path may
// call it), so drive it the same way a real CHANNEL_ENABLED_PRM_SET command would: build the
// command argument buffer and invoke the component's cmdIn port directly. Opcode 0x0 is
// OPCODE_CHANNEL_ENABLED_SET (see generated BufferRepeaterComponentAc.hpp) -- the first/only
// settable param on this component, stable as long as BufferRepeater.fpp isn't changed.
Utilities::BufferRepeater_OutputChannelEnables uartOnlyChannelEnables(
{Fw::Enabled::ENABLED, Fw::Enabled::DISABLED, Fw::Enabled::DISABLED});
Fw::CmdArgBuffer channelEnablesArgs;
(void)channelEnablesArgs.serialize(uartOnlyChannelEnables);
constexpr FwOpcodeType OPCODE_CHANNEL_ENABLED_SET = 0x0;
downlinkRepeater.get_cmdIn_InputPort(0)->invoke(downlinkRepeater.getIdBase() + OPCODE_CHANNEL_ENABLED_SET, 0,
channelEnablesArgs);
}

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.

🩺 Stability & Availability | 🔵 Trivial | 💤 Low value

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Inspect downlinkRepeater's command wiring and CmdDispatcher's response handling
ast-grep outline PROVESFlightControllerReference/ReferenceDeployment/Top/topology.fpp --match 'downlinkRepeater' --items all
rg -n -A3 -B3 'downlinkRepeater' PROVESFlightControllerReference/ReferenceDeployment/Top/topology.fpp
fd -i 'CommandDispatcherComponentImpl' lib/fprime

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 1385


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== topology cmd/dispatch wiring =="
rg -n 'CompCmd|cmdResponse|CmdDispatcher|cmdIn|cmdOut|seqCmdBuff|cmdResponseOut' PROVESFlightControllerReference/ReferenceDeployment/Top/topology.fpp || true

echo
echo "== downlinkRepeater usages in repo =="
rg -n 'downlinkRepeater|Channel_Enabled|CHANNEL_ENABLED|BufferRepeater' PROVESFlightControllerReference/ReferenceDeployment/Top topogen GENERATED 2>/dev/null || true

echo
echo "== generated topology sources containing downlinkRepeater =="
fd -i 'ReferenceDeploymentTopology.*' . -t f -x sh -c 'echo "--- $1"; rg -n -A5 -B5 "downlinkRepeater" "$1" || true' sh {}

echo
echo "== locate BufferRepeater component and CmdDispatcher/component implementations =="
fd -i 'BufferRepeater.*|CommandDispatcher.*' . -t f | sed -n '1,120p'

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 6968


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== downlinkRepeater component instance declaration in instances.fpp =="
sed -n '200,235p' PROVESFlightControllerReference/ReferenceDeployment/Top/instances.fpp

echo
echo "== generated/downstream command-response port declarations involving downlinkRepeater =="
rg -n -A2 -B2 'downlinkRepeater|BufferRepeater' ./PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopologyDefs.hpp ./PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp 2>/dev/null || true

echo
echo "== find all cmdResponseIn/compCmdStat wiring in generated topology headers/cpp =="
rg -n 'cmdResponseIn|compCmdStat|BufferRepeater::.*Cmd|OpcodeChannelEnabled' ./PROVESFlightControllerReference/ReferenceDeployment/Top ./ 2>/dev/null || true

echo
echo "== locate generated BufferRepeater source/header =="
fd -i 'BufferRepeater.*' . -t f | sed -n '1,200p'

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 9667


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== git files matching BufferRepeater =="
git ls-files | rg -i 'BufferRepeater|bufferrepeater|lib/fprime-extra|fprime-extras|fprime' || true

echo
echo "== all repository references to Utilities.BufferRepeater/BufferRepeater =="
rg -n 'Utilities\.BufferRepeater|BufferRepeater|OPCODE_CHANNEL_ENABLED|CHANNEL_ENABLED_SET|cmdResponseOut|cmdResponse_out|ResponseCmd|cmdResponse_out' . || true

echo
echo "== current git status diff stat/name-only =="
git diff --stat HEAD 2>/dev/null || true
git diff --name-only HEAD 2>/dev/null || true

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 37784


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== commands / references within fprime checkout tree =="
rg -n 'DOWNLINK_REPEAT|CHANNEL_ENABLED|Channel_Enabled|OPCODE_CHANNEL_ENABLED|paramSet_CHANNEL_ENABLED|BufferRepeater' lib/fprime lib/fprime-extras PROVESFlightControllerReference/project/config AcConstants.fpp 2>/dev/null || true

echo
echo "== generate topology sources containing cmdResponse/compCmdStat/wiring for BufferRepeater =="
rg -n 'cmdResponseOut|compCmdStat|cmdIn|seqCmdBuff|ReferenceDeployment.*cmdResponse|BufferRepeater|downlinkRepeater' PROVESFlightControllerReference/ReferenceDeployment/Top 2>/dev/null || true

echo
echo "== inspect fprime extras git status/submodule hints =="
git ls-files -s lib/fprime-extras lib/fprime lib/fprime-zephyr || true
git -C lib/fprime-extras describe --tags --always --dirty 2>/dev/null || true
git -C lib/fprime describe --tags --always --dirty 2>/dev/null || true

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 4924


🏁 Script executed:

#!/bin/bash
set -euo pipefail

echo "== BufferRepeater generated/ac files in submodule working tree =="
find lib/fprime-extras -maxdepth 8 -type f \( -iname '*BufferRepeater*' -o -iname '*bufferrepeater*' \) -print | sed -n '1,200p'

echo
echo "== BufferRepeater definitions in submodule =="
find lib/fprime-extras -maxdepth 8 -type f \( -iname '*BufferRepeater*' -o -iname '*bufferrepeater*' \) -exec sh -c 'echo "--- $1"; rg -n -A12 -B12 "CHANNEL_ENABLED|Command|cmdResponse|cmdIn|OPCODE|paramSet" "$1" || true' sh {} \; | sed -n '1,260p'

echo
echo "== CommandDispatcher response handling in fprime submodule =="
find lib/fprime -maxdepth 10 -type f -path '*CommandDispatcher*' -o -path '*cmdDisp*' | sed -n '1,200p'
rg -n 'CmdDispatcher|compCmdStat|cmdResponseHandler|pending|ResponseReceived|isPending|cmdResponse' lib/fprime -S || true

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 364


Use the BufferRepeater opcode constant instead of hardcoding 0x0.

This depends on generated command ordering being unchanged, but the comment calls out the correct generated opcode name (OPCODE_CHANNEL_ENABLED_SET); import or generate that constant and use it here so a future interface change stops compiling instead of silently sending a different command.

🧰 Tools
🪛 Clang (14.0.6)

[warning] 154-154: variable 'channelEnablesArgs' is not initialized

(cppcoreguidelines-init-variables)


[warning] 156-156: variable 'OPCODE_CHANNEL_ENABLED_SET' is not initialized

(cppcoreguidelines-init-variables)

🤖 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
`@PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp`
around lines 146 - 159, Replace the locally hardcoded OPCODE_CHANNEL_ENABLED_SET
value in the downlinkRepeater command-dispatch block with the generated
BufferRepeater opcode constant. Import or reference the generated symbol
directly, preserving the existing invoke call while ensuring interface changes
produce a compile-time failure.

Comment on lines +150 to +157
// OPCODE_CHANNEL_ENABLED_SET (see generated BufferRepeaterComponentAc.hpp) -- the first/only
// settable param on this component, stable as long as BufferRepeater.fpp isn't changed.
Utilities::BufferRepeater_OutputChannelEnables uartOnlyChannelEnables(
{Fw::Enabled::ENABLED, Fw::Enabled::DISABLED, Fw::Enabled::DISABLED});
Fw::CmdArgBuffer channelEnablesArgs;
(void)channelEnablesArgs.serialize(uartOnlyChannelEnables);
constexpr FwOpcodeType OPCODE_CHANNEL_ENABLED_SET = 0x0;
downlinkRepeater.get_cmdIn_InputPort(0)->invoke(downlinkRepeater.getIdBase() + OPCODE_CHANNEL_ENABLED_SET, 0,

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Confirm the generated opcode constant name for BufferRepeater's CHANNEL_ENABLED_SET
fd -i 'BufferRepeaterComponentAc' lib/fprime-extras
rg -n -A3 -B3 'CHANNEL_ENABLED' lib/fprime-extras --iglob '*BufferRepeater*'

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 188


🏁 Script executed:

#!/bin/bash
set -u

printf 'Tracked files with BufferRepeater/topology names:\n'
git ls-files | rg -i 'BufferRepeater|ReferenceDeploymentTopology|BufferRepeaterComponentAc|Top' | head -200

printf '\nTarget file excerpt around referenced lines:\n'
if [ -f PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp ]; then
  sed -n '135,175p' PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp | cat -n
fi

printf '\nSearch all repo for opcode/channel enable references:\n'
rg -n -i 'OPCODE_CHANNEL_ENABLED_SET|CHANNEL_ENABLED_SET|channelEnabled|ChannelEnabled|ChannelEnables|BufferRepeater' . --glob '!*.png' --glob '!*.svg' | head -300

Repository: Open-Source-Space-Foundation/proves-core-reference

Length of output: 6590


🌐 Web query:

F Prime autogenerated component header OPCODE_ command opcode constant name examples

💡 Result:

In F Prime (F'), the opcode constant names generated in autocoded component headers typically follow the pattern OPCODE_<COMMAND_MNEMONIC> [1][2]. When a command is defined in an FPP (F Prime Prime) or XML component definition file, the autocoder generates a C++ header for the component that includes an enumeration of available command opcodes [1][2]. For example, if you define a command with the mnemonic NO_OP in your component, the autogenerated header will contain a constant such as OPCODE_NO_OP [1][2]. Key details regarding this naming convention: 1. Generation Pattern: The prefix OPCODE_ is prepended to the command mnemonic [1][2]. If the command mnemonic itself contains a prefix (e.g., CMD_NO_OP), the generated constant name will reflect that (e.g., OPCODE_CMD_NO_OP) [1][2]. 2. Purpose: These constants provide a type-safe, human-readable way to reference command opcodes within the C++ implementation of the component, rather than using raw integer values [1][2]. 3. Autocoding: These constants are part of the component base class generated by the F Prime autocoder from FPP or XML files [3][4]. 4. Usage: These opcodes are used by the component's command dispatcher and command handlers to identify which command has been received [5][6]. The corresponding command handler function in the derived C++ class typically follows a pattern like _cmdHandler [3][4]. Example from the Svc::CommandDispatcherComponentBase documentation: - Mnemonic: CMD_NO_OP -> Constant: OPCODE_CMD_NO_OP [1][2] - Mnemonic: CMD_NO_OP_STRING -> Constant: OPCODE_CMD_NO_OP_STRING [1][2] - Mnemonic: CMD_TEST_CMD_1 -> Constant: OPCODE_CMD_TEST_CMD_1 [1][2]

Citations:


Use the generated opcode constant instead of hardcoding 0x0.

Replace constexpr FwOpcodeType OPCODE_CHANNEL_ENABLED_SET = 0x0; with the autocoded constant generated for Utilities::BufferRepeater's CHANNEL_ENABLED_SET command, so this default remains valid if the component definition changes.

🧰 Tools
🪛 Clang (14.0.6)

[warning] 154-154: variable 'channelEnablesArgs' is not initialized

(cppcoreguidelines-init-variables)


[warning] 156-156: variable 'OPCODE_CHANNEL_ENABLED_SET' is not initialized

(cppcoreguidelines-init-variables)

🤖 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
`@PROVESFlightControllerReference/ReferenceDeployment/Top/ReferenceDeploymentTopology.cpp`
around lines 150 - 157, The channel-enable command invocation currently
hardcodes opcode 0x0; replace OPCODE_CHANNEL_ENABLED_SET with the generated
CHANNEL_ENABLED_SET opcode constant from Utilities::BufferRepeater, preserving
the existing command invocation and arguments.

Comment on lines +210 to +215
downlinker = fprime_test_api.pipeline.files.downlinker
fprime_test_api.clear_histories()
t0 = time.time()
fprime_test_api.send_command(f"{FILE_DOWNLINK}.SendFile", [board_path, dest_name])

candidate = Path(downlinker._FileDownlinker__directory) / dest_name

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.

📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

Tests rely on a private/mangled fprime_gds attribute, duplicated across the file.

downlinker._FileDownlinker__directory is a name-mangled private attribute of the fprime_gds FileDownlinker class, accessed directly in test_large_round_trip (line 215) and again in test_downlink_n_chunks (lines 352, 356, and four times in the diagnostic f-string at 358-363). This couples the suite to fprime_gds internals that can change without notice across library versions, and the repeated Path(...) / dest_name construction is duplicated logic.

Consider extracting a small helper, e.g. _downlink_path(downlinker, name) -> Path, and checking whether fprime_gds's IntegrationTestAPI/FileDownlinker exposes a public accessor for the download directory instead.

Also applies to: 343-363

🤖 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 `@PROVESFlightControllerReference/test/int/uart_file_transfer_test.py` around
lines 210 - 215, Replace direct accesses to
downlinker._FileDownlinker__directory in test_large_round_trip and
test_downlink_n_chunks with a small shared _downlink_path(downlinker, name)
helper. Use a public IntegrationTestAPI/FileDownlinker download-directory
accessor if available; otherwise centralize the existing path construction
inside the helper and update all diagnostic references to use it.

Mikefly123 and others added 2 commits July 23, 2026 22:13
The prepareForReboot persist (previous commit) covers commanded resets, but
the safe-mode reboot observed in CI is unplanned from firmware's view (load
switch shutoff -> rig-side watchdog/power reset with no software hook), so
the board resumes from the write-ahead mark and ground stays behind either
way. Handle it ground-side, mirroring sync_sequence_number_test:

- conftest: autouse fixture reads the board counter via bypass-listed
  GET_SEQ_NUM before each test and fast-forwards the framer plugin's
  sequence file when the board is ahead (ground-ahead is normal and left
  alone). Stops the post-reboot cascade that errored every later test file.
- common.proves_send_and_assert_command: resync on every failed retry, so a
  test that spans a reboot (mode_manager safe tests) heals mid-test instead
  of exhausting its retries against silent rejections.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
- rtc test_04: uplink_sequence_and_await_completion fired the file uplink
  immediately after a fire-and-forget CreateDirectory; when the START packet
  won the race the transfer died on FileOpenError and CS_RUN failed. Await
  directory creation (or its already-exists error completion) first.
- mode_manager safe_09: asserted EnteringSafeMode from an instantaneous
  history snapshot; the safe-mode-entry event burst is occasionally lost on
  the downlink entirely. Await the event with a real timeout and fall back
  to the GET_SAFE_MODE_REASON == COMMAND_LOSS command oracle.
- uart acceptance: tolerate high-water reaching the pool cap (a slow enough
  SD stall can transiently saturate any finite pool -- observed 25/25 on the
  CI rig vs 11/25 on the bench). Hard criteria stay: CRC-clean transfers,
  every buffer returned, board alive.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo

@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: 5

🤖 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 `@PROVESFlightControllerReference/test/int/common.py`:
- Line 136: Update the retry recovery flow in the common test helper around
resync_sequence_number to accept and use the selected deframer instead of always
using the helper’s UART default. Thread the active deframer from the caller
through recovery, preserving the existing UART selection for tests that do not
provide an alternate deframer and ensuring radio retries use
ComCcsdsLora.tcSecurityDeframer.
- Around line 135-138: Log the caught resync exception before continuing
recovery, while preserving the best-effort retry behavior. Update the exception
handlers around resync_sequence_number in
PROVESFlightControllerReference/test/int/common.py lines 135-138 and
PROVESFlightControllerReference/test/int/conftest.py lines 214-217 to emit the
exception through the existing test logging mechanism, then continue with
backoff or yielding without re-raising; run Ruff via make fmt.
- Line 76: Remove the redundant "r" mode argument from the open call in the
relevant test setup while preserving the existing UTF-8 encoding and
file-reading behavior; ensure the result satisfies Ruff UP015 when running make
fmt.

In `@PROVESFlightControllerReference/test/int/rtc_test.py`:
- Around line 112-122: Update the directory setup flow around
CreateDirectorySucceeded and DirectoryCreateError to require a received error
event when creation does not succeed. Validate that the error indicates the
directory already exists, and fail the test for timeout or any other filesystem
failure before proceeding to uplink.

In `@PROVESFlightControllerReference/test/int/uart_file_transfer_test.py`:
- Around line 306-315: Update the health-sampling flow around sample() so each
post-uplink request requires a newly received Health packet, rather than reusing
sample()’s retained latest telemetry. Track receipt after each request, retry
sampling when no fresh packet arrives, and fail the acceptance check if
freshness cannot be established before evaluating saturation.
🪄 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: ASSERTIVE

Plan: Pro Plus

Run ID: a8407621-7afc-489d-a220-75d0e8b27a4b

📥 Commits

Reviewing files that changed from the base of the PR and between b0bc46d and b7a8570.

📒 Files selected for processing (5)
  • PROVESFlightControllerReference/test/int/common.py
  • PROVESFlightControllerReference/test/int/conftest.py
  • PROVESFlightControllerReference/test/int/mode_manager_test.py
  • PROVESFlightControllerReference/test/int/rtc_test.py
  • PROVESFlightControllerReference/test/int/uart_file_transfer_test.py

board_seq = int(evt.args[0].val)
seq_file = "./Framing/src/sequence_number.bin"
try:
with open(seq_file, "r", encoding="utf-8") as f:

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Remove the redundant read mode.

Line 76 triggers Ruff UP015; text read mode is already the default.

Proposed fix
-        with open(seq_file, "r", encoding="utf-8") as f:
+        with open(seq_file, encoding="utf-8") as f:

As per coding guidelines, use Ruff for Python linting and formatting, as invoked by make fmt.

📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
with open(seq_file, "r", encoding="utf-8") as f:
with open(seq_file, encoding="utf-8") as f:
🧰 Tools
🪛 Ruff (0.15.21)

[warning] 76-76: Unnecessary mode argument

Remove mode argument

(UP015)

🤖 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 `@PROVESFlightControllerReference/test/int/common.py` at line 76, Remove the
redundant "r" mode argument from the open call in the relevant test setup while
preserving the existing UTF-8 encoding and file-reading behavior; ensure the
result satisfies Ruff UP015 when running make fmt.

Sources: Coding guidelines, Linters/SAST tools

Comment on lines +135 to +138
try:
resync_sequence_number(fprime_test_api)
except Exception: # noqa: BLE001 -- recovery must not mask the retry
pass

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.

📐 Maintainability & Code Quality | 🟡 Minor | ⚡ Quick win

Keep sequence-resync failures observable.

Best-effort recovery should not fail the test, but silently discarding every exception hides a persistent desynchronization or GDS failure and triggers Ruff S110.

  • PROVESFlightControllerReference/test/int/common.py#L135-L138: log the resync exception before continuing with retry backoff.
  • PROVESFlightControllerReference/test/int/conftest.py#L214-L217: log the resync exception before yielding to the test.

As per coding guidelines, use Ruff for Python linting and formatting, as invoked by make fmt.

🧰 Tools
🪛 Ruff (0.15.21)

[warning] 135-138: Use contextlib.suppress(Exception) instead of try-except-pass

(SIM105)


[error] 137-138: try-except-pass detected, consider logging the exception

(S110)

📍 Affects 2 files
  • PROVESFlightControllerReference/test/int/common.py#L135-L138 (this comment)
  • PROVESFlightControllerReference/test/int/conftest.py#L214-L217
🤖 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 `@PROVESFlightControllerReference/test/int/common.py` around lines 135 - 138,
Log the caught resync exception before continuing recovery, while preserving the
best-effort retry behavior. Update the exception handlers around
resync_sequence_number in PROVESFlightControllerReference/test/int/common.py
lines 135-138 and PROVESFlightControllerReference/test/int/conftest.py lines
214-217 to emit the exception through the existing test logging mechanism, then
continue with backoff or yielding without re-raising; run Ruff via make fmt.

Sources: Coding guidelines, Linters/SAST tools

# bypass-listed, so resyncing here works even in that state and
# costs one round-trip per failed attempt.
try:
resync_sequence_number(fprime_test_api)

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Select the active deframer during retry recovery.

Line 136 always uses the helper’s UART default. Radio runs explicitly select ComCcsdsLora.tcSecurityDeframer in conftest.py; after a reboot, syncing UART here leaves the LoRa counter desynchronized and the retried authenticated command still rejected. Thread the selected deframer into this recovery path.

🤖 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 `@PROVESFlightControllerReference/test/int/common.py` at line 136, Update the
retry recovery flow in the common test helper around resync_sequence_number to
accept and use the selected deframer instead of always using the helper’s UART
default. Thread the active deframer from the caller through recovery, preserving
the existing UART selection for tests that do not provide an alternate deframer
and ensuring radio retries use ComCcsdsLora.tcSecurityDeframer.

Comment on lines +112 to +122
if (
fprime_test_api.await_event(
f"{fileManager}.CreateDirectorySucceeded", timeout=5
)
is None
):
# Directory may already exist from an earlier test -- the error
# completion is fine, we only need the command to have finished.
fprime_test_api.await_event(
f"{fileManager}.DirectoryCreateError", timeout=2
)

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.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Reject directory-creation failures other than “already exists.”

If CreateDirectorySucceeded is absent, this awaits DirectoryCreateError but ignores both its presence and cause. A timeout or filesystem failure falls through to the uplink and reintroduces the race/failure this setup is meant to prevent. Assert an error was received and verify it is the idempotent existing-directory case.

🤖 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 `@PROVESFlightControllerReference/test/int/rtc_test.py` around lines 112 - 122,
Update the directory setup flow around CreateDirectorySucceeded and
DirectoryCreateError to require a received error event when creation does not
succeed. Validate that the error indicates the directory already exists, and
fail the test for timeout or any other filesystem failure before proceeding to
uplink.

Comment on lines +306 to +315
# High-water reaching the pool cap is tolerated: a slow enough SD can
# transiently saturate any finite pool, and the #471 guards make that a
# degraded mode (FrameDropped warnings, transfer retries) instead of a
# FATAL. The hard acceptance criteria are: transfers CRC-clean, every
# buffer returned (curr == 0), and the board alive for the next round.
if hi is not None and hi >= total:
print(
f"[471-acceptance] WARNING: high-water {hi} reached pool size "
f"{total} during uplink {i} (SD stall absorbed the whole pool)"
)

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.

🎯 Functional Correctness | 🟠 Major | ⚡ Quick win

Require a fresh buffer-health sample before tolerating saturation.

sample() retains latest, so after its first success it stops retrying even if the post-uplink SEND_PKT was lost. This branch can then accept pool saturation using stale CurrBuffs == 0 / HiBuffs data, without proving buffers recovered. Track telemetry received after each request and retry or fail when no fresh Health packet is observed.

🤖 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 `@PROVESFlightControllerReference/test/int/uart_file_transfer_test.py` around
lines 306 - 315, Update the health-sampling flow around sample() so each
post-uplink request requires a newly received Health packet, rather than reusing
sample()’s retained latest telemetry. Track receipt after each request, retry
sampling when no fresh packet arrives, and fail the acceptance check if
freshness cannot be established before evaluating saturation.

Mikefly123 and others added 3 commits July 23, 2026 23:48
…evel reboot quirks

- SEND_PKT gained a second 'section' argument in F Prime v4.2.2; sending
  only the packet id is a board-side FORMAT_ERROR, which silently broke the
  acceptance test's on-demand buffer telemetry sampling on CI (rounds 5-6).
  Pass ("2", "REALTIME") and degrade pool checks to a warning when
  telemetry still isn't observable -- the acceptance gate is three CRC-clean
  204KB uplinks on one boot, pool telemetry is diagnostics.
- mode_manager safe_09: the hardware watchdog can fire a second time before
  FSW re-arms petting after the first reset (observed boot count +2 on the
  CI rig); require at least one reboot instead of exactly one.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
…atch

The downlink direction has the same no-ARQ residual frame corruption as
the uplink (observed once in ~7 CI rounds: size-correct file, bytes
differ). The on-board source is already CRC-verified, so one re-downlink
discriminates link noise from real corruption, mirroring the uplink retry.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
Round-8 CI showed the board rebooting around the acceptance test (fresh
PrmDb, APID counters reset) and both uplink attempts losing their START
packet to the resulting auth desync. Realign the sequence number and let
the board settle between attempts, and allow a third attempt.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WpBURCutAx8281i59nj6fo
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

Status: No status

1 participant