Skip to content

fix: prevent aidevops update from dirtying git working tree#2287

Merged
marcusquinn merged 1 commit intomainfrom
bugfix/fix-update-dirty-worktree
Feb 25, 2026
Merged

fix: prevent aidevops update from dirtying git working tree#2287
marcusquinn merged 1 commit intomainfrom
bugfix/fix-update-dirty-worktree

Conversation

@marcusquinn
Copy link
Copy Markdown
Owner

Summary

Fixes #2286aidevops update left uncommitted changes in the git working tree, causing subsequent updates to fail with merge conflicts.

Root Cause

Two functions modified tracked files in the git repo during setup.sh --non-interactive (called by cmd_update()):

  1. set_permissions() (setup-modules/core.sh:539) — used relative paths (.agents/scripts/*.sh) that resolved to the git repo, changing file modes on tracked files
  2. update_scan_results_log() (security-helper.sh:394) — used git rev-parse --show-toplevel to find the repo root, then wrote timestamps and scan history to .agents/SKILL-SCAN-RESULTS.md in the working tree

Fix (3 parts)

  • set_permissions(): Target the deployed directory (~/.aidevops/agents/scripts/) instead of repo-relative .agents/scripts/
  • update_scan_results_log(): Write to the deployed copy at ~/.aidevops/agents/SKILL-SCAN-RESULTS.md instead of the git working tree
  • cmd_update(): Add pre-pull cleanup of stale changes (fixes existing broken installs) and post-setup safety net (git checkout -- .)

Testing

  • All 629 tests pass (496 passed, 133 skipped, 0 failed) via test-smoke-help.sh
  • ShellCheck: no new violations
  • Bash syntax check: all 3 modified files pass bash -n

Root cause: set_permissions() in setup-modules/core.sh used relative paths
that resolved to the git repo when called from cmd_update(), changing file
modes on tracked files. update_scan_results_log() in security-helper.sh
used git rev-parse --show-toplevel to write scan results into the repo.

Fix (3 parts):
- set_permissions(): target deployed dir (~/.aidevops/agents/scripts/)
  instead of repo-relative .agents/scripts/
- update_scan_results_log(): write to deployed copy at
  ~/.aidevops/agents/SKILL-SCAN-RESULTS.md instead of repo working tree
- cmd_update(): add pre-pull cleanup of stale changes (fixes existing
  broken installs) and post-setup safety net (git checkout -- .)
@coderabbitai
Copy link
Copy Markdown
Contributor

coderabbitai bot commented Feb 25, 2026

Warning

Rate limit exceeded

@marcusquinn has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 3 minutes and 22 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 9bbcf0a and bcc44fa.

📒 Files selected for processing (3)
  • .agents/scripts/security-helper.sh
  • aidevops.sh
  • setup-modules/core.sh
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch bugfix/fix-update-dirty-worktree

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.

@gemini-code-assist
Copy link
Copy Markdown

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 addresses an issue where aidevops update operations would leave uncommitted changes in the Git working tree, leading to subsequent update failures due to merge conflicts. The core of the fix involves redirecting file modifications (such as setting permissions and writing scan results) from the Git repository's working tree to the deployed application directory. Additionally, the update process has been made more robust by incorporating explicit Git working tree cleanup steps before and after setup operations, ensuring a clean state for git pull.

Highlights

  • Permission Setting: The set_permissions() function now targets the deployed directory (~/.aidevops/agents/scripts/) for setting executable permissions, rather than modifying files directly within the Git repository.
  • Scan Results Logging: The update_scan_results_log() function has been updated to write scan results to the deployed copy at ~/.aidevops/agents/SKILL-SCAN-RESULTS.md, preventing modifications to the Git working tree.
  • Update Command Robustness: The cmd_update() function now includes a pre-pull cleanup step (git checkout -- .) to discard stale changes before fetching updates, and a post-setup safety net (git checkout -- .) to ensure a clean working tree after setup.sh runs.
Changelog
  • .agents/scripts/security-helper.sh
    • Modified update_scan_results_log to write scan results to the deployed directory ($HOME/.aidevops/agents) instead of the Git repository's working tree.
  • aidevops.sh
    • Added a git checkout -- . command before git fetch in cmd_update to clean up stale working tree changes.
    • Introduced git checkout -- . as a safety net after setup.sh calls within cmd_update to discard any changes introduced by the setup script.
  • setup-modules/core.sh
    • Updated set_permissions to apply executable permissions to scripts in the deployed directory ($HOME/.aidevops/agents/scripts/) instead of directly modifying files within the Git repository.
    • Adjusted configuration file permission setting to target user-specific config directories and repo-local configs appropriately.
Activity
  • No human activity has occurred on this pull request yet.
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.

@github-actions
Copy link
Copy Markdown
Contributor

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Wed Feb 25 15:21:54 UTC 2026: Code review monitoring started
Wed Feb 25 15:21:55 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 71

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 71
  • VULNERABILITIES: 0

Generated on: Wed Feb 25 15:21:57 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link
Copy Markdown

@augmentcode
Copy link
Copy Markdown

augmentcode bot commented Feb 25, 2026

🤖 Augment PR Summary

Summary: Prevents aidevops update from leaving a dirty git working tree that can block fast-forward pulls.

Changes:

  • Write skill scan results history to the deployed copy under ~/.aidevops/agents/ instead of the repo working tree
  • Update set_permissions() to target deployed agent scripts (avoids chmod changes on tracked repo files)
  • Add pre-pull cleanup and post-setup safety checkout in cmd_update() to recover from prior stale changes

Technical Notes: Aims to keep updates compatible with git pull --ff-only by ensuring setup/update don’t modify tracked files in-place.

🤖 Was this summary useful? React with 👍 or 👎

Copy link
Copy Markdown

@augmentcode augmentcode bot left a comment

Choose a reason for hiding this comment

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

Review completed. 3 suggestions posted.

Fix All in Augment

Comment augment review to trigger a new review at any time.

# SCAN_RESULTS_FILE is ".agents/SKILL-SCAN-RESULTS.md" (repo-relative);
# strip the ".agents/" prefix to get the deployed path
local results_filename="${SCAN_RESULTS_FILE#.agents/}"
local results_file="${deployed_dir}/${results_filename}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

update_scan_results_log() now writes to results_file under ~/.aidevops/agents, but the success message later still prints ${SCAN_RESULTS_FILE} (repo-relative), which may mislead users when they try to locate the log output.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

# See: https://github.com/marcusquinn/aidevops/issues/2286
if ! git diff --quiet 2>/dev/null; then
print_info "Cleaning up stale working tree changes..."
git checkout -- . 2>/dev/null || true
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

This git checkout -- . will discard any local working tree edits in $INSTALL_DIR, not just the stale chmod/scan-log changes from prior updates; that’s potentially surprising data loss if someone has custom local modifications.

Severity: medium

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

# (e.g. chmod on tracked scripts, scan results written to repo)
# This ensures git pull --ff-only won't be blocked.
# See: https://github.com/marcusquinn/aidevops/issues/2286
if ! git diff --quiet 2>/dev/null; then
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

git diff --quiet only checks unstaged working tree changes; staged/index changes could still block git pull --ff-only, so the “won’t be blocked” comment here may not hold in that case.

Severity: low

Fix This in Augment

🤖 Was this useful? React with 👍 or 👎, or 🚀 if it prevented an incident/outage.

Copy link
Copy Markdown

@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 resolves the issue where aidevops update would dirty the git working tree, preventing future updates. The changes correctly retarget file operations to the deployed application directory instead of the source repository. The addition of cleanup steps in cmd_update to handle previously broken installations is a thoughtful touch. My review focuses on improving error handling by removing unnecessary stderr suppression, which aligns with the repository's general scripting guidelines and will make the scripts more robust and easier to debug.

local deployed_dir="$HOME/.aidevops/agents"
# SCAN_RESULTS_FILE is ".agents/SKILL-SCAN-RESULTS.md" (repo-relative);
# strip the ".agents/" prefix to get the deployed path
local results_filename="${SCAN_RESULTS_FILE#.agents/}"
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The parameter expansion ${SCAN_RESULTS_FILE#.agents/} is a clean way to strip the prefix. However, for improved readability and to make the intent clearer, using basename might be a better choice, especially if the structure of SCAN_RESULTS_FILE were to change in the future.

Suggested change
local results_filename="${SCAN_RESULTS_FILE#.agents/}"
local results_filename="$(basename "$SCAN_RESULTS_FILE")"

# (e.g. chmod on tracked scripts, scan results written to repo)
# This ensures git pull --ff-only won't be blocked.
# See: https://github.com/marcusquinn/aidevops/issues/2286
if ! git diff --quiet 2>/dev/null; then
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Using 2>/dev/null here suppresses potentially useful error messages from git. The git diff --quiet command is already silent on success, so this redirection only hides problems. Removing it will make debugging easier if something unexpected occurs, which is in line with the project's scripting guidelines.

Suggested change
if ! git diff --quiet 2>/dev/null; then
if ! git diff --quiet; then
References
  1. Avoid using '2>/dev/null' for blanket suppression of command errors in shell scripts to ensure that authentication, syntax, or system issues remain visible for debugging.

# See: https://github.com/marcusquinn/aidevops/issues/2286
if ! git diff --quiet 2>/dev/null; then
print_info "Cleaning up stale working tree changes..."
git checkout -- . 2>/dev/null || true
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Blanket suppression of stderr with 2>/dev/null is discouraged as it can hide underlying issues, such as file permission problems during checkout. Since git checkout -- . is silent on success, there's no need to redirect its output. The || true is sufficient to handle expected failures. Removing the redirection aligns with project best practices for error visibility.

Suggested change
git checkout -- . 2>/dev/null || true
git checkout -- . || true
References
  1. Avoid using '2>/dev/null' for blanket suppression of command errors in shell scripts to ensure that authentication, syntax, or system issues remain visible for debugging.

# Safety net: discard any working tree changes setup.sh may have introduced
# (e.g. chmod on tracked scripts, scan results written to repo)
# See: https://github.com/marcusquinn/aidevops/issues/2286
git checkout -- . 2>/dev/null || true
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This git checkout command also unnecessarily suppresses stderr. For better error visibility and easier debugging, it's recommended to remove 2>/dev/null. The || true will still prevent the script from exiting on failure. This change aligns with the project's general rule to avoid hiding potential errors.

Suggested change
git checkout -- . 2>/dev/null || true
git checkout -- . || true
References
  1. Avoid using '2>/dev/null' for blanket suppression of command errors in shell scripts to ensure that authentication, syntax, or system issues remain visible for debugging.

# Safety net: discard any working tree changes setup.sh may have introduced
# (e.g. chmod on tracked scripts, scan results written to repo)
# See: https://github.com/marcusquinn/aidevops/issues/2286
git checkout -- . 2>/dev/null || true
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

To maintain consistency and improve debuggability, please remove the stderr suppression (2>/dev/null) from this git checkout command as well. This will allow any unexpected errors to be reported, following the project's guideline on error handling.

Suggested change
git checkout -- . 2>/dev/null || true
git checkout -- . || true
References
  1. Avoid using '2>/dev/null' for blanket suppression of command errors in shell scripts to ensure that authentication, syntax, or system issues remain visible for debugging.

Comment on lines +547 to 555
chmod +x "$deployed_dir/scripts/"*.sh 2>/dev/null || true
# Also handle modularised subdirectories (e.g. memory/, supervisor-modules/)
find "$deployed_dir/scripts" -mindepth 2 -name "*.sh" -exec chmod +x {} + 2>/dev/null || true
fi

# Secure configuration files (these are in the user's config dir, not the repo)
chmod 600 "$HOME/.config/aidevops/"*.json 2>/dev/null || true
# Also secure repo-local configs if present (for interactive setup from repo root)
chmod 600 configs/*.json 2>/dev/null || true
Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

Throughout this function, 2>/dev/null is used to suppress errors from chmod and find. While this handles cases where glob patterns don't match any files, it also hides potentially critical errors like permission issues. It's better to remove 2>/dev/null to improve debuggability. The || true is sufficient to prevent the script from exiting on non-fatal errors (like no files matching a glob). This change would align with the project's general rules promoting error visibility.

Suggested change
chmod +x "$deployed_dir/scripts/"*.sh 2>/dev/null || true
# Also handle modularised subdirectories (e.g. memory/, supervisor-modules/)
find "$deployed_dir/scripts" -mindepth 2 -name "*.sh" -exec chmod +x {} + 2>/dev/null || true
fi
# Secure configuration files (these are in the user's config dir, not the repo)
chmod 600 "$HOME/.config/aidevops/"*.json 2>/dev/null || true
# Also secure repo-local configs if present (for interactive setup from repo root)
chmod 600 configs/*.json 2>/dev/null || true
chmod +x "$deployed_dir/scripts/"*.sh || true
# Also handle modularised subdirectories (e.g. memory/, supervisor-modules/)
find "$deployed_dir/scripts" -mindepth 2 -name "*.sh" -exec chmod +x {} + || true
fi
# Secure configuration files (these are in the user's config dir, not the repo)
chmod 600 "$HOME/.config/aidevops/"*.json || true
# Also secure repo-local configs if present (for interactive setup from repo root)
chmod 600 configs/*.json || true
References
  1. Avoid using '2>/dev/null' to suppress errors on file operations if the file's existence has already been verified by a preceding check (e.g., [[ -f "$file" ]] or an early return). This practice is redundant for 'file not found' errors and can mask other important issues like permissions problems.
  2. In shell scripts with 'set -e' enabled, use '|| true' to prevent the script from exiting when a command like 'jq' fails on an optional lookup. Do not suppress stderr with '2>/dev/null' so that actual syntax or system errors remain visible.

@marcusquinn marcusquinn merged commit fa24d01 into main Feb 25, 2026
27 checks passed
marcusquinn added a commit that referenced this pull request Feb 25, 2026
… detached HEAD

Fixes #2288 — aidevops update stuck on old version (e.g. 2.125 while 2.128.3 available).

Root cause: git pull --ff-only fails when the working tree is dirty or local
history has diverged, with no recovery path. Users on pre-PR#2287 versions
could not get the dirty-tree fix because the fix itself required a successful pull.

Three changes:
1. Ensure local repo is on main branch before pulling (handles detached HEAD)
2. Clean both staged and unstaged changes before pull (extends PR #2287 fix)
3. Fall back to git reset --hard origin/main when --ff-only fails, instead of
   returning an error — the install repo should always mirror origin/main exactly
marcusquinn added a commit that referenced this pull request Feb 25, 2026
… detached HEAD (#2289)

Fixes #2288 — aidevops update stuck on old version (e.g. 2.125 while 2.128.3 available).

Root cause: git pull --ff-only fails when the working tree is dirty or local
history has diverged, with no recovery path. Users on pre-PR#2287 versions
could not get the dirty-tree fix because the fix itself required a successful pull.

Three changes:
1. Ensure local repo is on main branch before pulling (handles detached HEAD)
2. Clean both staged and unstaged changes before pull (extends PR #2287 fix)
3. Fall back to git reset --hard origin/main when --ff-only fails, instead of
   returning an error — the install repo should always mirror origin/main exactly
@marcusquinn marcusquinn deleted the bugfix/fix-update-dirty-worktree branch March 3, 2026 03:23
marcusquinn added a commit that referenced this pull request Apr 13, 2026
…manence trap

Adds _is_bot_generated_cleanup_issue() helper that detects issues created
by post-merge-review-scanner.sh (labels: review-followup or source:review-
scanner). Extracts the NMR approval gate from _dispatch_dedup_check_layers
into _check_nmr_approval_gate() to keep the parent function under the
100-line complexity threshold.

When the gate evaluates, if:
  (a) the issue is bot-generated cleanup AND
  (b) the needs-maintainer-review label is NOT currently present,
then the historical timeline-based ever-NMR check is skipped, allowing
dispatch to proceed.

The exemption does NOT fire when NMR is currently present — the active
case preserves t1894's security posture (maintainer-gated issues still
require cryptographic approval). The exemption surgically unblocks the
drift case where the fast-fail escalation path applied NMR, the maintainer
subsequently removed it, and the historical timeline event keeps the
issue permanently blocked.

Live evidence from the 2026-04-13 awardsapp queue drain:
  #2294, #2293, #2292, #2290, #2287 all stuck on ever-NMR with current
  labels: [auto-dispatch, origin:worker, origin:interactive, review-
  followup, source:review-scanner]. No current NMR label. Fix 3a
  immediately unblocks them on the next pulse cycle.

9 new unit tests cover: review-followup detection, source:review-scanner
detection, both labels together, regular issue rejection, empty labels
array, empty meta_json, invalid JSON, partial-name rejection (avoid
'review-followup-queued' false positive), and the exact awardsapp#2294
regression scenario.

Function lengths after refactor:
  _dispatch_dedup_check_layers: 77 lines (was 83 pre-Fix-3, 103 post-Fix-3
                                           before _check_nmr_approval_gate
                                           extraction)
  _check_nmr_approval_gate:     23 lines (new)
  _is_bot_generated_cleanup_issue: 6 lines (new)

Scope: intentionally surgical. Does NOT change timeline API semantics,
remove NMR from scanner output (GH#18538), or touch the origin:worker
vs origin:interactive double-labeling (separate bug).

Fixes #18648
marcusquinn added a commit that referenced this pull request Apr 13, 2026
…manence trap (#18649)

Adds _is_bot_generated_cleanup_issue() helper that detects issues created
by post-merge-review-scanner.sh (labels: review-followup or source:review-
scanner). Extracts the NMR approval gate from _dispatch_dedup_check_layers
into _check_nmr_approval_gate() to keep the parent function under the
100-line complexity threshold.

When the gate evaluates, if:
  (a) the issue is bot-generated cleanup AND
  (b) the needs-maintainer-review label is NOT currently present,
then the historical timeline-based ever-NMR check is skipped, allowing
dispatch to proceed.

The exemption does NOT fire when NMR is currently present — the active
case preserves t1894's security posture (maintainer-gated issues still
require cryptographic approval). The exemption surgically unblocks the
drift case where the fast-fail escalation path applied NMR, the maintainer
subsequently removed it, and the historical timeline event keeps the
issue permanently blocked.

Live evidence from the 2026-04-13 awardsapp queue drain:
  #2294, #2293, #2292, #2290, #2287 all stuck on ever-NMR with current
  labels: [auto-dispatch, origin:worker, origin:interactive, review-
  followup, source:review-scanner]. No current NMR label. Fix 3a
  immediately unblocks them on the next pulse cycle.

9 new unit tests cover: review-followup detection, source:review-scanner
detection, both labels together, regular issue rejection, empty labels
array, empty meta_json, invalid JSON, partial-name rejection (avoid
'review-followup-queued' false positive), and the exact awardsapp#2294
regression scenario.

Function lengths after refactor:
  _dispatch_dedup_check_layers: 77 lines (was 83 pre-Fix-3, 103 post-Fix-3
                                           before _check_nmr_approval_gate
                                           extraction)
  _check_nmr_approval_gate:     23 lines (new)
  _is_bot_generated_cleanup_issue: 6 lines (new)

Scope: intentionally surgical. Does NOT change timeline API semantics,
remove NMR from scanner output (GH#18538), or touch the origin:worker
vs origin:interactive double-labeling (separate bug).

Fixes #18648
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.

fix: aidevops update dirties git working tree, blocking next update

1 participant