Skip to content

.pr_agent_auto_best_practices

qodo-merge-bot edited this page Apr 1, 2026 · 5 revisions

Pattern 1: Validate external inputs (env vars, CLI args, and incoming protocol payload sizes) before use, and fail deterministically with a clear error when inputs are missing or malformed.

Example code before:

// env var / CLI arg used without validation
int pr = parseInt(getenv("PR_NUMBER"));      // could be NaN/NULL
const char *path = argv[1];                 // argc not checked

// protocol handler reads without checking length
uint8_t item = buf[0];
uint16_t pos = read_u16(buf + 1);

Example code after:

// validate env var / CLI args
const char *s = getenv("PR_NUMBER");
if (!s) { fprintf(stderr, "PR_NUMBER missing\n"); return 1; }
char *end = NULL;
long pr = strtol(s, &end, 10);
if (*s == '\0' || *end != '\0') { fprintf(stderr, "PR_NUMBER invalid\n"); return 1; }

if (argc < 2) { fprintf(stderr, "usage: tool <path>\n"); return 2; }

// validate payload size before reading
if (dataSize < 3) return MSP_RESULT_ERROR;
uint8_t item = buf[0];
uint16_t pos = read_u16(buf + 1);
Relevant past accepted suggestions:
Suggestion 1: [reliability] `parseInt` result not validated
`parseInt` result not validated The PR comment step uses `parseInt(process.env.PR_NUMBER)` without checking for `NaN` or enforcing base-10 parsing, which can lead to non-deterministic behavior if the env var is missing/malformed. This violates the requirement to validate external inputs and handle invalid values deterministically.

Issue description

The workflow parses PR_NUMBER from an environment variable using parseInt(...) and then uses it without checking for NaN (and without specifying radix 10). If the env var is missing or malformed, this can produce non-deterministic behavior (e.g., NaN in URLs / API params) instead of a clear, deterministic failure.

Issue Context

This job runs with elevated permissions (pull-requests: write) and should validate external inputs (including env vars derived from artifacts/outputs) before use.

Fix Focus Areas

  • .github/workflows/pr-test-builds.yml[107-110] բավ

Suggestion 2:

Add payload size validation check

Add a payload size check in the MSP_OSD_CUSTOM_POSITION handler to ensure the incoming data is at least 3 bytes before reading from the buffer.

src/main/fc/fc_msp.c [2718-2731]

 case MSP_OSD_CUSTOM_POSITION: {
+    if (dataSize < 3) {
+        return MSP_RESULT_ERROR;
+    }
     uint8_t item;
     sbufReadU8Safe(&item, src);
     if (item < OSD_ITEM_COUNT){ // item == addr
         osdEraseCustomItem(item);
         osdLayoutsConfigMutable()->item_pos[0][item] = sbufReadU16(src) | (1 << 13);
         osdDrawCustomItem(item);
     }
     else{
         return MSP_RESULT_ERROR;
     }
 
     break;
 }

Suggestion 3:
  • [learned best practice] Validate `"$#"` before reading `$1` and print a short usage message on error so failures are deterministic and user-friendly.
    Suggestion 4:
  • [general] Add a check to the firmware renaming script to ensure `.hex` files exist before attempting to loop through and rename them, preventing potential errors.

  • Pattern 2: Avoid build fragility by explicitly including required headers/macros and by removing/ignoring generated build outputs that contain machine-specific paths or state.

    Example code before:

    // header relies on transitive includes for macros/types
    #pragma once
    #define MY_ID CONCAT4(A, B, C, D)   // CONCAT4 not defined here
    
    // generated build files accidentally committed
    set(CMAKE_ASM_TARGET_INCLUDE_PATH "/Users/alice/project/include")
    

    Example code after:

    // header includes what it uses
    #pragma once
    #include "common/utils.h"           // defines CONCAT4
    #define MY_ID CONCAT4(A, B, C, D)
    
    // generated build outputs removed and ignored
    # .gitignore
    CMakeFiles/
    CMakeCache.txt
    
    Relevant past accepted suggestions:
    Suggestion 1: [reliability] Headers rely on transitive CONCAT4
    Headers rely on transitive CONCAT4 The new bus_spi_stm32{h7,f7}xx.h headers use CONCAT4 but do not include the header that defines it, relying on current include order/transitive includes from other headers. This is a latent build fragility: a future refactor or reuse of these headers elsewhere can cause compile failures (e.g., CONCAT4 undefined).

    Issue description

    The new SPI AF lookup table headers use CONCAT4(...) but do not include the header that defines it (common/utils.h). They currently compile only due to transitive includes (via drivers/io.hio_def.hcommon/utils.h) and therefore are fragile to include-order changes or reuse.

    Issue Context

    This is a latent build fragility / maintainability issue: it may not break today, but it can break later during refactors or if another file includes these headers without including common/utils.h first.

    Fix Focus Areas

    • src/main/drivers/bus_spi_stm32h7xx.h[32-38]
    • src/main/drivers/bus_spi_stm32f7xx.h[36-42]

    Suggestion 2:

    Remove generated build file from repository

    Remove the generated CMake build file from the repository. It contains user-specific absolute paths that will cause build failures for other developers and should be added to .gitignore.

    src/src/main/target/AXISFLYINGF7PRO/CMakeFiles/AXISFLYINGF7PRO_for_bl.elf.dir/DependInfo.cmake [1-474]

    -# Consider dependencies only in project.
    -set(CMAKE_DEPENDS_IN_PROJECT_ONLY OFF)
    +# This file should be removed from the repository.
     
    -# The set of languages for which implicit dependencies are needed:
    -set(CMAKE_DEPENDS_LANGUAGES
    -  "ASM"
    -  )
    -# The set of files for implicit dependencies of each language:
    -set(CMAKE_DEPENDS_CHECK_ASM
    -  "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/lib/main/CMSIS/DSP/Source/TransformFunctions/arm_bitreversal2.S" "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/src/src/main/target/AXISFLYINGF7PRO/CMakeFiles/AXISFLYINGF7PRO_for_bl.elf.dir/__/__/__/__/lib/main/CMSIS/DSP/Source/TransformFunctions/arm_bitreversal2.S.obj"
    -  "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/src/main/startup/startup_stm32f722xx.s" "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/src/src/main/target/AXISFLYINGF7PRO/CMakeFiles/AXISFLYINGF7PRO_for_bl.elf.dir/__/__/startup/startup_stm32f722xx.s.obj"
    -  )
    -set(CMAKE_ASM_COMPILER_ID "GNU")
    -
    -# Preprocessor definitions for this target.
    -set(CMAKE_TARGET_DEFINITIONS_ASM
    -...
    -
    -# The include file search paths:
    -set(CMAKE_ASM_TARGET_INCLUDE_PATH
    -  "main/target/AXISFLYINGF7PRO"
    -  "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/lib/main/STM32F7/Drivers/STM32F7xx_HAL_Driver/Inc"
    -  "/Users/ahmed/Desktop/Projects/INAV-RPiOSD/inav/lib/main/STM32F7/Drivers/CMSIS/Device/ST/STM32F7xx/Include"
    -...
    -

    Suggestion 3:

    Remove generated file from version control

    Remove the generated Makefile.cmake file from version control. This file is environment-specific and should be ignored by adding the CMakeFiles directory to .gitignore.

    src/CMakeFiles/Makefile.cmake [1-9]

    -# CMAKE generated file: DO NOT EDIT!
    -# Generated by "Unix Makefiles" Generator, CMake Version 4.1
    +# This file should be removed from the pull request and repository.
     
    -# The generator used is:
    -set(CMAKE_DEPENDS_GENERATOR "Unix Makefiles")
    -
    -# The top level Makefile was generated from the following files:
    -set(CMAKE_MAKEFILE_DEPENDS
    -  "CMakeCache.txt"
    -...
    -

    Pattern 3: Add explicit timeout handling for polling/wait loops and DMA/IO state transitions; if a timeout is hit, return a specific error instead of breaking/continuing with partial state.

    Example code before:

    while (!(REG & READY_BIT)) {
        if (--timeout == 0) break;  // continues afterward with incomplete state
    }
    disable_dma_stream();
    // assume DMA is disabled; proceed to reconfigure
    configure_dma_stream();
    

    Example code after:

    while (!(REG & READY_BIT)) {
        if (--timeout == 0) return ERR_TIMEOUT;
    }
    
    uint32_t t = DMA_DISABLE_TIMEOUT;
    disable_dma_stream();
    while (dma_stream_enabled() && --t) { /* wait */ }
    if (t == 0) return ERR_DMA_STILL_ACTIVE;
    
    configure_dma_stream();
    
    Relevant past accepted suggestions:
    Suggestion 1:
  • [possible issue] In `SD_StartBlockTransfert`, check if the DMA disable loop timed out. If it did, set an error and return to avoid reconfiguring an active DMA stream, which could cause unpredictable behavior.
    Suggestion 2:
  • [possible issue] In `SD_HighSpeed`, if the `swTimeout` is reached while waiting for SDIO status, return an error like `SD_TIMEOUT` instead of just breaking the loop to prevent processing incomplete data. [possible issue, importance: 7]
    New proposed code:
    Suggestion 3:
  • [general] Add a timeout or retry counter to the polling loops in `STORAGE_Read` to prevent the system from hanging if the SD card becomes unresponsive.

  • Pattern 4: Guard numeric edge cases in calculations (e.g., division by zero and vector normalization of near-zero magnitude), and use consistent sentinel values for invalid/no-data states.

    Example code before:

    float tpa = (airspeed - ref) / ref;          // ref could be 0
    normalize(v);                                // v could be ~0 length
    
    // failed read only partially resets data
    mag.x = 0; mag.z = 0;                        // mag.y left stale
    
    measurement_cm = 0;                          // looks like valid "0cm"
    

    Example code after:

    float tpa = 0.0f;
    if (ref > 0.0f) tpa = (airspeed - ref) / ref;
    
    if (norm2(v) > 0.01f) normalize(v);
    
    // reset all axes consistently on failure
    mag.x = mag.y = mag.z = 0;
    
    // sentinel for "no new data"
    measurement_cm = RANGEFINDER_NO_NEW_DATA;
    
    Relevant past accepted suggestions:
    Suggestion 1:

    Prevent division by zero during normalization

    Add a check to ensure vCoGlocal has a non-zero magnitude before normalization to prevent a potential division-by-zero error.

    src/main/flight/imu.c [472-482]

    -if (vectorNormSquared(&vHeadingEF) > 0.01f) {
    +if (vectorNormSquared(&vHeadingEF) > 0.01f && vectorNormSquared(&vCoGlocal) > 0.01f) {
         // Normalize to unit vector
         vectorNormalize(&vHeadingEF, &vHeadingEF);
         vectorNormalize(&vCoGlocal, &vCoGlocal);
     
         // error is cross product between reference heading and estimated heading (calculated in EF)
         vectorCrossProduct(&vCoGErr, &vCoGlocal, &vHeadingEF);
     
         // Rotate error back into body frame
         quaternionRotateVector(&vCoGErr, &vCoGErr, &orientation);
     }

    Suggestion 2:

    Prevent division-by-zero in TPA calculation

    Add a check to prevent division-by-zero when referenceAirspeed is zero in the tpaThrottle calculation, falling back to the standard throttle value if necessary.

    src/main/flight/pid.c [501-502]

     const float referenceAirspeed = pidProfile()->fixedWingReferenceAirspeed; // in cm/s
    -tpaThrottle = currentControlRateProfile->throttle.pa_breakpoint + (uint16_t)((airspeed - referenceAirspeed) / referenceAirspeed * (currentControlRateProfile->throttle.pa_breakpoint - getThrottleIdleValue()));
    +if (referenceAirspeed > 0) {
    +    tpaThrottle = currentControlRateProfile->throttle.pa_breakpoint + (uint16_t)((airspeed - referenceAirspeed) / referenceAirspeed * (currentControlRateProfile->throttle.pa_breakpoint - getThrottleIdleValue()));
    +} else {
    +    // Fallback to regular throttle if reference airspeed is not configured
    +    tpaThrottle = rcCommand[THROTTLE];
    +}

    Suggestion 3:
  • [learned best practice] Initialize the measurement field to a fail-safe sentinel (e.g., `RANGEFINDER_NO_NEW_DATA`) so startup/first-read states don't appear as a valid `0cm` reading.
    Suggestion 4:

    Reset all magnetometer axes

    The Y-axis is not being reset to zero in case of failed read, which could leave stale data. All three axes should be reset to maintain consistency and prevent potential issues with outdated magnetometer readings.

    src/main/drivers/compass/compass_qmc5883p.c [131-133]

     // set magData to zero for case of failed read
     mag->magADCRaw[X] = 0;
    +mag->magADCRaw[Y] = 0;
     mag->magADCRaw[Z] = 0;

  • Pattern 5: Keep behavior and semantics consistent across modules and documentation by aligning shared logic, operand conventions, and API signatures (and updating docs/examples when code behavior changes).

    Example code before:

    // module A priority
    if (FAILSAFE) mode="FS";
    else if (MANUAL) mode="MANU";
    else if (GEOZONE) mode="GEO";
    
    // module B priority differs
    if (FAILSAFE) mode="FS";
    else if (GEOZONE) mode="GEO";
    else if (MANUAL) mode="MANU";
    
    // docs/example call doesn't match function signature
    createLogicCondition(id, op1, op2, rawValue, flags);
    

    Example code after:

    // shared/consistent priority order (or centralized helper)
    const char *mode = getFlightModeLabel();  // same logic used by both modules
    
    // docs/examples match actual API contract
    createLogicCondition(id,
                         (operand_t){ .type=OP_FLIGHT, .field=FIELD_WIND_DIR },
                         (operand_t){ .type=OP_CONST,  .value=123 },
                         flags);
    
    Relevant past accepted suggestions:
    Suggestion 1:

    Harmonize operand semantics and docs

    Mirror the sign and invalid-return conventions with the new relative wind field in docs and keep operand meanings consistent; update docs and ensure both operands follow the same invalid sentinel and frame definitions.

    src/main/programming/logic_condition.c [763-785]

    -case LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION: // deg
    +case LOGIC_CONDITION_OPERAND_FLIGHT_WIND_DIRECTION: // deg, 0..359, -1 invalid
     #ifdef USE_WIND_ESTIMATOR
         {
             if (isEstimatedWindSpeedValid()) {
    -            uint16_t windAngle;
    -            getEstimatedHorizontalWindSpeed(&windAngle);
    -            int32_t windHeading = windAngle + 18000; // Correct heading to display correctly.
    -    
    -            windHeading = (CENTIDEGREES_TO_DEGREES((int)windHeading));
    -            while (windHeading < 0) {
    -                windHeading += 360;
    -            }
    -            while (windHeading >= 360) {
    -                windHeading -= 360;
    -            }
    -            return windHeading;
    -        } else
    +            uint16_t windAngleCd;
    +            getEstimatedHorizontalWindSpeed(&windAngleCd);
    +            // Convert wind vector angle (towards) to heading-from (meteorological) in degrees [0,360)
    +            int32_t headingDeg = CENTIDEGREES_TO_DEGREES((int)(windAngleCd + 18000));
    +            while (headingDeg < 0) headingDeg += 360;
    +            while (headingDeg >= 360) headingDeg -= 360;
    +            return headingDeg;
    +        } else {
                 return -1;
    +        }
         }
     #else
    -        return -1;
    +    return -1;
     #endif
    -        break;
    +    break;

    Suggestion 2:

    Align flight mode priority logic

    Align the flight mode priority logic in crsf.c with osd.c. The current implementation has a different priority for MANUAL_MODE and GEOZONE between the two files, which should be synchronized.

    src/main/telemetry/crsf.c [356-368]

     #ifdef USE_FW_AUTOLAND
             if (FLIGHT_MODE(NAV_FW_AUTOLAND)) {
                 flightMode = "LAND";
             } else
     #endif
             if (FLIGHT_MODE(FAILSAFE_MODE)) {
                 flightMode = "!FS!";
    +        } else if (FLIGHT_MODE(MANUAL_MODE)) {
    +            flightMode = "MANU";
     #ifdef USE_GEOZONE
             } else if (FLIGHT_MODE(NAV_SEND_TO) && !FLIGHT_MODE(NAV_WP_MODE)) {
                 flightMode = "GEO";
    -#endif            
    -        } else if (FLIGHT_MODE(MANUAL_MODE)) {
    -            flightMode = "MANU";
    +#endif

    Suggestion 3:
  • [learned best practice] Make the replacement guidance exact per command (get vs set) to avoid ambiguity and divergence with docs/clients; reference only the corresponding replacement for each define.
    Suggestion 4:
  • [possible issue] Correct the `createLogicCondition` call in the `codegen.js` example. The function expects four arguments with operand objects, not five arguments with a mix of raw values and objects.

  • [Auto-generated best practices - 2026-04-01]

  • WIKI TOPICS

    Wiki Home Page

    INAV Version Release Notes

    9.0.0 Release Notes
    8.0.0 Release Notes
    7.1.0 Release Notes
    7.0.0 Release Notes
    6.0.0 Release Notes
    5.1 Release notes
    5.0.0 Release Notes
    4.1.0 Release Notes
    4.0.0 Release Notes
    3.0.0 Release Notes
    2.6.0 Release Notes
    2.5.1 Release notes
    2.5.0 Release Notes
    2.4.0 Release Notes
    2.3.0 Release Notes
    2.2.1 Release Notes
    2.2.0 Release Notes
    2.1.0 Release Notes
    2.0.0 Release Notes
    1.9.1 Release notes
    1.9.0 Release notes
    1.8.0 Release notes
    1.7.3 Release notes
    Older Release Notes

    QUICK START GUIDES

    Getting started with iNav
    Fixed Wing Guide
    Howto: CC3D flight controller, minimOSD , telemetry and GPS for fixed wing
    Howto: CC3D flight controller, minimOSD, GPS and LTM telemetry for fixed wing
    INAV for BetaFlight users
    launch mode
    Multirotor guide
    YouTube video guides
    DevDocs Getting Started.md
    DevDocs INAV_Fixed_Wing_Setup_Guide.pdf
    DevDocs Safety.md

    Connecting to INAV

    Bluetooth setup to configure your flight controller
    DevDocs Wireless Connections (BLE, TCP and UDP).md\

    Flashing and Upgrading

    Boards, Targets and PWM allocations
    Upgrading from an older version of INAV to the current version
    DevDocs Installation.md
    DevDocs USB Flashing.md

    Setup Tab
    Live 3D Graphic & Pre-Arming Checks

    Calibration Tab
    Accelerometer, Compass, & Optic Flow Calibration

    Alignment Tool Tab
    Adjust mount angle of FC & Compass

    Ports Tab
    Map Devices to UART Serial Ports

    Receiver Tab
    Set protocol and channel mapping

    Mixer Tab
    Set aircraft type and how its controlled

    Outputs Tab
    Set ESC Protocol and Servo Parameters

    Modes Tab
    Assign flight modes to transmitter switches
    Standard Modes
    Navigation Modes
    Return to Home
    Fixed Wing Autolaunch
    Auto Launch

    Configuration Tab
    No wiki page currently

    Failsafe Tab
    Set expected behavior of aircraft upon failsafe

    PID Tuning

    Navigation PID tuning (FW)
    Navigation PID tuning (MC)
    EZ-Tune
    PID Attenuation and scaling
    Tune INAV PID-FF controller for fixedwing
    DevDocs Autotune - fixedwing.md
    DevDocs INAV PID Controller.md
    DevDocs INAV_Wing_Tuning_Masterclass.pdf
    DevDocs PID tuning.md
    DevDocs Profiles.md

    GPS

    GPS and Compass setup
    GPS Failsafe and Glitch Protection

    Rangefinder & Optic Flow

    Optic Flow and Rangefinder Setup
    Setup and usage for terrain following & GPS-free position hold

    OSD and VTx

    DevDocs Betaflight 4.3 compatible OSD.md
    OSD custom messages
    OSD Hud and ESP32 radars
    DevDocs OSD.md
    DevDocs VTx.md

    LED Strip

    DevDocs LedStrip.md

    ADVANCED

    Programming

    DevDocs Programming Framework.md

    Adjustments

    DevDocs Inflight Adjustments.md

    Mission Control

    iNavFlight Missions
    DevDocs Safehomes.md

    MultiWii Serial Protocol

    MSP V2
    MSP Messages reference guide
    MSP Navigation Messages
    INAV MSP frames changelog

    Telemetry

    INAV Remote Management, Control and Telemetry
    MAVlink Control and Telemetry
    Lightweight Telemetry (LTM)

    Tethered Logging

    Log when FC is connected via USB

    Blackbox

    DevDocs Blackbox.md
    INAV blackbox variables
    DevDocs USB_Mass_Storage_(MSC)_mode.md

    CLI

    iNav CLI variables
    DevDocs Cli.md
    DevDocs Settings.md

    VTOL

    DevDocs MixerProfile.md
    DevDocs VTOL.md

    TROUBLESHOOTING

    "Something" is disabled Reasons
    Blinkenlights
    Sensor auto detect and hardware failure detection Pixel OSD FAQs
    TROUBLESHOOTING
    Why do I have limited servo throw in my airplane

    ADTL TOPICS, FEATURES, DEV INFO

    AAT Automatic Antenna Tracker
    Building custom firmware
    Default values for different type of aircrafts
    Source Enums
    Features safe to add and remove to fit your needs.
    Developer info
    Making a new Virtualbox to make your own INAV[OrangeRX LRS RX and OMNIBUS F4](OrangeRX-LRS-RX-and-OMNIBUS-F4)
    Rate Dynamics
    Target and Sensor support
    Ublox 3.01 firmware and Galileo
    DevDocs Controls
    DevDocs 1wire.md
    DevDocs ADSB.md
    DevDocs Battery.md
    DevDocs Buzzer.md
    DevDocs Channel forwarding.md
    DevDocs Display.md
    DevDocs Fixed Wing Landing.md
    DevDocs GPS_fix_estimation.md
    DevDocs LED pin PWM.md
    DevDocs Lights.md
    DevDocs OSD Joystick.md
    DevDocs Servo Gimbal.md
    DevDocs Temperature sensors.md

    OLD LEGACY INFO

    Supported boards
    DevDocs Boards.md
    Legacy Mixers
    Legacy target ChebuzzF3
    Legacy target Colibri RACE
    Legacy target Motolab
    Legacy target Omnibus F3
    Legacy target Paris Air Hero 32
    Legacy target Paris Air Hero 32 F3
    Legacy target Sparky
    Legacy target SPRacingF3
    Legacy target SPRacingF3EVO
    Legacy target SPRacingF3EVO_1SS
    DevDocs Configuration.md
    Request form new PRESET
    DevDocs Introduction.md
    Welcome to INAV, useful links and products
    UAV Interconnect Bus
    DevDocs Rangefinder.md
    DevDocs Rssi.md
    DevDocs Runcam device.md
    DevDocs Serial.md
    DevDocs Telemetry.md
    DevDocs Rx.md
    DevDocs Spektrum bind.md
    DevDocs INAV_Autolaunch.pdf

    Clone this wiki locally