Skip to content

fix: swap stat -f %m to Linux-first order for cross-platform compat#1492

Merged
marcusquinn merged 1 commit intomainfrom
bugfix/stat-linux-compat
Feb 15, 2026
Merged

fix: swap stat -f %m to Linux-first order for cross-platform compat#1492
marcusquinn merged 1 commit intomainfrom
bugfix/stat-linux-compat

Conversation

@marcusquinn
Copy link
Owner

@marcusquinn marcusquinn commented Feb 15, 2026

Summary

Fixes #1491stat -f %m (macOS-first) breaks on Linux, causing stdout pollution and "unbound variable" errors.

Root Cause

On Linux, stat -f %m interprets -f as "filesystem" mode and dumps multi-line filesystem metadata to stdout (not stderr). Since 2>/dev/null only suppresses stderr, the garbage is captured by $(), corrupting the variable. Arithmetic evaluation then fails on the garbage text.

Fix

Swap all 10 macOS-first || chains to Linux-first:

# Before (broken on Linux):
stat -f %m "$file" 2>/dev/null || stat -c %Y "$file" 2>/dev/null || echo "0"

# After (works on both):
stat -c %Y "$file" 2>/dev/null || stat -f %m "$file" 2>/dev/null || echo "0"

stat -c on macOS fails cleanly (no stdout, exit 1) so the fallback to stat -f works correctly.

Also simplified subagent-index-helper.sh from a 5-line if/else to the standard || chain.

Files Changed (10)

All || chain patterns swapped to Linux-first. The 4 files using uname guards were already safe and left unchanged.

Verification

Tested on Linux via OrbStack (Ubuntu 22.04):

  • stat -c %Y returns clean numeric timestamp
  • Arithmetic age calculation: PASS
  • All 10 fixed scripts confirmed Linux-first pattern

Summary by CodeRabbit

  • Chores
    • Standardized file timestamp retrieval methods across internal helper and supervisor scripts to improve cross-platform compatibility and consistency for cache freshness validation, system configuration checks, memory management operations, upstream data synchronization, event timing calculations, and various automated maintenance and coordination infrastructure tasks throughout the system.

…1491)

On Linux, stat -f %m interprets -f as 'filesystem' mode and dumps
multi-line filesystem metadata to stdout before exiting non-zero.
Since 2>/dev/null only suppresses stderr, the stdout pollution is
captured by $(), corrupting the variable and causing arithmetic
evaluation failures ('unbound variable' errors).

Fix: swap all 10 macOS-first || chains to Linux-first:
  stat -c %Y "$file" 2>/dev/null || stat -f %m "$file" 2>/dev/null

stat -c on macOS fails cleanly (no stdout, exit 1) so the fallback
to stat -f works correctly. Also simplified subagent-index-helper.sh
from a 5-line if/else to the standard || chain pattern.

Verified on Linux via OrbStack (Ubuntu 22.04):
- stat -c %Y returns clean numeric timestamp
- Arithmetic age calculation passes
- All 10 fixed scripts confirmed Linux-first

Closes #1491
@gemini-code-assist
Copy link

Summary of Changes

Hello @marcusquinn, I'm Gemini Code Assist1! I'm currently reviewing this pull request and will post my feedback shortly. In the meantime, here's a summary to help you and other reviewers quickly get up to speed!

This pull request significantly enhances the cross-platform compatibility of several shell scripts by rectifying an issue with the stat command's behavior across different operating systems. By reordering the stat command attempts to prioritize the Linux-compatible syntax, the changes ensure that scripts execute reliably on both Linux and macOS, preventing errors caused by platform-specific command output and improving overall script robustness.

Highlights

Changelog
  • .agents/scripts/higgsfield-helper.sh
    • Reordered stat command arguments to ensure cross-platform compatibility for retrieving file modification times.
  • .agents/scripts/humanise-update-helper.sh
    • Reordered stat command arguments to ensure cross-platform compatibility for retrieving file modification times.
  • .agents/scripts/mcp-index-helper.sh
    • Reordered stat command arguments to ensure cross-platform compatibility for retrieving file modification times.
  • .agents/scripts/memory-audit-pulse.sh
    • Reordered stat command arguments in two locations to ensure cross-platform compatibility for retrieving file modification times.
  • .agents/scripts/memory/_common.sh
    • Reordered stat command arguments to ensure cross-platform compatibility for retrieving file modification times.
  • .agents/scripts/migrate-pr-backfill.sh
    • Reordered stat command arguments to ensure cross-platform compatibility for retrieving file modification times.
  • .agents/scripts/ralph-upstream-check.sh
    • Reordered stat command arguments to ensure cross-platform compatibility for retrieving file modification times.
  • .agents/scripts/schema-validator-helper.sh
    • Reordered stat command arguments to ensure cross-platform compatibility for retrieving file modification times.
  • .agents/scripts/subagent-index-helper.sh
    • Refactored stat command logic from an if/else block to a concise || chain for improved cross-platform compatibility and readability.
  • .agents/scripts/supervisor/pulse.sh
    • Reordered stat command arguments to ensure cross-platform compatibility for retrieving file modification times.
Activity
  • No specific activity was recorded for this pull request.
Using Gemini Code Assist

The full guide for Gemini Code Assist can be found on our documentation page, here are some quick tips.

Invoking Gemini

You can request assistance from Gemini at any point by creating a comment using either /gemini <command> or @gemini-code-assist <command>. Below is a summary of the supported commands on the current page.

Feature Command Description
Code Review /gemini review Performs a code review for the current pull request in its current state.
Pull Request Summary /gemini summary Provides a summary of the current pull request in its current state.
Comment @gemini-code-assist Responds in comments when explicitly tagged, both in pull request comments and review comments.
Help /gemini help Displays a list of available commands.

Customization

To customize Gemini Code Assist for GitHub experience, repository maintainers can create a configuration file and/or provide a custom code review style guide (such as PEP-8 for Python) by creating and adding files to a .gemini/ folder in the base of the repository. Detailed instructions can be found here.

Limitations & Feedback

Gemini Code Assist may make mistakes. Please leave feedback on any instances where its feedback is incorrect or counter productive. You can react with 👍 and 👎 on @gemini-code-assist comments. If you're interested in giving your feedback about your experience with Gemini Code Assist for Github and other Google products, sign up here.

You can also get AI-powered code generation, chat, as well as code reviews directly in the IDE at no cost with the Gemini Code Assist IDE Extension.

Footnotes

  1. Review the Privacy Notices, Generative AI Prohibited Use Policy, Terms of Service, and learn how to configure Gemini Code Assist in GitHub here. Gemini can make mistakes, so double check it and use code with caution.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 15, 2026

Walkthrough

This pull request fixes a cross-platform compatibility bug in shell scripts where stat -f %m (macOS syntax) attempted first on Linux caused stdout pollution from failed commands, leading to unbound variable errors. The fix swaps the stat command order to prefer stat -c %Y (Linux syntax) first, with stat -f %m as fallback across all scripts.

Changes

Cohort / File(s) Summary
Core Agent Scripts
.agents/scripts/higgsfield-helper.sh, .agents/scripts/humanise-update-helper.sh, .agents/scripts/mcp-index-helper.sh, .agents/scripts/memory-audit-pulse.sh, .agents/scripts/migrate-pr-backfill.sh, .agents/scripts/ralph-upstream-check.sh, .agents/scripts/schema-validator-helper.sh
Swapped stat command precedence from stat -f %m || stat -c %Y to stat -c %Y || stat -f %m in file timestamp retrieval logic (e.g., in status checks, cache age calculations, freshness validation).
Memory and Index Management
.agents/scripts/memory/_common.sh, .agents/scripts/subagent-index-helper.sh
Updated timestamp extraction: memory/_common.sh swapped stat order in auto_prune; subagent-index-helper.sh refactored into unified cross-platform expression with zero fallback when both stat variants fail.
Supervisor Escalation
.agents/scripts/supervisor/pulse.sh
Reordered stat invocations in lock age calculation (Phase 3.6) to prioritize Linux syntax, changing escalation lock freshness evaluation logic.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~10 minutes

Possibly related PRs

Poem

📊 Shell scripts now play fair on Linux and macOS,
stat commands reordered, no more garbage output flows,
Unbound variables vanquished with a preference swap,
Cross-platform harmony—no more fallback flops! 🎯

🚥 Pre-merge checks | ✅ 6
✅ Passed checks (6 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title accurately summarizes the main change: swapping stat command order to prioritize Linux-first for cross-platform compatibility.
Linked Issues check ✅ Passed All code changes directly address issue #1491 requirements: 10 files updated with Linux-first stat order, restoring correct cross-platform behavior and preventing stdout pollution.
Out of Scope Changes check ✅ Passed All changes are in-scope: swapping stat command order in fallback chains per issue #1491 objectives, with intentional exclusion of uname-guarded files per PR objectives.
Docstring Coverage ✅ Passed Docstring coverage is 81.82% which is sufficient. The required threshold is 80.00%.
Merge Conflict Detection ✅ Passed ✅ No merge conflicts detected when merging into main

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/stat-linux-compat

No actionable comments were generated in the recent review. 🎉


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 and usage tips.

@github-actions
Copy link

🔍 Code Quality Report

�[0;35m[MONITOR]�[0m Code Review Monitoring Report

�[0;34m[INFO]�[0m Latest Quality Status:
SonarCloud: 0 bugs, 0 vulnerabilities, 16 code smells

�[0;34m[INFO]�[0m Recent monitoring activity:
Sun Feb 15 15:36:48 UTC 2026: Code review monitoring started
Sun Feb 15 15:36:48 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 16

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 16
  • VULNERABILITIES: 0

Generated on: Sun Feb 15 15:36:51 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

@marcusquinn marcusquinn merged commit 32fa87d into main Feb 15, 2026
11 checks passed
Copy link

@gemini-code-assist gemini-code-assist bot left a comment

Choose a reason for hiding this comment

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

Code Review

This pull request effectively addresses a cross-platform compatibility issue with the stat command by prioritizing the Linux-compatible version (-c %Y) over the macOS-specific one (-f %m). The changes are applied consistently across 10 files, which will prevent stdout pollution and "unbound variable" errors on Linux systems. The simplification in subagent-index-helper.sh, replacing a verbose if/else block with the concise || chain, is a great improvement for readability and consistency. I have one minor suggestion to improve stylistic consistency regarding arithmetic expansion.

local lock_pid lock_age
lock_pid=$(head -1 "$escalation_lock" 2>/dev/null || echo "")
lock_age=$(($(date +%s) - $(stat -f %m "$escalation_lock" 2>/dev/null || stat -c %Y "$escalation_lock" 2>/dev/null || echo "0")))
lock_age=$(($(date +%s) - $(stat -c %Y "$escalation_lock" 2>/dev/null || stat -f %m "$escalation_lock" 2>/dev/null || echo "0")))

Choose a reason for hiding this comment

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

medium

For consistency with other scripts in this pull request and for cleaner syntax, it's better to avoid the redundant nested parentheses for arithmetic expansion. The $(($(...))) pattern can be simplified to $((...)). This improves readability and aligns with the common practice seen in the other modified files.

Suggested change
lock_age=$(($(date +%s) - $(stat -c %Y "$escalation_lock" 2>/dev/null || stat -f %m "$escalation_lock" 2>/dev/null || echo "0")))
lock_age=$(( $(date +%s) - $(stat -c %Y "$escalation_lock" 2>/dev/null || stat -f %m "$escalation_lock" 2>/dev/null || echo "0") ))

@marcusquinn marcusquinn deleted the bugfix/stat-linux-compat branch February 21, 2026 01:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

stat -f %m (macOS-first) breaks on Linux — stdout pollution causes "unbound variable" errors

1 participant