Skip to content

fix: enforce worker TODO.md restriction with multi-layer guards (t173)#649

Merged
marcusquinn merged 1 commit intomainfrom
feature/t173
Feb 8, 2026
Merged

fix: enforce worker TODO.md restriction with multi-layer guards (t173)#649
marcusquinn merged 1 commit intomainfrom
feature/t173

Conversation

@marcusquinn
Copy link
Owner

Summary

  • Prevents TODO.md race condition where multiple workers + supervisor push to TODO.md on main simultaneously, causing merge conflicts
  • Adds three layers of enforcement: prompt injection, context isolation, and git pre-commit hook
  • Fixes ref:GH#564

Changes

supervisor-helper.sh — Prompt-level guard

  • build_dispatch_cmd() now injects explicit "DO NOT EDIT TODO.md" restriction into every worker prompt

loop-common.sh — Context-level guard

  • Skips TODO.md reads in headless mode (workers never see TODO.md tasks in re-anchor context)
  • Adds MANDATORY Worker Restrictions block to re-anchor prompt when FULL_LOOP_HEADLESS=true
  • Workers only work on their assigned task, not tasks picked from TODO.md

full-loop-helper.sh — Git-level guard

  • install_headless_todo_guard() installs a pre-commit hook in the worker worktree that rejects any commit containing TODO.md or todo/ changes when FULL_LOOP_HEADLESS=true
  • remove_headless_todo_guard() cleans up the hook on loop cancel/complete
  • Guard is installed at cmd_start() when --headless is active

Testing

  • bash -n syntax check: all 3 files pass
  • shellcheck -S error: zero violations on all 3 files
  • test-smoke-help.sh: 284 passed, 0 failed
  • test-batch-quality-hardening.sh: 55/56 passed (1 expected diff: deployed vs repo version)

Workers (headless dispatch runners) must never edit TODO.md — the supervisor
owns all TODO.md updates. Without enforcement, multiple workers + supervisor
pushing to TODO.md on main simultaneously causes merge conflicts.

Three layers of protection:

1. Prompt-level: build_dispatch_cmd() injects explicit 'DO NOT EDIT TODO.md'
   restriction into every worker prompt (supervisor-helper.sh)

2. Context-level: loop-common.sh skips TODO.md reads in headless mode so
   workers never see TODO.md tasks in their re-anchor context, and adds
   MANDATORY Worker Restrictions block to the re-anchor prompt

3. Git-level: full-loop-helper.sh installs a pre-commit hook in the worker
   worktree that rejects any commit containing TODO.md or todo/ changes
   when FULL_LOOP_HEADLESS=true. Cleaned up on loop cancel/complete.
@coderabbitai
Copy link
Contributor

coderabbitai bot commented Feb 8, 2026

Warning

Rate limit exceeded

@marcusquinn has exceeded the limit for the number of commits that can be reviewed per hour. Please wait 2 minutes and 37 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.

✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/t173

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

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 robustness of the system by implementing a comprehensive strategy to manage TODO.md access in a multi-worker environment. The changes ensure that only the supervisor can modify TODO.md, preventing potential race conditions and merge conflicts that could arise from concurrent worker operations. This is achieved through explicit instructions in worker prompts, isolation of TODO.md context from headless workers, and a strong git-level enforcement mechanism.

Highlights

  • Race Condition Prevention: Implements safeguards to prevent TODO.md merge conflicts caused by multiple workers and the supervisor simultaneously modifying the file.
  • Multi-Layered Enforcement: Introduces three distinct layers of protection: prompt injection, context isolation, and a git pre-commit hook to enforce TODO.md restrictions.
  • Bug Fix: Addresses and resolves the issue referenced as GH#564, which was related to TODO.md race conditions.
Changelog
  • .agents/scripts/full-loop-helper.sh
    • Introduced install_headless_todo_guard() to dynamically create or update a git pre-commit hook that blocks commits containing changes to TODO.md or the todo/ directory when in headless mode.
    • Added remove_headless_todo_guard() for cleaning up this pre-commit hook upon loop cancellation or completion.
    • Integrated the installation of this guard into the cmd_start() function and its removal into cmd_cancel() and cmd_complete().
  • .agents/scripts/loop-common.sh
    • Modified loop_generate_reanchor() to detect headless worker mode and conditionally skip reading TODO.md for task extraction.
    • Incorporated a "MANDATORY Worker Restrictions" block into the re-anchor prompt for headless workers, explicitly instructing them not to interact with TODO.md or related planning files.
    • Ensured the TODO.md In Progress section is omitted from the re-anchor context when workers are in headless mode.
  • .agents/scripts/supervisor-helper.sh
    • Updated build_dispatch_cmd() to inject a clear "MANDATORY Worker Restrictions" message directly into the prompt given to workers, reinforcing the rule against modifying TODO.md and todo/ files.
Activity
  • New features have been added to enforce TODO.md restrictions for worker processes.
  • A known issue (GH#564) related to TODO.md race conditions has been addressed.
  • The changes have undergone thorough testing, including bash -n syntax checks, shellcheck -S error with zero violations, test-smoke-help.sh passing all 284 tests, and test-batch-quality-hardening.sh passing 55 out of 56 tests (with one expected diff).
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

github-actions bot commented Feb 8, 2026

🔍 Code Quality Report

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

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

�[0;34m[INFO]�[0m Recent monitoring activity:
Sun Feb 8 18:55:10 UTC 2026: Code review monitoring started
Sun Feb 8 18:55:10 UTC 2026: SonarCloud - Bugs: 0, Vulnerabilities: 0, Code Smells: 37

📈 Current Quality Metrics

  • BUGS: 0
  • CODE SMELLS: 37
  • VULNERABILITIES: 0

Generated on: Sun Feb 8 18:55:13 UTC 2026


Generated by AI DevOps Framework Code Review Monitoring

@sonarqubecloud
Copy link

sonarqubecloud bot commented Feb 8, 2026

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 introduces a multi-layered defense to prevent workers from modifying TODO.md, which is a solid approach to fixing the race condition. The changes include prompt injection, context isolation, and a git pre-commit hook. The implementation is mostly good, but I've found a critical issue in the git hook removal logic that could corrupt the pre-commit file, and a medium-severity issue regarding duplicated code that affects maintainability. My review includes suggestions to address these points.

Comment on lines +94 to +143
install_headless_todo_guard() {
local git_dir
git_dir=$(git rev-parse --git-dir 2>/dev/null || echo "")
if [[ -z "$git_dir" ]]; then
return 0
fi

local hooks_dir="$git_dir/hooks"
local hook_file="$hooks_dir/pre-commit"
local guard_marker="# t173-headless-todo-guard"

mkdir -p "$hooks_dir"

# If a pre-commit hook already exists, append our guard (if not already present)
if [[ -f "$hook_file" ]]; then
if grep -q "$guard_marker" "$hook_file" 2>/dev/null; then
return 0 # Already installed
fi
# Append to existing hook
cat >> "$hook_file" << 'GUARD'

# t173-headless-todo-guard
# Block TODO.md and planning file commits in headless worker mode
if [[ "${FULL_LOOP_HEADLESS:-false}" == "true" ]]; then
if git diff --cached --name-only | grep -qE '^(TODO\.md|todo/)'; then
echo "[t173 GUARD] BLOCKED: Headless workers must not commit TODO.md or todo/ files."
echo "[t173 GUARD] The supervisor owns all TODO.md updates. Put notes in commit messages or PR body."
exit 1
fi
fi
GUARD
else
# Create new hook
cat > "$hook_file" << 'GUARD'
#!/usr/bin/env bash
# t173-headless-todo-guard
# Block TODO.md and planning file commits in headless worker mode
if [[ "${FULL_LOOP_HEADLESS:-false}" == "true" ]]; then
if git diff --cached --name-only | grep -qE '^(TODO\.md|todo/)'; then
echo "[t173 GUARD] BLOCKED: Headless workers must not commit TODO.md or todo/ files."
echo "[t173 GUARD] The supervisor owns all TODO.md updates. Put notes in commit messages or PR body."
exit 1
fi
fi
GUARD
chmod +x "$hook_file"
fi

return 0
}

Choose a reason for hiding this comment

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

high

To make the removal of this git hook guard more robust, I recommend adding unique start and end markers to the heredoc. This will allow for a more reliable removal process that isn't prone to errors from nested if/fi blocks. I'll suggest a corresponding change in remove_headless_todo_guard.

# Install a git pre-commit hook that blocks TODO.md changes in headless mode (t173)
# This is a hard guard — even if the AI agent tries to commit TODO.md, git rejects it.
install_headless_todo_guard() {
    local git_dir
    git_dir=$(git rev-parse --git-dir 2>/dev/null || echo "")
    if [[ -z "$git_dir" ]]; then
        return 0
    fi

    local hooks_dir="$git_dir/hooks"
    local hook_file="$hooks_dir/pre-commit"
    local guard_marker="# t173-headless-todo-guard-start"

    mkdir -p "$hooks_dir"

    # If a pre-commit hook already exists, append our guard (if not already present)
    if [[ -f "$hook_file" ]]; then
        if grep -q "$guard_marker" "$hook_file" 2>/dev/null; then
            return 0  # Already installed
        fi
        # Append to existing hook
        cat >> "$hook_file" << 'GUARD'

# t173-headless-todo-guard-start
# Block TODO.md and planning file commits in headless worker mode
if [[ "${FULL_LOOP_HEADLESS:-false}" == "true" ]]; then
    if git diff --cached --name-only | grep -qE '^(TODO\.md|todo/)'; then
        echo "[t173 GUARD] BLOCKED: Headless workers must not commit TODO.md or todo/ files."
        echo "[t173 GUARD] The supervisor owns all TODO.md updates. Put notes in commit messages or PR body."
        exit 1
    fi
fi
# t173-headless-todo-guard-end
GUARD
    else
        # Create new hook
        cat > "$hook_file" << 'GUARD'
#!/usr/bin/env bash
# t173-headless-todo-guard-start
# Block TODO.md and planning file commits in headless worker mode
if [[ "${FULL_LOOP_HEADLESS:-false}" == "true" ]]; then
    if git diff --cached --name-only | grep -qE '^(TODO\.md|todo/)'; then
        echo "[t173 GUARD] BLOCKED: Headless workers must not commit TODO.md or todo/ files."
        echo "[t173 GUARD] The supervisor owns all TODO.md updates. Put notes in commit messages or PR body."
        exit 1
    fi
fi
# t173-headless-todo-guard-end
GUARD
        chmod +x "$hook_file"
    fi

    return 0
}

Comment on lines +146 to +169
remove_headless_todo_guard() {
local git_dir
git_dir=$(git rev-parse --git-dir 2>/dev/null || echo "")
if [[ -z "$git_dir" ]]; then
return 0
fi

local hook_file="$git_dir/hooks/pre-commit"
if [[ ! -f "$hook_file" ]]; then
return 0
fi

# Remove the guard block (from marker to end of guard)
if grep -q "t173-headless-todo-guard" "$hook_file" 2>/dev/null; then
# Use sed to remove the guard block
local tmp_file
tmp_file=$(mktemp)
awk '/# t173-headless-todo-guard/{skip=1} /^fi$/ && skip{skip=0; next} !skip' "$hook_file" > "$tmp_file"
mv "$tmp_file" "$hook_file"
chmod +x "$hook_file"
fi

return 0
}

Choose a reason for hiding this comment

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

high

The awk command used here is not robust. It stops skipping lines at the first fi it encounters. Since the guard block contains a nested if, this will corrupt the pre-commit hook by only partially removing the block.

To fix this, I've suggested adding unique start/end markers in install_headless_todo_guard. With those markers, sed can be used here for a reliable removal.

# Remove the t173 headless guard from pre-commit hook (cleanup)
remove_headless_todo_guard() {
    local git_dir
    git_dir=$(git rev-parse --git-dir 2>/dev/null || echo "")
    if [[ -z "$git_dir" ]]; then
        return 0
    fi

    local hook_file="$git_dir/hooks/pre-commit"
    if [[ ! -f "$hook_file" ]]; then
        return 0
    fi

    # Remove the guard block (from marker to end of guard)
    if grep -q "# t173-headless-todo-guard-start" "$hook_file" 2>/dev/null; then
        # Use sed to remove the guard block
        local tmp_file
        tmp_file=$(mktemp)
        sed '/^# t173-headless-todo-guard-start$/,/^# t173-headless-todo-guard-end$/d' "$hook_file" > "$tmp_file"
        mv "$tmp_file" "$hook_file"
        chmod +x "$hook_file"
    fi

    return 0
}

Comment on lines +392 to +404
# Build headless worker restriction block (t173)
local headless_restriction=""
if [[ "$is_headless" == "true" ]]; then
headless_restriction="
## MANDATORY Worker Restrictions (t173 - Headless Mode)

- **Do NOT edit, commit, or push TODO.md** — the supervisor owns all TODO.md updates.
- **Do NOT edit todo/PLANS.md or todo/tasks/*** — these are supervisor-managed.
- Report status via exit code, log output, and PR creation only.
- Put task notes in commit messages or PR body, never in TODO.md.
- Work ONLY on the assigned task described above. Do not pick tasks from TODO.md.
"
fi

Choose a reason for hiding this comment

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

medium

This block of text defining worker restrictions is very similar to another block added in .agents/scripts/supervisor-helper.sh. Duplicating this text can lead to maintainability issues, as changes would need to be made in multiple places, and they have already diverged slightly.

Since both scripts source shared-constants.sh, consider defining this restriction text as a readonly variable there and reusing it in both places. This would ensure consistency and simplify future updates.

Comment on lines +2583 to +2591
# t173: Explicit worker restriction — prevents TODO.md race condition
prompt="$prompt

## MANDATORY Worker Restrictions (t173)
- Do NOT edit, commit, or push TODO.md — the supervisor owns all TODO.md updates.
- Do NOT edit todo/PLANS.md or todo/tasks/* — these are supervisor-managed.
- Report status via exit code, log output, and PR creation only.
- Put task notes in commit messages or PR body, never in TODO.md."

Choose a reason for hiding this comment

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

medium

This block of text defining worker restrictions is very similar to another block added in .agents/scripts/loop-common.sh. Duplicating this text can lead to maintainability issues, as changes would need to be made in multiple places, and they have already diverged slightly.

Since both scripts source shared-constants.sh, consider defining this restriction text as a readonly variable there and reusing it in both places. This would ensure consistency and simplify future updates.

@marcusquinn marcusquinn merged commit eb6f90e into main Feb 8, 2026
19 checks passed
@marcusquinn marcusquinn deleted the feature/t173 branch February 21, 2026 01:59
@marcusquinn marcusquinn added the code-reviews-actioned All review feedback has been actioned label Mar 7, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

code-reviews-actioned All review feedback has been actioned

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant