Build: Better protection against agents editing the main working directory
CI / lint (push) Successful in 24s
CI / typecheck (push) Successful in 54s
CI / quality (push) Successful in 45s
CI / security (push) Successful in 1m15s
CI / build (push) Successful in 29s
CI / push-validation (push) Successful in 30s
CI / helm (push) Successful in 37s
CI / e2e_tests (push) Successful in 3m39s
CI / integration_tests (push) Successful in 4m28s
CI / unit_tests (push) Successful in 5m22s
CI / docker (push) Successful in 21s
CI / coverage (push) Successful in 11m39s
CI / status-check (push) Successful in 1s

This commit is contained in:
CleverAgents Build Agent
2026-04-13 23:36:34 -04:00
parent c11b05b773
commit 38bcd41338
27 changed files with 150 additions and 35 deletions
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2 temperature: 0.2
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.3 temperature: 0.3
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ temperature: 0.2
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
color: success color: success
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2 temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector) # NO MODEL SPECIFIED - inherits from caller (tier selector)
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2 temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector) # NO MODEL SPECIFIED - inherits from caller (tier selector)
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -7,7 +7,11 @@ hidden: true
temperature: 0.3 temperature: 0.3
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+6 -1
View File
@@ -7,7 +7,11 @@ temperature: 0.2
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
color: "#059669" color: "#059669"
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
@@ -61,6 +65,7 @@ You manually fix a specific pull request. The user tells you which PR to fix. Yo
4. Fix the code — address both CI failures and review feedback. 4. Fix the code — address both CI failures and review feedback.
5. Run quality gates locally (`nox -e lint`, `nox -e typecheck`, `nox -e unit_tests`, `nox -e integration_tests`). 5. Run quality gates locally (`nox -e lint`, `nox -e typecheck`, `nox -e unit_tests`, `nox -e integration_tests`).
6. Commit and push using `git-commit-helper`. 6. Commit and push using `git-commit-helper`.
7. Clean up the isolated clone using `repo-isolator`.
## Rules ## Rules
+5 -1
View File
@@ -9,7 +9,11 @@ hidden: true
temperature: 0.1 temperature: 0.1
# No model specified — tier is set by the supervisor via tier selectors # No model specified — tier is set by the supervisor via tier selectors
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2 temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector) # NO MODEL SPECIFIED - inherits from caller (tier selector)
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector) # NO MODEL SPECIFIED - inherits from caller (tier selector)
color: warning color: warning
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ temperature: 0.1
# NO MODEL SPECIFIED - inherits from caller (tier selector) # NO MODEL SPECIFIED - inherits from caller (tier selector)
color: warning color: warning
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+6 -2
View File
@@ -9,7 +9,11 @@ temperature: 0.1
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
color: warning color: warning
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
@@ -46,7 +50,7 @@ permission:
# PR CI Test Fixer # PR CI Test Fixer
You fix failing CI checks on a PR branch. You work in an isolated clone directory. You fix failing CI checks on a PR branch. You work in an isolated clone directory`$WORK_DIR` is always a path inside `/tmp/` created by `repo-isolator`. All file edits and every git operation (`add`, `commit`, `push`, branch switching) must be performed inside `$WORK_DIR`, never against `/app`.
## What You Do ## What You Do
+10 -4
View File
@@ -10,7 +10,11 @@ temperature: 0.1
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
color: "#059669" color: "#059669"
permission: permission:
edit: deny edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: deny webfetch: deny
bash: bash:
"*": deny "*": deny
@@ -99,6 +103,8 @@ Before merging any PR, the following must be true:
5. **No `needs feedback` label** — the PR is not waiting for human input 5. **No `needs feedback` label** — the PR is not waiting for human input
6. **Not blocked** — no `Blocked` label 6. **Not blocked** — no `Blocked` label
**Note:** The above criteria do not need to be satisfied before dispatching a `pr-merge-worker`. While they must be satisfied before the actual merge, the rebasing and conflict resolution steps are still useful even if it isnt ready to be merged.
## Workers ## Workers
You call `pr-merge-worker` as a **blocking subagent** (via the Task tool) for rebase operations with conflict resolution when PRs are behind the base branch. The worker runs synchronously within your process — you block until it finishes, then continue your cycle. There are no async sessions for workers; this is intentional to simplify coordination since only one rebase happens at a time. You call `pr-merge-worker` as a **blocking subagent** (via the Task tool) for rebase operations with conflict resolution when PRs are behind the base branch. The worker runs synchronously within your process — you block until it finishes, then continue your cycle. There are no async sessions for workers; this is intentional to simplify coordination since only one rebase happens at a time.
@@ -109,9 +115,9 @@ Poll every 5 minutes using `bash("sleep 300", timeout=360000)`.
Each cycle: Each cycle:
1. List all open PRs. (the forgejo task paginates so be sure to go through every page) 1. List all open PRs. (the forgejo task paginates so be sure to go through every page)
2. for each PR that is marked as mergable attempt to do a fast-forward or rebase merge, as always do the mandatory post-merge verification. 2. for each PR that can be merged (has a passing CI,, isn't stale, and has at least 1 approval review) do a fast-forward or rebase merge, as always do the mandatory post-merge verification.
2. For all other PR (including those you attempted to merge but were unsuccessful), filter such that only those that have passing CI quality gates/tests remain. 3. For all other PR (including those you attempted to merge but were unsuccessful) order them as follows: 1) CI passes and has one or more review approval 2) CI is failing without conflicts and has one or more review approval 3) CI is failing with conflicts and has one or more review approval 4) All other PRs, within each of those four categories sort by priority label with the most critical label ("Priority/CI Blocking") coming first.
4. For each PR that needs rebasing, regardless of if it has conflicts, (prioritized by: milestone order lowest first, then priority label, then MoSCoW label, then issue number), call `pr-merge-worker` as a **blocking subagent** via the Task tool. Pass the PR number, repository info, and credentials in the prompt. The worker will rebase onto the latest master, resolve conflicts, wait for CI, and attempt a fast-forward or rebase merge. You block until the worker finishes before processing the next PR. 4. For each PR, in the order specified in step 2 above, call `pr-merge-worker` as a **blocking subagent** via the Task tool. Pass the PR number, repository info, and credentials in the prompt. The worker will rebase onto the latest master, resolve conflicts, wait for CI, and attempt a fast-forward or rebase merge. You block until the worker finishes before processing the next PR.
5. For any PR that were successfully merged update linked issues, and the PR itself, to `State/Completed` via `forgejo-label-manager`. 5. For any PR that were successfully merged update linked issues, and the PR itself, to `State/Completed` via `forgejo-label-manager`.
## Tracking ## Tracking
+7 -3
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.1 temperature: 0.1
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
"external_directory":
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
@@ -46,7 +50,7 @@ You perform a single rebase operation on a PR branch, resolve any conflicts, and
## Procedure ## Procedure
Your prompt tells you which PR to rebase. You must: Your prompt tells you which PR to rebase. You must:
1. Create an isolated clone using the `repo-isolator` subagent ensuring you pass it the branch used by the PR. 1. Create an isolated clone using the `repo-isolator` subagent ensuring you pass it the branch used by the PR. Make sure all work is done within this clone's directory.
2. Rebase it onto the base branch (usually `master`). 2. Rebase it onto the base branch (usually `master`).
3. Resolve any conflicts that arise by reviewing the recent git history and using that to fix the conflicts 3. Resolve any conflicts that arise by reviewing the recent git history and using that to fix the conflicts
4. Force-push with lease using `git-commit-helper` 4. Force-push with lease using `git-commit-helper`
@@ -59,6 +63,6 @@ Your prompt tells you which PR to rebase. You must:
1. **One task, then exit.** Do not loop. Do not sleep. Do not look for more work. 1. **One task, then exit.** Do not loop. Do not sleep. Do not look for more work.
2. **Force-push with lease only.** Never use `--force` without `--lease`. 2. **Force-push with lease only.** Never use `--force` without `--lease`.
3. **Clean up your clone.** Delete the temporary directory before exiting. 3. **Clean up your clone.** Delete the temporary clone directory before exiting.
4. **Apply labels via `forgejo-label-manager`.** Never apply labels directly or using the Forgejo MCP/task. All label operations must go through `forgejo-label-manager`. 4. **Apply labels via `forgejo-label-manager`.** Never apply labels directly or using the Forgejo MCP/task. All label operations must go through `forgejo-label-manager`.
5. **Exhaustive pagination for all list results.** Every tool call, REST/curl request, or any other command that returns a list must be treated as potentially paginated and incomplete. Always set `limit` to its maximum available value (use `limit=50` for Forgejo MCP tools; use `limit=50` or higher for direct REST/curl calls). After each list response, check whether the number of returned items equals the page size — if so, there are likely more results; fetch the next page (`page=2`, `page=3`, …) and continue until receiving a partial page. Never assume the first response is the complete result. This rule applies to every list-returning call without exception. *Examples specific to this agent (not exhaustive):* `git log` listing commits during conflict resolution must be fully read; any future REST/curl calls returning JSON arrays must be paginated. 5. **Exhaustive pagination for all list results.** Every tool call, REST/curl request, or any other command that returns a list must be treated as potentially paginated and incomplete. Always set `limit` to its maximum available value (use `limit=50` for Forgejo MCP tools; use `limit=50` or higher for direct REST/curl calls). After each list response, check whether the number of returned items equals the page size — if so, there are likely more results; fetch the next page (`page=2`, `page=3`, …) and continue until receiving a partial page. Never assume the first response is the complete result. This rule applies to every list-returning call without exception. *Examples specific to this agent (not exhaustive):* `git log` listing commits during conflict resolution must be fully read; any future REST/curl calls returning JSON arrays must be paginated.
+10 -3
View File
@@ -9,7 +9,11 @@ temperature: 0.2
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
color: primary color: primary
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
@@ -29,6 +33,8 @@ permission:
task: task:
"*": deny "*": deny
"forgejo-label-manager": allow "forgejo-label-manager": allow
"repo-isolator": allow
"git-commit-helper": allow
"forgejo_*": allow "forgejo_*": allow
# CRITICAL: Never list repo-level labels — use org labels via forgejo-label-manager # CRITICAL: Never list repo-level labels — use org labels via forgejo-label-manager
"forgejo_list_repo_labels": deny "forgejo_list_repo_labels": deny
@@ -60,5 +66,6 @@ You set up project infrastructure from scratch. Your caller provides the product
1. **Detect before creating.** Always check if something exists before creating it. 1. **Detect before creating.** Always check if something exists before creating it.
2. **Never overwrite.** If a file or label already exists, skip it. 2. **Never overwrite.** If a file or label already exists, skip it.
3. **Credentials from prompt.** All Forgejo PAT, git identity, etc. come from the caller's prompt. 3. **Credentials from prompt.** All Forgejo PAT, git identity, etc. come from the caller's prompt.
4. **Apply labels via `forgejo-label-manager`.** Never apply labels directly or using the Forgejo MCP/task. All label operations must go through `forgejo-label-manager`. 4. **Work in an isolated clone.** Use `repo-isolator` to create an isolated clone of the target repository in `/tmp/`. All file creation and edits must be done in the clone — never directly in `/app`. Use `git-commit-helper` to commit and push changes. Clean up the isolated clone using `repo-isolator` when done.
5. **Exhaustive pagination for all list results.** Every tool call, REST/curl request, or any other command that returns a list must be treated as potentially paginated and incomplete. Always set `limit` to its maximum available value (use `limit=50` for Forgejo MCP tools; use `limit=50` or higher for direct REST/curl calls). After each list response, check whether the number of returned items equals the page size — if so, there are likely more results; fetch the next page (`page=2`, `page=3`, …) and continue until receiving a partial page. Never assume the first response is the complete result. This rule applies to every list-returning call without exception. *Examples specific to this agent (not exhaustive):* any `forgejo_list_*` calls used when checking for existing labels, milestones, or workflows must be paginated — missing one means re-creating something that already exists. 5. **Apply labels via `forgejo-label-manager`.** Never apply labels directly or using the Forgejo MCP/task. All label operations must go through `forgejo-label-manager`.
6. **Exhaustive pagination for all list results.** Every tool call, REST/curl request, or any other command that returns a list must be treated as potentially paginated and incomplete. Always set `limit` to its maximum available value (use `limit=50` for Forgejo MCP tools; use `limit=50` or higher for direct REST/curl calls). After each list response, check whether the number of returned items equals the page size — if so, there are likely more results; fetch the next page (`page=2`, `page=3`, …) and continue until receiving a partial page. Never assume the first response is the complete result. This rule applies to every list-returning call without exception. *Examples specific to this agent (not exhaustive):* any `forgejo_list_*` calls used when checking for existing labels, milestones, or workflows must be paginated — missing one means re-creating something that already exists.
+5 -1
View File
@@ -9,7 +9,9 @@ temperature: 0.1
model: openai/gpt-5-codex model: openai/gpt-5-codex
color: "#6B7280" color: "#6B7280"
permission: permission:
edit: deny edit:
"*": deny
"/tmp/**": allow
webfetch: deny webfetch: deny
bash: bash:
"*": deny "*": deny
@@ -37,6 +39,8 @@ permission:
# CRITICAL: DO NOT use forgejo_add_issue_labels directly # CRITICAL: DO NOT use forgejo_add_issue_labels directly
# Always delegate to forgejo-label-manager for label operations # Always delegate to forgejo-label-manager for label operations
"forgejo_add_issue_labels": deny "forgejo_add_issue_labels": deny
"external_directory":
"/tmp/**": allow
--- ---
# Repository Isolator # Repository Isolator
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2 temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector) # NO MODEL SPECIFIED - inherits from caller (tier selector)
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2 temperature: 0.2
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -11,7 +11,11 @@ temperature: 0.1
model: openai/gpt-5-codex model: openai/gpt-5-codex
color: accent color: accent
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -9,7 +9,11 @@ temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector) # NO MODEL SPECIFIED - inherits from caller (tier selector)
color: warning color: warning
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -7,7 +7,11 @@ hidden: true
temperature: 0.0 temperature: 0.0
model: openai/gpt-5-codex model: openai/gpt-5-codex
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: deny webfetch: deny
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -7,7 +7,11 @@ hidden: true
temperature: 0.0 temperature: 0.0
model: anthropic/claude-haiku-4-5 model: anthropic/claude-haiku-4-5
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: deny webfetch: deny
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -7,7 +7,11 @@ hidden: true
temperature: 0.0 temperature: 0.0
model: anthropic/claude-opus-4-6 model: anthropic/claude-opus-4-6
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: deny webfetch: deny
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -7,7 +7,11 @@ hidden: true
temperature: 0.0 temperature: 0.0
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: deny webfetch: deny
bash: bash:
"*": deny "*": deny
+6 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.1 temperature: 0.1
model: anthropic/claude-sonnet-4-6 model: anthropic/claude-sonnet-4-6
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
@@ -53,6 +57,7 @@ Your prompt provides the current milestone status data and the timeline file for
3. Add new entries — never overwrite existing ones. 3. Add new entries — never overwrite existing ones.
4. Commit using `git-commit-helper` with a Conventional Changelog message. 4. Commit using `git-commit-helper` with a Conventional Changelog message.
5. Push and exit. (No PR needed — timeline updates go directly to master.) 5. Push and exit. (No PR needed — timeline updates go directly to master.)
6. Clean up the isolated clone using `repo-isolator`.
## Rules ## Rules
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.1 temperature: 0.1
# NO MODEL SPECIFIED - inherits from caller (tier selector) # NO MODEL SPECIFIED - inherits from caller (tier selector)
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny
+5 -1
View File
@@ -8,7 +8,11 @@ hidden: true
temperature: 0.2 temperature: 0.2
# NO MODEL SPECIFIED - inherits from caller (tier selector) # NO MODEL SPECIFIED - inherits from caller (tier selector)
permission: permission:
edit: allow edit:
"*": deny
"/tmp/**": allow
external_directory:
"/tmp/**": allow
webfetch: allow webfetch: allow
bash: bash:
"*": deny "*": deny