Compare commits

...

41 Commits

Author SHA1 Message Date
gemini-cli[bot] 88a12098d4 fix(core): prevent text duplication in AfterAgent hook prompt_response
Fixed a logic error in `Turn.getResponseText()` where all individual streaming chunks were being joined with a space. This lead to two issues:
1. Extra spaces between words/chunks in the output.
2. Massive duplication of the entire response text because the final consolidated response (yielded by some content generators or logic) was being joined with the individual chunks.

The new implementation uses a robust heuristic: if a chunk's text starts with the accumulated text so far, it is treated as a cumulative update and replaces the current buffer; otherwise, it is treated as a delta and appended. This handles delta chunks, cumulative chunks, and redundant consolidated responses gracefully.

Fixes #27030

cc @spencertang @mbleigh
2026-05-14 23:43:01 +00:00
Christian Gunderman 4293ddfcc7 fix(ci): fix repo context for gh run in brain workflow 2026-05-14 16:18:39 -07:00
Christian Gunderman 53a573d7ef fix(ci): fix paths and cache in gemini-cli-bot-brain workflow 2026-05-14 16:14:54 -07:00
Christian Gunderman 6b6f0a3655 fix(ci): update bot workflow to use multi-directory checkout 2026-05-14 16:06:14 -07:00
Christian Gunderman 3795f40cf6 fix(bot): forbid agent from fixing metrics scripts and enforce issue tracker 2026-05-14 14:11:59 -07:00
Christian Gunderman 67b46ee365 fix(bot): mandate internal skill activation and workspace root writes in task skills 2026-05-14 12:05:47 -07:00
Christian Gunderman ad612d9311 chore(bot): strengthen mandate execution and skill activation instructions 2026-05-14 12:03:27 -07:00
Christian Gunderman 00aaba6759 fix(bot): ensure management files are written to and read from workspace root 2026-05-14 11:50:19 -07:00
Christian Gunderman 48e6cc179e fix(bot): explicitly target branch in 'gh pr edit' to ensure labeling 2026-05-14 11:11:44 -07:00
Christian Gunderman 746be830fe fix(bot): ensure branch names always start with 'bot/' prefix 2026-05-14 10:23:03 -07:00
Christian Gunderman f65cacfea4 fix(bot): exempt bots from PR rate limiter 2026-05-14 09:13:50 -07:00
Christian Gunderman 8d6b0effc3 fix(bot): always target main in brain workflow to prevent leaking feature branch changes 2026-05-14 09:08:00 -07:00
Christian Gunderman 6efdbd3e48 fix(bot): improve reasoning resilience and prevent infinite loops
- Update PR skill to allow informed escalation and help requests when stuck.

- Update main brain prompt to encourage strategic pivoting between hypotheses.

- Explicitly authorize bailing out of tasks that cannot be verified.
2026-05-13 21:50:51 -07:00
Christian Gunderman a2841350ba fix(bot): implement prompt-level loop prevention and enforce test timeouts 2026-05-13 21:15:05 -07:00
Christian Gunderman 928ce879ea fix(bot): improve patch resilience in brain workflow
- Pin publish job to the specific SHA from reasoning job to avoid drift.

- Use git apply --3way --ignore-whitespace for better conflict resolution.
2026-05-13 20:35:02 -07:00
Christian Gunderman 5dc5b4ed4a fix: handle ENAMETOOLONG gracefully during path resolution
This fix was recovered from a timed-out bot run. It addresses issue #26979
where the CLI would crash if a user provided an extremely long path string
in an @ command (e.g. @/aaa...a).

Changes:
- Updated 'robustRealpath' in 'packages/core/src/utils/paths.ts' to catch
  and gracefully handle 'ENAMETOOLONG' and 'EINVAL' errors from fs.realpathSync
  and fs.lstatSync.
- Added a defensive try-catch block to 'checkPermissions' in
  'packages/cli/src/ui/hooks/atCommandProcessor.ts' to prevent long path
  strings from crashing the CLI during @ command parsing.
- Added regression unit tests to verify the fix.
2026-05-13 20:24:15 -07:00
Christian Gunderman 98781cd97d chore(bot): remove maxSessionTurns and improve artifact archiving
- Removed the `maxSessionTurns: 30` limit from the bot's configuration as requested.
- Added `if: always()` to the `Archive Brain Data` step in the workflow to ensure logs (telemetry, debug) are available even when the agent fails or times out.
2026-05-13 18:37:18 -07:00
Christian Gunderman daaa631071 feat(bot): optimize search strategy in issue-fixer skill
Updated the issue-fixer skill to instruct the bot to search one top-level
folder at a time and avoid problematic directories with large data files
(memory-tests, last_brain_data) to prevent grep_search timeouts.
2026-05-13 18:27:39 -07:00
Christian Gunderman 506184d739 fix(bot): cap reasoning loops to 30 turns
The non-interactive CLI defaults to infinite `maxSessionTurns`. This causes the bot to get stuck in infinite trial-and-error loops when it struggles to fix a failing test suite, only stopping when the 60-minute GitHub Actions timeout kills the runner.

By setting `maxSessionTurns: 30` in the bot's configuration, the LLM session will gracefully abort with an error if it cannot find a solution within a reasonable number of attempts.
2026-05-13 17:59:25 -07:00
Christian Gunderman 9b3fef4f68 fix(ci): prevent infinite hangs in bot reasoning workflow
- Added a 60 minute timeout to the Reasoning job in the workflow.
- Updated the issue-fixer skill to instruct the bot to wrap 'npm run preflight' with the linux 'timeout' utility to prevent infinite loops from hanging the entire LLM agent.
2026-05-13 17:15:50 -07:00
Christian Gunderman 83d15895f1 fix(bot): archive pr-labels.txt to apply labels to PRs
The pr-labels.txt file was being correctly generated by the bot but was not
being passed to the Publish Artifacts job because it was omitted from the
Archive Brain Data step.
2026-05-13 16:29:02 -07:00
Christian Gunderman 03877eae3b fix(core): trust explicitly allowed variables over name patterns
This removes the `NEVER_ALLOWED_NAME_PATTERNS` filter from `getSecureSanitizationConfig`.

Previously, if a user explicitly added a variable like `GH_TOKEN` to their `allowedEnvironmentVariables` in `settings.json`, it would be silently dropped during configuration parsing because it matched the `NEVER_ALLOWED_NAME_PATTERNS` regex.

This change ensures that explicit user allowlists take precedence over heuristic name-based pattern matching, while still maintaining the strict blocklist for known highly-sensitive system variables (`NEVER_ALLOWED_ENVIRONMENT_VARIABLES`).
2026-05-13 16:11:34 -07:00
Christian Gunderman d17a813cc3 Revert "fix:reorder env var redaction checks to scan values first (#21059)"
This reverts commit 9773a084c9.
2026-05-13 15:29:05 -07:00
Christian Gunderman a6e460e595 feat(bot): add support for applying labels to bot-created PRs
- Update the publish workflow to read labels from a pr-labels.txt file and apply them to the PR using the gh CLI.
- Update the prs skill to instruct the bot to write labels to pr-labels.txt.
- Update the issue-fixer and metrics skills to explicitly request the application of their respective labels in pr-labels.txt.
2026-05-13 15:13:46 -07:00
Christian Gunderman 4449f3f43c chore(bot): configure environment variable redaction bypass
This adds a settings.json file to the bot's configuration to explicitly allow GH_TOKEN and GITHUB_TOKEN to bypass the environment variable redaction engine. This is required because the bot runs in GitHub Actions, which enforces strict redaction mode by default. The workflow file was also simplified by removing the wrapper script that was previously used to bypass redaction.
2026-05-13 15:05:02 -07:00
Christian Gunderman 7789469bd1 fix(bot): provide GH_TOKEN in bot-brain workflow to fix gh cli auth 2026-05-13 12:08:13 -07:00
Christian Gunderman 4464ff23fc fix(bot): disable gh pagers globally via env
- Sets `GH_PAGER: ''` in the workflow environment variables to ensure any `gh` CLI commands executed by the bot or in bash scripts do not hang waiting for user input in the headless CI environment.
2026-05-13 11:36:37 -07:00
Christian Gunderman 8a6ec5978c fix(bot): force json or --no-pager in gh cli commands to prevent hangs
- Replaces `gh issue list` with the `--json` flag to prevent default pagers (like `less`) from waiting for user input and hanging the CI run.
- Adds `--no-pager` to `gh run view`.
2026-05-13 11:17:02 -07:00
Christian Gunderman c460745bcb fix(bot): enforce github-first discovery and forbid local fishing
- Updates the `issue-fixer` skill to strictly forbid the use of local tools (like lint, tsc, grep) for finding tasks.
- Updates `scheduled.md` to clarify that local tools are for verification only.
2026-05-13 10:12:43 -07:00
Christian Gunderman 278858ed11 fix(bot): satisfy YAML lint rules and clarify monorepo build order 2026-05-13 09:54:50 -07:00
Christian Gunderman a58e3f5654 chore(build): standardize all package library targets to ES2023 2026-05-13 09:50:45 -07:00
Christian Gunderman 821cb2be9b feat(ci): re-enable telemetry and debug logging for the bot 2026-05-13 09:27:51 -07:00
Christian Gunderman 3250033366 fix(ci): resolve YAML syntax error in bot brain workflow 2026-05-12 22:35:04 -07:00
Christian Gunderman 7344507c7b feat(ci): enable detailed telemetry and debug logging for the bot 2026-05-12 22:32:42 -07:00
Christian Gunderman e9df8d2914 fix(bot): allow redirection in CI policy and improve skill activation steering 2026-05-12 22:20:37 -07:00
Christian Gunderman 9e90ccefb4 fix(bot): restrict critique skill to only fix scoped flaws 2026-05-12 21:35:11 -07:00
Christian Gunderman c1b15d23b6 fix(ci): make scheduled brain base prompt generic to respect mandate 2026-05-12 14:55:53 -07:00
Christian Gunderman 20669e964c Fix skill syntax. 2026-05-12 14:33:01 -07:00
Christian Gunderman c0e78767b7 feat(ci): improve mandate selection and add activate_skill to policy 2026-05-12 14:15:45 -07:00
Christian Gunderman 75ab39ca3a feat(ci): add mandate override to brain workflow 2026-05-12 14:15:41 -07:00
Christian Gunderman a11f347928 Issue fixer. 2026-05-12 14:15:38 -07:00
20 changed files with 421 additions and 207 deletions
+137 -76
View File
@@ -2,7 +2,8 @@ name: '🧠 Gemini CLI Bot: Brain'
on:
schedule:
- cron: '0 0 * * *' # Every 24 hours
- cron: '0 0 * * *' # Nightly (Strategic Metrics)
- cron: '0 */4 * * *' # Every 4 hours (Issue Fixing)
issue_comment:
types: ['created']
workflow_dispatch:
@@ -26,7 +27,16 @@ on:
enable_prs:
description: 'Enable PRs (automatically promote changes to PRs)'
type: 'boolean'
default: false
default: true
mandate:
description: 'Mandate to execute'
type: 'choice'
options:
- 'auto'
- 'issue-fixer'
- 'metrics'
- 'interactive'
default: 'auto'
concurrency:
group: '${{ github.workflow }}-${{ github.event.issue.number || github.event.inputs.issue_number || github.ref }}'
@@ -36,11 +46,11 @@ jobs:
reasoning:
name: 'Brain (Reasoning Layer)'
runs-on: 'ubuntu-latest'
timeout-minutes: 60
if: |
github.repository == 'google-gemini/gemini-cli' && (
github.event_name == 'schedule' ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.run_interactive != 'true') ||
(github.event_name == 'workflow_dispatch' && github.event.inputs.run_interactive == 'true') ||
(github.event_name == 'workflow_dispatch') ||
(github.event_name == 'issue_comment' && github.event.comment.user.login != 'gemini-cli[bot]' && contains(github.event.comment.body, '@gemini-cli') && contains(fromJSON('["COLLABORATOR", "MEMBER", "OWNER"]'), github.event.comment.author_association))
)
# The reasoning phase is strictly readonly.
@@ -48,45 +58,50 @@ jobs:
contents: 'read'
issues: 'read'
actions: 'read'
outputs:
sha: ${{ steps.get_sha.outputs.sha }}
env:
GEMINI_CLI_TRUST_WORKSPACE: 'true'
steps:
- name: 'Determine Checkout Ref'
id: 'determine_ref'
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
run: |
REF="${{ github.ref }}"
if [ -n "$ISSUE_NUMBER" ]; then
PR_HEAD=$(gh pr view "$ISSUE_NUMBER" --repo "${{ github.repository }}" --json headRefName --jq .headRefName 2>/dev/null || echo "")
if [ -n "$PR_HEAD" ]; then
REF="$PR_HEAD"
fi
fi
echo "ref=$REF" >> "$GITHUB_OUTPUT"
- name: 'Checkout'
- name: 'Checkout Branch (Agent Code)'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
with:
ref: '${{ steps.determine_ref.outputs.ref }}'
ref: '${{ github.ref }}'
path: 'agent-code'
fetch-depth: 0
persist-credentials: false
- name: 'Get Current SHA'
id: 'get_sha'
working-directory: agent-code
run: echo "sha=$(git rev-parse HEAD)" >> "$GITHUB_OUTPUT"
- name: 'Setup Node.js'
uses: 'actions/setup-node@49933ea5288caeca8642d1e84afbd3f7d6820020' # ratchet:actions/setup-node@v4
with:
node-version: '20'
cache: 'npm'
cache-dependency-path: 'agent-code/package-lock.json'
- name: 'Install dependencies'
working-directory: agent-code
run: 'npm ci'
- name: 'Build Gemini CLI'
working-directory: agent-code
run: 'npm run bundle'
- name: 'Checkout Main (Target Repo)'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
with:
ref: 'main'
path: 'repo-target'
fetch-depth: 0
persist-credentials: false
- name: 'Download Previous State'
env:
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
run: |
if [ "${{ github.event.inputs.clear_memory }}" = "true" ]; then
@@ -95,19 +110,19 @@ jobs:
fi
# Find the last successful run of this workflow
LAST_RUN_ID=$(gh run list --workflow "${{ github.workflow }}" --status success --limit 1 --json databaseId --jq '.[0].databaseId')
LAST_RUN_ID=$(gh run list -R "${{ github.repository }}" --workflow "${{ github.workflow }}" --status success --limit 1 --json databaseId --jq '.[0].databaseId')
if [ -n "$LAST_RUN_ID" ]; then
echo "Found previous successful run: $LAST_RUN_ID"
# Download brain memory to a temp dir so we can selectively restore only persistent state
mkdir -p .temp_brain_data
gh run download "$LAST_RUN_ID" -n brain-data -D .temp_brain_data || echo "brain-data not found"
gh run download "$LAST_RUN_ID" -R "${{ github.repository }}" -n brain-data -D .temp_brain_data || echo "brain-data not found"
# Restore only persistent memory files
cp .temp_brain_data/tools/gemini-cli-bot/lessons-learned.md tools/gemini-cli-bot/lessons-learned.md 2>/dev/null || true
mkdir -p tools/gemini-cli-bot/history/
cp .temp_brain_data/tools/gemini-cli-bot/history/*.csv tools/gemini-cli-bot/history/ 2>/dev/null || true
cp .temp_brain_data/tools/gemini-cli-bot/lessons-learned.md repo-target/tools/gemini-cli-bot/lessons-learned.md 2>/dev/null || true
mkdir -p repo-target/tools/gemini-cli-bot/history/
cp .temp_brain_data/tools/gemini-cli-bot/history/*.csv repo-target/tools/gemini-cli-bot/history/ 2>/dev/null || true
rm -rf .temp_brain_data
else
echo "No previous successful run found."
@@ -115,25 +130,62 @@ jobs:
- name: 'Collect Current Metrics'
env:
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
working-directory: agent-code
run: 'npx tsx tools/gemini-cli-bot/metrics/index.ts'
- name: 'Run Brain Phases'
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
GEMINI_MODEL: 'gemini-3-flash-preview'
GEMINI_CLI_HOME: 'tools/gemini-cli-bot'
ENABLE_PRS: "${{ github.event.inputs.enable_prs || 'false' }}"
GEMINI_CLI_HOME: '../agent-code/tools/gemini-cli-bot'
ENABLE_PRS: "${{ github.event.inputs.enable_prs || 'true' }}"
TRIGGER_ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
TRIGGER_COMMENT_ID: '${{ github.event.comment.id || github.event.inputs.comment_id }}'
# Enable detailed activity logging for debugging
GEMINI_TELEMETRY_ENABLED: 'true'
GEMINI_TELEMETRY_LOG_PROMPTS: 'true'
GEMINI_TELEMETRY_OUTFILE: 'brain-telemetry.json'
GEMINI_DEBUG_LOG_FILE: 'brain-debug.log'
GH_PAGER: ''
working-directory: repo-target
run: |
PROMPT_PATH="tools/gemini-cli-bot/brain/scheduled.md"
if [ "${{ github.event_name }}" = "issue_comment" ] || [ "${{ github.event.inputs.run_interactive }}" = "true" ]; then
PROMPT_PATH="tools/gemini-cli-bot/brain/interactive.md"
export ENABLE_PRS="true"
# Determine intent and prompt
MANDATE_INPUT="${{ github.event.inputs.mandate || 'auto' }}"
# Initialize defaults
PROMPT_FILE="../agent-code/tools/gemini-cli-bot/brain/scheduled.md"
MANDATE="Your specific mandate for this run: Implement surgical fixes for repository issues (issue-fixer skill)."
# Resolve Mandate and Prompt File
if [ "$MANDATE_INPUT" = "issue-fixer" ]; then
echo "Trigger: Manual Override (issue-fixer)"
MANDATE="Your specific mandate for this run: Implement surgical fixes for repository issues (issue-fixer skill)."
elif [ "$MANDATE_INPUT" = "metrics" ]; then
echo "Trigger: Manual Override (metrics)"
MANDATE="Your specific mandate for this run: Analyze repository metrics to identify bottlenecks and self-evolve (metrics skill)."
elif [ "$MANDATE_INPUT" = "interactive" ]; then
echo "Trigger: Manual Override (interactive)"
PROMPT_FILE="../agent-code/tools/gemini-cli-bot/brain/interactive.md"
MANDATE="Your specific mandate for this run: Respond to the user request in <untrusted_context>."
elif [ "${{ github.event_name }}" = "issue_comment" ] || [ "${{ github.event.inputs.run_interactive }}" = "true" ]; then
echo "Trigger: Issue/PR Comment or Interactive Dispatch"
PROMPT_FILE="../agent-code/tools/gemini-cli-bot/brain/interactive.md"
MANDATE="Your specific mandate for this run: Respond to the user request in <untrusted_context>."
elif [ "${{ github.event.schedule }}" = "0 0 * * *" ]; then
echo "Trigger: Nightly Schedule (Metrics)"
MANDATE="Your specific mandate for this run: Analyze repository metrics to identify bottlenecks and self-evolve (metrics skill)."
else
echo "Trigger: Scheduled or Manual Dispatch (Default: Issue-Fixer)"
fi
echo "Selected Prompt: $PROMPT_FILE"
echo "Selected Mandate: $MANDATE"
# Prepare Context if available
touch trigger_context.md
if [ -n "$TRIGGER_ISSUE_NUMBER" ]; then
echo "<untrusted_context>" > trigger_context.md
@@ -151,16 +203,18 @@ jobs:
echo "</untrusted_context>" >> trigger_context.md
fi
if [ "$ENABLE_PRS" = "true" ]; then
echo "**System Directive**: PR creation is ENABLED for this run. You MUST activate the **'prs' skill** to stage your changes and generate a \`pr-description.md\` file if you are proposing fixes." >> trigger_context.md
echo "**CRITICAL System Directive**: You MUST ONLY propose and implement a **SINGLE** improvement or fix per run. Bundling unrelated changes (e.g., a documentation update and a script fix, or a metrics update and a logic fix) into a single PR is STRICTLY FORBIDDEN and will result in immediate rejection during the critique phase. If you identify multiple issues, pick the most impactful one and ignore the others for now." >> trigger_context.md
else
echo "**System Directive**: PR creation is DISABLED for this run. You MUST NOT stage files or attempt to create a PR description." >> trigger_context.md
# Pass PR Enablement Directive
PR_DIRECTIVE="PR creation is DISABLED. You MUST NOT stage files."
if [ "${{ github.event.inputs.enable_prs || 'true' }}" = "true" ] || [ "${{ github.event_name }}" = "issue_comment" ]; then
PR_DIRECTIVE="PR creation is ENABLED. You MUST activate the 'prs' skill to stage changes if proposing fixes."
fi
echo "" >> trigger_context.md
cat trigger_context.md "$PROMPT_PATH" > combined_prompt.md
node bundle/gemini.js --policy tools/gemini-cli-bot/ci-policy.toml --prompt="$(cat combined_prompt.md)"
# Assemble final prompt: Context + Base Brain + Specific Mandate
echo "System: $PR_DIRECTIVE" > combined_prompt.md
cat trigger_context.md "$PROMPT_FILE" >> combined_prompt.md
echo -e "\n\n# MANDATE FOR THIS RUN\n$MANDATE" >> combined_prompt.md
node ../agent-code/bundle/gemini.js --policy ../agent-code/tools/gemini-cli-bot/ci-policy.toml --prompt="$(cat combined_prompt.md)"
if [ -n "$TRIGGER_ISSUE_NUMBER" ] && [ ! -s "issue-comment.md" ] && [ ! -s "pr-comment.md" ]; then
echo "Agent failed to respond. Generating fallback error message."
@@ -171,17 +225,20 @@ jobs:
- name: 'Run Critique Phase'
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event.inputs.run_interactive == 'true' }}"
working-directory: repo-target
env:
GEMINI_API_KEY: '${{ secrets.GEMINI_API_KEY }}'
GH_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
GEMINI_MODEL: 'gemini-3-flash-preview'
GEMINI_CLI_HOME: 'tools/gemini-cli-bot'
GEMINI_CLI_HOME: '../agent-code/tools/gemini-cli-bot'
GH_PAGER: ''
run: |
if git diff --staged --quiet; then
echo "No changes staged. Skipping critique."
echo "[APPROVED]" > critique_result.txt
else
node bundle/gemini.js --policy tools/gemini-cli-bot/ci-policy.toml --prompt="$(cat tools/gemini-cli-bot/.gemini/skills/critique/SKILL.md)" 2>&1 | tee critique_output.log
node ../agent-code/bundle/gemini.js --policy ../agent-code/tools/gemini-cli-bot/ci-policy.toml --prompt="$(cat ../agent-code/tools/gemini-cli-bot/.gemini/skills/critique/SKILL.md)" 2>&1 | tee critique_output.log
if [ "${PIPESTATUS[0]}" -eq 0 ] && grep -q "\[APPROVED\]" critique_output.log && ! grep -q "\[REJECTED\]" critique_output.log; then
echo "[APPROVED]" > critique_result.txt
@@ -193,28 +250,42 @@ jobs:
- name: 'Generate Patch'
if: "${{ github.event.inputs.enable_prs == 'true' || github.event_name == 'issue_comment' || github.event.inputs.run_interactive == 'true' }}"
working-directory: repo-target
run: |
touch bot-changes.patch
touch pr-description.md
touch pr-labels.txt
touch branch-name.txt
touch issue-comment.md
touch pr-comment.md
if [ -f critique_result.txt ] && grep -q "\[APPROVED\]" critique_result.txt && ! grep -q "\[REJECTED\]" critique_result.txt; then
git diff --staged > bot-changes.patch
else
echo "Critique did not approve. Skipping patch generation."
fi
- name: 'Stage Artifacts'
if: always()
run: |
mkdir -p staged-artifacts/tools/gemini-cli-bot/history
cp repo-target/tools/gemini-cli-bot/lessons-learned.md staged-artifacts/tools/gemini-cli-bot/ 2>/dev/null || true
cp repo-target/tools/gemini-cli-bot/history/*.csv staged-artifacts/tools/gemini-cli-bot/history/ 2>/dev/null || true
cp repo-target/brain-telemetry.json staged-artifacts/ 2>/dev/null || true
cp repo-target/brain-debug.log staged-artifacts/ 2>/dev/null || true
cp repo-target/bot-changes.patch staged-artifacts/ 2>/dev/null || true
cp repo-target/pr-description.md staged-artifacts/ 2>/dev/null || true
cp repo-target/branch-name.txt staged-artifacts/ 2>/dev/null || true
cp repo-target/pr-comment.md staged-artifacts/ 2>/dev/null || true
cp repo-target/pr-number.txt staged-artifacts/ 2>/dev/null || true
cp repo-target/issue-comment.md staged-artifacts/ 2>/dev/null || true
cp repo-target/pr-labels.txt staged-artifacts/ 2>/dev/null || true
- name: 'Archive Brain Data'
if: always()
uses: 'actions/upload-artifact@ea165f8d65b6e75b540449e92b4886f43607fa02' # ratchet:actions/upload-artifact@v4
with:
name: 'brain-data'
path: |
tools/gemini-cli-bot/lessons-learned.md
tools/gemini-cli-bot/history/*.csv
bot-changes.patch
pr-description.md
branch-name.txt
pr-comment.md
pr-number.txt
issue-comment.md
path: staged-artifacts/
retention-days: 90
publish:
@@ -241,25 +312,10 @@ jobs:
permission-pull-requests: 'write'
permission-issues: 'write'
- name: 'Determine Checkout Ref'
id: 'determine_ref'
env:
GITHUB_TOKEN: '${{ secrets.GITHUB_TOKEN }}'
ISSUE_NUMBER: '${{ github.event.issue.number || github.event.inputs.issue_number }}'
run: |
REF="main"
if [ -n "$ISSUE_NUMBER" ]; then
PR_HEAD=$(gh pr view "$ISSUE_NUMBER" --repo "${{ github.repository }}" --json headRefName --jq .headRefName 2>/dev/null || echo "")
if [ -n "$PR_HEAD" ]; then
REF="$PR_HEAD"
fi
fi
echo "ref=$REF" >> "$GITHUB_OUTPUT"
- name: 'Checkout'
uses: 'actions/checkout@08c6903cd8c0fde910a37f88322edcfb5dd907a8' # ratchet:actions/checkout@v5
with:
ref: '${{ steps.determine_ref.outputs.ref }}'
ref: '${{ needs.reasoning.outputs.sha }}'
fetch-depth: 0
persist-credentials: false
@@ -286,23 +342,20 @@ jobs:
fi
if [[ ! "$BRANCH_NAME" =~ ^bot/ ]]; then
echo "Error: Branch name '$BRANCH_NAME' does not start with 'bot/'. Safety abort."
exit 1
echo "Warning: Branch name '$BRANCH_NAME' does not start with 'bot/'. Prepending 'bot/' for safety."
BRANCH_NAME="bot/$BRANCH_NAME"
fi
git checkout -B "$BRANCH_NAME"
git apply "${{ runner.temp }}/brain-data/bot-changes.patch"
git apply --3way --ignore-whitespace "${{ runner.temp }}/brain-data/bot-changes.patch"
git add .
PR_TITLE="🤖 Gemini Bot Maintenance Update"
if [ -s "${{ runner.temp }}/brain-data/pr-description.md" ]; then
git commit -F "${{ runner.temp }}/brain-data/pr-description.md"
else
git commit -m "🤖 Gemini Bot Productivity Optimizations"
fi
PR_TITLE="🤖 Gemini Bot Productivity Optimizations"
if [ -s "${{ runner.temp }}/brain-data/pr-description.md" ]; then
PR_TITLE=$(head -n 1 "${{ runner.temp }}/brain-data/pr-description.md")
else
git commit -m "$PR_TITLE"
fi
if ! git push origin "$BRANCH_NAME" --force; then
@@ -325,6 +378,14 @@ jobs:
gh pr create --draft --title "🤖 Gemini Bot Productivity Optimizations" --body "Automated changes generated by Gemini CLI Bot." --head "$NEW_BRANCH_NAME" --base main
fi
fi
if [ -s "${{ runner.temp }}/brain-data/pr-labels.txt" ]; then
while IFS= read -r label; do
if [ -n "$label" ]; then
gh pr edit "$BRANCH_NAME" --add-label "$label" || true
fi
done < "${{ runner.temp }}/brain-data/pr-labels.txt"
fi
fi
- name: 'Post PR/Issue Comment'
+1
View File
@@ -18,6 +18,7 @@ jobs:
pull-requests: 'write'
steps:
- name: 'Limit open pull requests per user'
if: "github.actor != 'gemini-cli[bot]' && github.actor != 'gemini-cli-robot'"
uses: 'Homebrew/actions/limit-pull-requests@9ceb7934560eb61d131dde205a6c2d77b2e1529d' # master
with:
except-author-associations: 'MEMBER,OWNER,COLLABORATOR'
@@ -17,6 +17,7 @@ import {
handleAtCommand,
escapeAtSymbols,
unescapeLiteralAt,
checkPermissions,
} from './atCommandProcessor.js';
import {
FileDiscoveryService,
@@ -1539,4 +1540,23 @@ describe('unescapeLiteralAt', () => {
const input = 'user@example.com and @scope/pkg';
expect(unescapeLiteralAt(escapeAtSymbols(input))).toBe(input);
});
describe('checkPermissions', () => {
it('should handle ENAMETOOLONG gracefully in checkPermissions', async () => {
const longPath = 'a'.repeat(5000);
const query = `@${longPath}`;
const localMockConfig = {
getTargetDir: () => '.',
validatePathAccess: () => true,
getResourceRegistry: () => ({
findResourceByUri: () => undefined,
}),
} as unknown as Config;
// checkPermissions should not throw ENAMETOOLONG
const permissions = await checkPermissions(query, localMockConfig);
expect(permissions).toEqual([]);
});
});
});
@@ -188,9 +188,15 @@ export async function checkPermissions(
const pathName = part.content.substring(1);
if (!pathName) continue;
const resolvedPathName = resolveToRealPath(
path.resolve(config.getTargetDir(), pathName),
);
let resolvedPathName: string;
try {
resolvedPathName = resolveToRealPath(
path.resolve(config.getTargetDir(), pathName),
);
} catch {
// If path resolution fails (e.g. ENAMETOOLONG), skip this path
continue;
}
if (config.validatePathAccess(resolvedPathName, 'read')) {
if (await fileExists(resolvedPathName)) {
+38
View File
@@ -134,6 +134,44 @@ describe('Turn', () => {
expect(turn.getDebugResponses().length).toBe(2);
});
it('should not duplicate text in getResponseText when both chunks and consolidated response are present', async () => {
const mockResponseStream = (async function* () {
// Chunk 1
yield {
type: StreamEventType.CHUNK,
value: {
candidates: [{ content: { parts: [{ text: 'Hello' }] } }],
} as GenerateContentResponse,
};
// Chunk 2
yield {
type: StreamEventType.CHUNK,
value: {
candidates: [{ content: { parts: [{ text: ' world' }] } }],
} as GenerateContentResponse,
};
// Final consolidated response (as yielded by GeminiChat)
yield {
type: StreamEventType.CHUNK,
value: {
candidates: [{
content: { parts: [{ text: 'Hello world' }] },
finishReason: 'STOP'
}],
} as GenerateContentResponse,
};
})();
mockSendMessageStream.mockResolvedValue(mockResponseStream);
for await (const _ of turn.run({ model: 'm' }, [{ text: 'req' }], new AbortController().signal)) {
// consume stream
}
const text = turn.getResponseText();
// CURRENT BUGGY BEHAVIOR: "Hello world Hello world"
expect(text).toBe('Hello world');
});
it('should yield tool_call_request events for function calls', async () => {
const mockResponseStream = (async function* () {
yield {
+15 -4
View File
@@ -472,10 +472,21 @@ export class Turn {
*/
getResponseText(): string {
if (this.cachedResponseText === undefined) {
this.cachedResponseText = this.debugResponses
.map((response) => getResponseText(response))
.filter((text): text is string => text !== null)
.join(' ');
let result = '';
for (const response of this.debugResponses) {
const text = getResponseText(response);
if (text) {
// Heuristic to handle both delta and cumulative responses:
// If the new text starts with our current result, it's likely a cumulative
// update (or a redundant final consolidated response).
if (result && text.startsWith(result)) {
result = text;
} else {
result += text;
}
}
}
this.cachedResponseText = result;
}
return this.cachedResponseText;
}
@@ -230,48 +230,6 @@ describe('sanitizeEnvironment', () => {
});
});
describe('value-first security: secret values must be caught even for allowed variable names', () => {
it('should redact ALWAYS_ALLOWED variables whose values contain a GitHub token', () => {
const env = {
HOME: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
PATH: '/usr/bin',
};
const sanitized = sanitizeEnvironment(env, EMPTY_OPTIONS);
expect(sanitized).toEqual({ PATH: '/usr/bin' });
});
it('should redact ALWAYS_ALLOWED variables whose values contain a certificate', () => {
const env = {
SHELL:
'-----BEGIN RSA PRIVATE KEY-----\nMIIE...\n-----END RSA PRIVATE KEY-----',
USER: 'alice',
};
const sanitized = sanitizeEnvironment(env, EMPTY_OPTIONS);
expect(sanitized).toEqual({ USER: 'alice' });
});
it('should redact user-allowlisted variables whose values contain a secret', () => {
const env = {
MY_SAFE_VAR: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
OTHER: 'fine',
};
const sanitized = sanitizeEnvironment(env, {
allowedEnvironmentVariables: ['MY_SAFE_VAR'],
blockedEnvironmentVariables: [],
enableEnvironmentVariableRedaction: true,
});
expect(sanitized).toEqual({ OTHER: 'fine' });
});
it('should NOT redact GEMINI_CLI_ variables even if their value looks like a secret (fully trusted)', () => {
const env = {
GEMINI_CLI_INTERNAL: 'ghp_xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx',
};
const sanitized = sanitizeEnvironment(env, EMPTY_OPTIONS);
expect(sanitized).toEqual(env);
});
});
it('should ensure all names in the sets are capitalized', () => {
for (const name of ALWAYS_ALLOWED_ENVIRONMENT_VARIABLES) {
expect(name).toBe(name.toUpperCase());
@@ -412,15 +370,16 @@ describe('getSecureSanitizationConfig', () => {
);
});
it('should filter out variables from allowed list that match NEVER_ALLOWED_NAME_PATTERNS', () => {
it('should not filter out variables from allowed list that match NEVER_ALLOWED_NAME_PATTERNS', () => {
const requestedConfig = {
allowedEnvironmentVariables: ['SAFE_VAR', 'MY_SECRET_TOKEN'],
allowedEnvironmentVariables: ['SAFE_VAR', 'MY_SECRET_TOKEN', 'GH_TOKEN'],
};
const config = getSecureSanitizationConfig(requestedConfig);
expect(config.allowedEnvironmentVariables).toContain('SAFE_VAR');
expect(config.allowedEnvironmentVariables).not.toContain('MY_SECRET_TOKEN');
expect(config.allowedEnvironmentVariables).toContain('MY_SECRET_TOKEN');
expect(config.allowedEnvironmentVariables).toContain('GH_TOKEN');
});
it('should deduplicate variables in allowed and blocked lists', () => {
@@ -14,9 +14,11 @@ export function sanitizeEnvironment(
processEnv: NodeJS.ProcessEnv,
config: EnvironmentSanitizationConfig,
): NodeJS.ProcessEnv {
// Enable strict sanitization in GitHub actions.
const isStrictSanitization =
!!processEnv['GITHUB_SHA'] || processEnv['SURFACE'] === 'Github';
// Always sanitize when in GitHub actions.
if (!config.enableEnvironmentVariableRedaction && !isStrictSanitization) {
return { ...processEnv };
}
@@ -150,22 +152,7 @@ function shouldRedactEnvironmentVariable(
key = key.toUpperCase();
value = value?.toUpperCase();
if (key.startsWith('GEMINI_CLI_')) {
return false;
}
if (value) {
for (const pattern of NEVER_ALLOWED_VALUE_PATTERNS) {
if (pattern.test(value)) {
return true;
}
}
}
if (key.startsWith('GIT_CONFIG_')) {
return false;
}
// User overrides take precedence.
if (allowedSet?.has(key)) {
return false;
}
@@ -173,14 +160,20 @@ function shouldRedactEnvironmentVariable(
return true;
}
if (ALWAYS_ALLOWED_ENVIRONMENT_VARIABLES.has(key)) {
// These are never redacted.
if (
ALWAYS_ALLOWED_ENVIRONMENT_VARIABLES.has(key) ||
key.startsWith('GEMINI_CLI_')
) {
return false;
}
// These are always redacted.
if (NEVER_ALLOWED_ENVIRONMENT_VARIABLES.has(key)) {
return true;
}
// If in strict mode (e.g. GitHub Action), and not explicitly allowed, redact it.
if (isStrictSanitization) {
return true;
}
@@ -191,6 +184,15 @@ function shouldRedactEnvironmentVariable(
}
}
// Redact if the value looks like a key/cert.
if (value) {
for (const pattern of NEVER_ALLOWED_VALUE_PATTERNS) {
if (pattern.test(value)) {
return true;
}
}
}
return false;
}
@@ -212,12 +214,6 @@ export function getSecureSanitizationConfig(
if (NEVER_ALLOWED_ENVIRONMENT_VARIABLES.has(upperKey)) {
return false;
}
// Never allow variables that match sensitive name patterns
for (const pattern of NEVER_ALLOWED_NAME_PATTERNS) {
if (pattern.test(upperKey)) {
return false;
}
}
return true;
});
+13
View File
@@ -602,6 +602,19 @@ describe('resolveToRealPath', () => {
/Infinite recursion detected/,
);
});
it('should handle ENAMETOOLONG gracefully', () => {
const longPath = path.resolve('/' + 'a'.repeat(5000));
vi.spyOn(fs, 'realpathSync').mockImplementation(() => {
const err = new Error('ENAMETOOLONG') as NodeJS.ErrnoException;
err.code = 'ENAMETOOLONG';
throw err;
});
// Should return the path itself if realpathSync fails with ENAMETOOLONG
expect(resolveToRealPath(longPath)).toBe(longPath);
});
});
describe('makeRelative', () => {
+8 -2
View File
@@ -440,7 +440,10 @@ function robustRealpath(p: string, visited = new Set<string>()): string {
e &&
typeof e === 'object' &&
'code' in e &&
(e.code === 'ENOENT' || e.code === 'EISDIR')
(e.code === 'ENOENT' ||
e.code === 'EISDIR' ||
e.code === 'ENAMETOOLONG' ||
e.code === 'EINVAL')
) {
try {
const stat = fs.lstatSync(p);
@@ -457,7 +460,10 @@ function robustRealpath(p: string, visited = new Set<string>()): string {
lstatError &&
typeof lstatError === 'object' &&
'code' in lstatError &&
(lstatError.code === 'ENOENT' || lstatError.code === 'EISDIR')
(lstatError.code === 'ENOENT' ||
lstatError.code === 'EISDIR' ||
lstatError.code === 'ENAMETOOLONG' ||
lstatError.code === 'EINVAL')
)
) {
throw lstatError;
+1 -1
View File
@@ -2,7 +2,7 @@
"extends": "../../tsconfig.json",
"compilerOptions": {
"outDir": "dist",
"lib": ["DOM", "DOM.Iterable", "ES2021"],
"lib": ["DOM", "DOM.Iterable", "ES2023"],
"composite": true,
"types": ["node"]
},
+1 -1
View File
@@ -3,7 +3,7 @@
"module": "NodeNext",
"moduleResolution": "NodeNext",
"target": "ES2022",
"lib": ["ES2022", "dom"],
"lib": ["ES2023", "dom"],
"sourceMap": true,
/*
* skipLibCheck is necessary because the a2a-server package depends on
@@ -0,0 +1,7 @@
{
"security": {
"environmentVariableRedaction": {
"allowed": ["GH_TOKEN", "GITHUB_TOKEN"]
}
}
}
@@ -114,8 +114,12 @@ above:
4. **Strict Scope Constraint**: You are STRICTLY FORBIDDEN from modifying or
staging any file that was not already staged by the investigation phase. You
must ONLY critique and fix the files explicitly included in
`git diff --staged`. Do not attempt to complete pending tasks from the
memory ledger or introduce unrelated refactoring to unstaged files.
`git diff --staged`. Furthermore, within those staged files, you must ONLY
fix the specific logical or technical flaw you identified. You are
STRICTLY FORBIDDEN from performing unrelated refactoring, changing type
signatures, or modifying code outside the immediate scope of the required fix.
Do not attempt to complete pending tasks from the memory ledger or introduce
unrelated "cleanups".
5. Re-stage the file with `git add`. **CRITICAL: You MUST use `git add` to
stage your fixes.**
@@ -0,0 +1,35 @@
---
name: issue-fixer
description: Proactively identify and implement surgical fixes for small-effort issues and maintain existing PRs to reduce repository backlog.
---
# Skill: Issue Fixer
## Goal
Proactively identify and implement surgical fixes for "small effort" issues and
maintain existing PRs to reduce the repository backlog.
## High-Level Expectations
1. **Maintenance**: Prioritize driving existing `bot-fix` PRs to completion. Check for CI failures, merge conflicts, or requested changes.
2. **Discovery**: Find open issues labeled `effort/small`. Prioritize those with clear reproduction steps. You are STRICTLY FORBIDDEN from using local commands (e.g., `npm run lint`, `npm run typecheck`, or grepping for TODOs) to find new work. You are also STRICTLY FORBIDDEN from attempting to fix issues with the internal metrics scripts or other repository tooling on your own; your ONLY source of new tasks is the GitHub issue tracker.
3. **Autonomous Implementation**: You are responsible for the entire fix: research, code changes, and test verification.
4. **Surgical Precision**: Changes must be minimal and strictly focused on the identified issue. Avoid "drive-by" refactoring.
5. **Local Verification (MANDATORY TIMEOUTS)**: You MUST run `timeout 10m npm run preflight` (or `timeout 5m npm test ...`) locally and iterate on any failures before finalizing your PR. Wrapping test commands in `timeout` is **MANDATORY** to prevent hanging the CI environment if your changes introduce an infinite loop.
6. **Expert Mentions**: Identify the domain expert for the affected files and CC them in the PR description.
7. **Focused Contributions**: Limit your active PRs to ~10 at a time. Try to complete existing PRs before opening new ones. If a maintainer closes a PR, that may be an indication that they are rejecting the fix.
## Workflow
1. **Inventory & Drive PRs**:
- **ACTIVATE SKILLS**: You MUST call `activate_skill(name="memory")` and `activate_skill(name="prs")` before continuing.
- Use the `prs` skill to list all open PRs labeled `bot-fix`.
- If any require attention (CI failure, requested changes), focus your entire run on resolving ONE of them.
- Do NOT start a new issue fix if an existing PR needs work.
2. **Search for Candidates**: If no PRs need attention, search for `effort/small` issues: `gh issue list --label "effort/small" --limit 10 --json number,title,url`.
- **CRITICAL**: `gh issue list` is your ONLY source for new tasks. Do not attempt to fix issues you discover independently in the codebase (such as broken metrics scripts) unless they correspond to a specific assigned issue from the tracker. If `gh` fails, you MUST diagnose the environment or abort the discovery phase. You are STRICTLY FORBIDDEN from using `google_web_search` to query GitHub, as it indexes closed issues. Do NOT run local discovery commands (e.g., `npm run lint`, `npm run typecheck`) to look for "easy fixes".
3. **Select ONE Issue** and implement a fix on a new branch.
- **Efficient Searching**: When searching the codebase with `grep_search`, you MUST search one top-level folder at a time (e.g. `packages/core`, `packages/cli`) to avoid timeouts. Avoid searching problematic directories with large data files like `memory-tests` and `last_brain_data` unless absolutely necessary.
4. **Verify**: You MUST run `timeout 10m npm run preflight` to verify. Wrapping test commands in `timeout` is mandatory to prevent hanging the CI environment if your changes introduce an infinite loop.
5. **Use the `prs` Skill** to stage changes and prepare the draft PR (labels: `bot-fix`, `issue-fixer`). Ensure you write `pr-description.md` and `pr-labels.txt` to the **workspace root**.
@@ -20,7 +20,13 @@ maintainability.
- Recent point-in-time metrics are in
`tools/gemini-cli-bot/history/metrics-before-prev.csv` and the current run's
metrics.
- **Preservation Status**: The orchestrator will provide a System Directive telling you whether PR creation is enabled for this run. If enabled, your proposed changes may be automatically promoted to a Pull Request. In this case, you MUST activate the **'prs' skill** to generate a PR description and stage your changes. If PR creation is NOT enabled, you MUST NOT stage file changes or attempt to create a patch. Instead, simply report your findings.
## Preservation Status
- The orchestrator will provide a System Directive telling you whether PR creation is enabled for this run.
- **ACTIVATE SKILLS**: You MUST call `activate_skill(name="memory")` at the start of your investigation.
- **PR Skill**: If PR creation is enabled, you MUST call `activate_skill(name="prs")` to generate a PR description and stage your changes. You MUST write `pr-description.md` and `pr-labels.txt` to the **workspace root**.
- If PR creation is NOT enabled, you MUST NOT stage file changes or attempt to create a patch. Instead, simply report your findings.
- **Ownership Mandate**: Any PR generated by this skill MUST use the `bot-fix` and `metrics` labels in `pr-labels.txt`.
## Repo Policy Priorities
@@ -10,6 +10,18 @@ description: Expertise in managing the Git and GitHub Pull Request lifecycle, in
Standardize how the Gemini CLI Bot stages its changes, generates Pull Request
descriptions, and manages the lifecycle of both new and existing PRs.
## Mandatory PR Driver (Ownership)
You are the "owner" of all PRs labeled `bot-fix`. You MUST proactively drive them toward a resolution (either completion or informed escalation):
1. **Inventory**: Use `gh pr list --label "bot-fix" --json number,title,headRefName,statusCheckRollup,comments` to find your active PRs.
2. **Resolution Priority**:
- **Fixable CI/Feedback**: If a `bot-fix` PR has failing status checks or new maintainer comments, you MUST prioritize fixing the CI or responding to the feedback before starting new work.
- **Unresolvable Roadblocks**: If you identify a persistent failure that appears to be an environment issue, a flaky test, or a fundamental architectural blocker that requires human intervention, you MUST NOT keep looping. Instead:
1. Summarize the blocker and your failed attempts in `pr-comment.md`.
2. Explicitly ask for maintainer help.
3. Record the learning in `lessons-learned.md`.
4. Move on to a different task.
## Staging & Patch Preparation (MANDATORY)
If you are proposing fixes and PR creation is enabled (per the System Directive):
@@ -21,17 +33,21 @@ If you are proposing fixes and PR creation is enabled (per the System Directive)
unrelated refactor, or a metrics script update. Metrics and fixes MUST
be in separate PRs.
2. **Generate PR Description**: Use the `write_file` tool to create
`pr-description.md`.
`pr-description.md` **at the workspace root**.
- **Title**: The very first line MUST be a concise, conventional title.
- **Body**: The rest should be the markdown body explaining the change, why
it is recommended, and the expected impact.
3. **Stage Fixes**: You MUST explicitly stage your fixes using the
- **Body**: Explain the change and expected impact. You MUST identify the domain expert for the affected files and mention them (cc @<user>).
- **Labels**: Use the `write_file` tool to create `pr-labels.txt` **at the workspace root** containing one label per line. You MUST ALWAYS add the `bot-fix` label.
3. **Branch Naming (Optional)**: If you wish to specify a custom branch name,
use `write_file` to create `branch-name.txt` **at the workspace root**. **CRITICAL**: The branch name
MUST start with the `bot/` prefix. If you do not specify a branch name, one
will be generated for you.
4. **Stage Fixes**: You MUST explicitly stage your fixes using the
`git add <files>` command.
4. **Internal File Protection (CRITICAL)**: You are STRICTLY FORBIDDEN from
5. **Internal File Protection (CRITICAL)**: You are STRICTLY FORBIDDEN from
staging internal bot management files. If they are accidentally staged, you
MUST unstage them using `git reset <file>`.
- **NEVER STAGE**: `pr-description.md`, `lessons-learned.md`,
`branch-name.txt`, `pr-comment.md`, `pr-number.txt`, `issue-comment.md`, or
`branch-name.txt`, `pr-comment.md`, `pr-number.txt`, `issue-comment.md`, `pr-labels.txt`, or
anything in `history/`.
## Unblocking & PR Updates (Recovery)
@@ -40,12 +56,14 @@ If you are continuing work on an existing Task or responding to a comment on an
existing bot PR:
1. **Target Existing Branch**: Use `write_file` to generate `branch-name.txt`
containing the current branch name (e.g., `bot/task-BT-01`).
2. **Track PR ID**: Use `write_file` to generate `pr-number.txt` containing the
**at the workspace root** containing the current branch name. **CRITICAL**: The branch name MUST start
with the `bot/` prefix (e.g., `bot/task-BT-01`). If it does not, your PR
creation will be rejected by the safety gate.
2. **Track PR ID**: Use `write_file` to generate `pr-number.txt` **at the workspace root** containing the
numeric PR ID.
3. **Respond to Maintainers**:
- For general responses, write your markdown comment to `issue-comment.md`.
- For specific PR feedback, write your markdown response to `pr-comment.md`.
4. **Handle CI Failures**: Diagnose failing checks using `gh run view`. Your
- For general responses, write your markdown comment to `issue-comment.md` **at the workspace root**.
- For specific PR feedback, write your markdown response to `pr-comment.md` **at the workspace root**.
4. **Handle CI Failures**: Diagnose failing checks using `gh --no-pager run view` or `gh api`. Your
priority must be generating a new patch and staging it with `git add` to fix
the failure.
+5 -4
View File
@@ -64,8 +64,8 @@ Root-Cause' workflow** to the **'worker' agent**:
If investigation confirms a change is required:
- **Activate PR Skill**: You MUST activate the **'prs' skill** to manage
staging, PR descriptions, and branch targeting.
- **ACTIVATE PR SKILL FIRST**: You MUST activate the **'prs' skill** BEFORE
staging any changes or generating PR data.
- **One Thing at a Time**: You MUST ONLY propose and implement a **single fix or
improvement per run**.
- **Surgical Changes**: Apply the minimal set of changes needed to address the
@@ -74,7 +74,7 @@ If investigation confirms a change is required:
user's specific request. You are STRICTLY FORBIDDEN from including any
unrelated updates when operating in interactive mode.
- **Acknowledgment**: Use the `write_file` tool to write a brief acknowledgement
to `issue-comment.md`.
to `issue-comment.md` **at the workspace root**.
### 3. Question & Answer (Q&A)
@@ -83,7 +83,8 @@ If the user's request is purely informational:
- **Evidence-Based Answers**: Delegate the information gathering to the
**'worker' agent** to verify facts before answering.
- **Output**: You MUST use the `write_file` tool to save your response to
`issue-comment.md`. DO NOT simply output your response to the console.
`issue-comment.md` **at the workspace root**. DO NOT simply output your
response to the console.
## Execution Constraints
+57 -33
View File
@@ -1,19 +1,30 @@
# Phase: Scheduled Agent (Strategic Investigation & Optimization)
# Phase: Scheduled Agent
## 1. MANDATORY START: Activate Mandate Skill
Your **MANDATE FOR THIS RUN** (provided at the end of this prompt) explicitly
dictates your task for this session. It will ask you to use a specific skill
(e.g., `issue-fixer` or `metrics`).
**You MUST call the `activate_skill` tool as the VERY FIRST ACTION of your FIRST
TURN to load the instructions for your mandate.**
1. Identify the skill name from your **MANDATE FOR THIS RUN**.
2. Call `activate_skill(name="<skill-name>")`.
3. Follow the detailed workflow and instructions provided by the activated
skill. Do NOT perform any other actions until the skill is activated.
## Goal
Analyze repository health metrics, identify bottlenecks, and propose proactive
improvements to the repository's workflows and automation. You must maintain
high architectural standards, security rigor, and maintainer-focused
productivity.
Execute the task specified in your **MANDATE FOR THIS RUN**. Maintain high
architectural standards, security rigor, and maintainer-focused productivity.
## CRITICAL: ONE THING AT A TIME
You are STRICTLY FORBIDDEN from proposing or implementing more than one
improvement or fix per run. Bundling unrelated changes (e.g., a documentation
update and a script fix) into a single PR is a failure of your primary mandate.
You are specifically forbidden from combining metrics script updates and logic
fixes/improvements in the same PR. If you identify multiple opportunities:
If you identify multiple opportunities:
1. Select the **single most impactful** improvement.
2. Focus your entire investigation and implementation on ONLY that improvement.
@@ -50,43 +61,56 @@ You MUST use the following skills to manage persistent state and PRs:
## Instructions
### 1. Investigation & Triage (Mandatory Delegation)
### 1. Hypothesis Testing & Strategic Pivoting
You MUST delegate the **'metrics' workflow** to the **'worker' agent**:
1. Invoke the 'worker' agent and instruct it to use the **'metrics' skill**.
2. Pass the current date and the relevant portions of the Task Ledger (ensuring
all untrusted data is wrapped in <untrusted_context> tags) for grounding.
3. Use the worker's summarized results to identify trends, anomalies, and
opportunities for proactive improvement.
### 2. Hypothesis Testing & Deep Dive
For any detected bottlenecks or opportunities:
For any detected bugs, bottlenecks, or opportunities:
- Formulate competing hypotheses.
- Delegate data-intensive evidence gathering (e.g., slicing logs, batch issue
analysis - ensuring all untrusted data is wrapped in <untrusted_context> tags)
to the worker agent.
- Select the optimal path based on the empirical evidence returned. You MUST
ONLY execute on a **single path** to ensure the resulting PR is focused and
surgical.
- Delegate high-volume or data-intensive evidence gathering (e.g., slicing logs,
batch issue analysis) to the **'worker' agent** if necessary.
- **Iterative Refinement**: Select the most likely path first. However, if your
initial implementation fails verification (e.g. tests still fail), you MUST
explicitly pivot to your second hypothesis rather than infinitely patching the
first one.
- **Bail Out**: If all your formulated hypotheses fail to yield a verified fix,
abort the task and record the findings. Delivering NO change is better than
delivering a broken or "best-guess" fix.
## Execution Constraints
- **One Thing at a Time**: You MUST ONLY propose and implement a **single
improvement or fix per run**. If you identify multiple opportunities, select
the one with the highest impact and record the others in `lessons-learned.md`
for future runs.
improvement or fix per run**.
- **Surgical Changes**: Apply the minimal set of changes needed to address the
identified opportunity correctly and safely.
- **Strict Scope**: You are STRICTLY FORBIDDEN from bundling unrelated updates
into a single PR.
- **Mandatory Delegation**: You MUST delegate the following workflows to the
**'worker' agent**:
- Repository metrics collection and initial triage ('metrics' skill).
- High-volume data collection or log analysis.
- **Do NOT delegate to the 'generalist' agent.**
- **Delegation Guidelines**: Do NOT delegate to the 'generalist' agent. Delegate
data-intensive tasks (like repository metrics collection) to the 'worker'
agent.
- **Verification vs. Discovery**: Local commands (e.g. `npm run lint`,
`npm run typecheck`) are for VERIFYING fixes to explicitly assigned tasks
only. They must NEVER be used for unprompted "fishing expeditions" to find new
work.
- **Monorepo Build Order**: When verifying the workspace or diagnosing errors,
you MUST run `npm run build` BEFORE running `npm run typecheck`. In a clean
state, `tsc` will report widespread errors (TS6305) if the project's build
artifacts do not yet exist. These are environment issues, not code bugs.
- **Strict Read-Only Reasoning**: You cannot push code or post comments via API.
Your only way to effect change is by writing to specific files and explicitly
staging file changes using the `git add` command.
## Loop Prevention & Success Criteria
To ensure efficiency and prevent infinite reasoning loops:
1. **Monitor Your Progress**: If you have attempted the same sequence of
actions (e.g., Edit -> Test -> Fail) for the same problem more than **3
times**, you MUST stop and re-evaluate your fundamental hypothesis.
2. **Failure Threshold**: If you cannot find a verified solution after **2
distinct hypotheses** (max 6 total edit/test cycles), you MUST abort the
task.
3. **Reporting Failure**: If you abort, summarize the roadblocks you
encountered in `lessons-learned.md`. It is better to deliver NO changes than
to burn excessive tokens on a loop.
4. **Verification is Key**: A task is only "complete" when all relevant tests
pass. Never stage a change that you know still fails tests.
+9 -1
View File
@@ -2,15 +2,23 @@
# This policy guarantees permission for shell commands and file writing in the bot's CI environment.
[[rule]]
toolName = ["run_shell_command", "write_file", "replace"]
toolName = ["run_shell_command", "write_file", "replace", "activate_skill"]
decision = "allow"
# Max priority to ensure it overrides all default and workspace rules.
priority = 999
# Explicitly target the headless environment to match the specificity of default denial rules.
interactive = false
# Capture output without triggering a policy downgrade to ASK_USER.
allowRedirection = true
[[rule]]
toolName = "invoke_agent"
decision = "allow"
priority = 999
interactive = false
[[rule]]
toolName = "google_web_search"
decision = "deny"
priority = 999
interactive = false