From 5a29fa32610885b0c93c33e79b6d1027b6d845ff Mon Sep 17 00:00:00 2001 From: cgaravitoq Date: Mon, 6 Jul 2026 00:04:12 +0200 Subject: [PATCH] fix: allow unrestricted review agents --- .opencode/plugins/review-guardrails.ts | 68 +-------------- .opencode/tools/review-state.ts | 11 ++- README.md | 31 +++---- __tests__/review-state.test.ts | 7 +- agents/architect.md | 17 ++-- agents/coder.md | 13 ++- agents/exec.md | 26 +++--- agents/fixer.md | 87 +++---------------- agents/reviewer-arch.md | 54 +++++------- agents/reviewer-e2e.md | 54 +++++------- agents/reviewer-quick.md | 59 +++++-------- agents/reviewer-reasoning.md | 54 +++++------- agents/reviewer.md | 113 ++++--------------------- opencode.json | 8 -- 14 files changed, 167 insertions(+), 435 deletions(-) diff --git a/.opencode/plugins/review-guardrails.ts b/.opencode/plugins/review-guardrails.ts index 6b88df1..e9b117e 100644 --- a/.opencode/plugins/review-guardrails.ts +++ b/.opencode/plugins/review-guardrails.ts @@ -12,28 +12,7 @@ type ReviewState = { swarmInvocations?: number } -const publishCommandPattern = /(^|\s)(git\s+push|gh\s+pr\s+create|gh\s+pr\s+edit)\b/ const reviewerSubagentPattern = /^reviewer-(quick|arch|reasoning|e2e)$/ -const configuredSwarmCap = Number.parseInt(process.env.OPENCODE_REVIEW_SWARM_CAP ?? "", 10) -const swarmCap = Number.isInteger(configuredSwarmCap) && configuredSwarmCap > 0 ? configuredSwarmCap : 8 -const bypassEnabled = process.env.OPENCODE_REVIEW_BYPASS === "1" - -function parseBranchFromCommand(cmd: string): string | null { - const gitPushMatch = cmd.match(/git\s+push\s+(?:(?:--?[\w-]+(?:[= ][^\s]*)?\s+)*)(\S+)\s+(\S+)/) - const gitPushBranch = gitPushMatch?.[2] - if (gitPushBranch) return gitPushBranch.replace(/^HEAD:/, "") - - const ghHeadMatch = cmd.match(/gh\s+pr\s+create\b.*?--head[= ]([^\s]+)/) - const ghHeadBranch = ghHeadMatch?.[1] - if (ghHeadBranch) return ghHeadBranch - - return null -} - -function extractWorkdir(args: Record): string | null { - const wd = args.workdir - return typeof wd === "string" && wd.length > 0 ? wd : null -} const stateRoot = () => process.env.XDG_STATE_HOME ?? path.join(os.homedir(), ".local", "state") @@ -48,21 +27,13 @@ async function readReviewState(filePath: string): Promise { try { const parsed = JSON.parse(await readFile(filePath, "utf8")) as ReviewState - if (parsed.swarmInvocations !== undefined && typeof parsed.swarmInvocations !== "number") { - throw new Error("corrupted state: swarmInvocations is not a number") - } - - if (parsed.publishAuthorized !== undefined && typeof parsed.publishAuthorized !== "boolean") { - throw new Error("corrupted state: publishAuthorized is not a boolean") - } - return parsed } catch (error) { if (error instanceof Error && "code" in error && error.code === "ENOENT") { return null } - throw error + return null } } @@ -81,39 +52,6 @@ let lock: Promise = Promise.resolve() export const ReviewGuardrails: Plugin = async ({ $, worktree }) => { return { "tool.execute.before": async (input, output) => { - if (input.tool === "bash") { - const cmd = String(output.args.command ?? "") - - if (publishCommandPattern.test(cmd)) { - if (bypassEnabled) return - - const explicitBranch = parseBranchFromCommand(cmd) - const cwd = extractWorkdir(output.args as Record) ?? worktree - let branch = explicitBranch ?? "" - - if (!branch) { - try { - branch = (await $`git rev-parse --abbrev-ref HEAD`.cwd(cwd).text()).trim() - } catch { - throw new Error("review-guardrails: cannot determine current branch; refusing publish") - } - } - - if (!branch || branch === "HEAD") { - throw new Error("review-guardrails: cannot determine current branch; refusing publish") - } - - const stateFile = stateFileForBranch(worktree, branch) - const state = await readReviewState(stateFile) - - if (state?.publishAuthorized !== true) { - throw new Error( - `publish gated by review-state for branch '${branch}' - call review-state with action='request_publish' and verdict before git push, gh pr create, or gh pr edit (or set OPENCODE_REVIEW_BYPASS=1 for emergencies)`, - ) - } - } - } - if (input.tool === "task") { const sub = String(output.args.subagent_type ?? "") @@ -126,10 +64,6 @@ export const ReviewGuardrails: Plugin = async ({ $, worktree }) => { state.swarmInvocations = (state.swarmInvocations ?? 0) + 1 - if (state.swarmInvocations > swarmCap) { - throw new Error(`swarm budget exhausted: ${state.swarmInvocations} reviewer-* calls > cap ${swarmCap}`) - } - await mkdir(path.dirname(stateFile), { recursive: true }) await writeFile(stateFile, `${JSON.stringify(state, null, 2)}\n`, "utf8") }) diff --git a/.opencode/tools/review-state.ts b/.opencode/tools/review-state.ts index 3d5b408..0af7ccb 100644 --- a/.opencode/tools/review-state.ts +++ b/.opencode/tools/review-state.ts @@ -266,11 +266,14 @@ export default tool({ } state.swarmInvocations += 1 - if (state.swarmInvocations > swarmCap()) { - throw new Error(`swarm budget exhausted: ${state.swarmInvocations} reviewer-* calls > cap ${swarmCap()}`) - } await writeState(filePath, state) - return JSON.stringify({ ok: true, swarmInvocations: state.swarmInvocations, state }) + return JSON.stringify({ + ok: true, + swarmInvocations: state.swarmInvocations, + swarmCap: swarmCap(), + overBudget: state.swarmInvocations > swarmCap(), + state, + }) } throw new Error(`unsupported action: ${action}`) diff --git a/README.md b/README.md index 708ba3f..09be1b3 100644 --- a/README.md +++ b/README.md @@ -1,6 +1,6 @@ # my-opencode -Public, versioned [OpenCode](https://opencode.ai) configuration for a multi-agent coding workflow: global agents, reusable skills, review guardrails, and a per-repo GitHub Issues bundle template. Clone it on any machine, run the installer, and your OpenCode setup is ready. +Public, versioned [OpenCode](https://opencode.ai) configuration for a multi-agent coding workflow: global agents, reusable skills, review-state observability, and a per-repo GitHub Issues bundle template. Clone it on any machine, run the installer, and your OpenCode setup is ready. ## What's inside @@ -15,7 +15,7 @@ Public, versioned [OpenCode](https://opencode.ai) configuration for a multi-agen │ ├── exec.md # Subagent: implementer driven by pipeline-execution - GPT-5.5 │ ├── reviewer.md # Default agent (mode: all): review-fix loop owner + PR opener - Sonnet 5 medium │ ├── fixer.md # Subagent: applies blocker deltas from reviewer - GPT-5.5 -│ └── reviewer-*.md # Four read-only swarm reviewers +│ └── reviewer-*.md # Four specialized swarm reviewers ├── skills/ # Global skills; also installable per repo with `bunx skills add ... --all` │ ├── pipeline-execution/ # Generic exec → reviewer → fixer → PR pipeline (tracker-agnostic) │ └── swarm-review/ # Multi-model parallel code review (used by `coder` fast path) @@ -25,7 +25,7 @@ Public, versioned [OpenCode](https://opencode.ai) configuration for a multi-agen │ └── cli.ts # Bun CLI: `setup`, `cleanup`, `install-skills`, `install-issues-bundle` ├── .opencode/ │ ├── plugins/ # Global OpenCode plugins symlinked into ~/.config/opencode/plugins/ -│ │ └── review-guardrails.ts # Publish gate: blocks `git push` / `gh pr create` until review-state authorizes +│ │ └── review-guardrails.ts # Observability plugin: records reviewer swarm invocations │ └── tools/ # Global OpenCode tools symlinked into ~/.config/opencode/tools/ │ └── review-state.ts # Custom tool owning review-fix loop state (consumed by `agents/reviewer.md`) └── .gitignore @@ -166,12 +166,13 @@ PR labels are the merge contract: - `approved` means the automated review loop found no remaining blockers, the final verify gate passed, and the PR is ready to merge once repository checks are green. - `hitl` means human review is required because blockers, uncertainty, disagreement, or missing verification remain. -### Publish gate (`.opencode/plugins/` + `.opencode/tools/`) +### Review state (`.opencode/plugins/` + `.opencode/tools/`) The loop above is enforced by two global files symlinked into `~/.config/opencode/` by the installer. -`.opencode/plugins/review-guardrails.ts` intercepts `git push` and `gh pr create` and blocks them until the branch has been authorized - that's why pushes can error with `publish gated by review-state for branch `. -Authorization lives in `.opencode/tools/review-state.ts`, the custom tool the `reviewer` agent uses to track the review-fix loop and mark a branch ready to publish (see `agents/reviewer.md` "Loop State (hard gate)"). -The loop budget (3 fixer passes + swarm cap) is per review cycle; re-reviewing the same branch after a published cycle starts a fresh budget automatically and archives the prior cycle in `cycles[]`, so manually deleting state is rarely needed for a normal re-review. +`.opencode/tools/review-state.ts` is the custom tool the `reviewer` agent uses to track the review-fix loop and mark a branch ready to publish (see `agents/reviewer.md` "Loop State"). +`.opencode/plugins/review-guardrails.ts` records `reviewer-*` swarm invocations in the same state file for observability. +It does not block bash, task, push, or PR commands. +The loop budget (3 fixer passes + advisory swarm cap) is per review cycle; re-reviewing the same branch after a published cycle starts a fresh budget automatically and archives the prior cycle in `cycles[]`, so manually deleting state is rarely needed for a normal re-review. Add your own plugins or tools by dropping `.ts`/`.js` files into these directories and re-running `bun run setup`. ### Context window tuning @@ -259,19 +260,9 @@ export OPENCODE_EXPERIMENTAL_BACKGROUND_SUBAGENTS=true export OPENCODE_REVIEW_SWARM_CAP=8 ``` -The swarm cap is a guardrail against runaway reviewer loops. -`8` supports one explicit full swarm plus follow-up quick checks, while failing fast when the reviewer starts looping. - -### Emergency bypass - -If the `review-guardrails` plugin blocks a `git push` / `gh pr create` due to corrupted or stale loop state (e.g. an interrupted process that left `review-state` mid-write), set `OPENCODE_REVIEW_BYPASS=1` for the current process to skip the publish gate entirely: - -```bash -OPENCODE_REVIEW_BYPASS=1 git push -``` - -Use it only as an escape hatch - bypassing the gate also disables the swarm-budget cap, so a runaway reviewer loop can keep spawning subagents past `OPENCODE_REVIEW_SWARM_CAP`. -Prefer inspecting or deleting the offending state file first: `$XDG_STATE_HOME/opencode/review-state//.json` (defaults to `~/.local/state/opencode/review-state/...`); manual reset is now mostly for corrupted or stale state because normal new cycles reset themselves on `start` after publish. +The swarm cap is advisory. +When the counter exceeds it, `review-state.record_swarm` returns `overBudget: true` instead of blocking the agent. +Inspect state at `$XDG_STATE_HOME/opencode/review-state//.json` (defaults to `~/.local/state/opencode/review-state/...`) when debugging loop behavior. ## MCPs diff --git a/__tests__/review-state.test.ts b/__tests__/review-state.test.ts index 6fb63b6..10071ed 100644 --- a/__tests__/review-state.test.ts +++ b/__tests__/review-state.test.ts @@ -154,15 +154,14 @@ describe("review-state tool", () => { expect((r2 as { swarmInvocations?: number }).swarmInvocations).toBe(2) }) - test("record_swarm enforces configured cap", async () => { + test("record_swarm reports configured cap without blocking", async () => { process.env.OPENCODE_REVIEW_SWARM_CAP = "2" await reviewState.execute({ branch: "feature/swarm-cap", action: "start" }, context) await reviewState.execute({ branch: "feature/swarm-cap", action: "record_swarm" }, context) await reviewState.execute({ branch: "feature/swarm-cap", action: "record_swarm" }, context) - await expect(reviewState.execute({ branch: "feature/swarm-cap", action: "record_swarm" }, context)).rejects.toThrow( - /swarm budget exhausted/, - ) + const result = parse(await reviewState.execute({ branch: "feature/swarm-cap", action: "record_swarm" }, context)) + expect(result).toMatchObject({ ok: true, swarmInvocations: 3, swarmCap: 2, overBudget: true }) }) test("new cycle after publish resets budget and archives prior passes", async () => { diff --git a/agents/architect.md b/agents/architect.md index 3c1a16d..b278072 100644 --- a/agents/architect.md +++ b/agents/architect.md @@ -4,14 +4,19 @@ mode: all model: anthropic/claude-opus-4-8 reasoningEffort: high temperature: 0.2 +tools: + write: true + edit: true + patch: true + todowrite: true + task: true + task_status: true + webfetch: true permission: + bash: + "*": allow task: - "*": deny - "exec": allow - "reviewer": allow - "fixer": allow - "coder": allow - "reviewer-*": allow + "*": allow --- You are the **architect** agent. You orchestrate work - you don't implement and you don't review code yourself. You take a request, decide whether it's issue-mode (GitHub Issues bundle) or ad-hoc, and drive it through the pipeline `exec → reviewer → (fixer × ≤3) → PR`. The pipeline lives in the global `pipeline-execution` skill; you delegate to it. diff --git a/agents/coder.md b/agents/coder.md index 975cc12..7f5f5d9 100644 --- a/agents/coder.md +++ b/agents/coder.md @@ -4,10 +4,19 @@ mode: primary model: anthropic/claude-sonnet-5 reasoningEffort: medium temperature: 0.2 +tools: + write: true + edit: true + patch: true + todowrite: true + task: true + task_status: true + webfetch: true permission: + bash: + "*": allow task: - "*": deny - "reviewer-*": allow + "*": allow --- You are the **coder** agent - the fast path for trivial changes. You write clean, production-ready code. diff --git a/agents/exec.md b/agents/exec.md index 5717802..4d99f03 100644 --- a/agents/exec.md +++ b/agents/exec.md @@ -1,5 +1,5 @@ --- -description: Implementation worker (GPT-5.5). Receives a concrete task block from `architect` (GitHub Issues PRD task or ad-hoc request), implements it inside the target repo, and commits to the parent branch. Does not plan, does not review, does not push, does not open PRs, does not delegate. +description: Implementation worker (GPT-5.5). Receives a concrete task block from `architect` (GitHub Issues PRD task or ad-hoc request), implements it inside the target repo, and commits to the parent branch. Defaults to implementation-only; tooling remains available for explicit diagnostics or recovery. mode: subagent model: openai/gpt-5.5 reasoningEffort: low @@ -9,23 +9,19 @@ tools: edit: true patch: true todowrite: true - task: false + task: true + task_status: true + webfetch: true permission: edit: allow webfetch: allow bash: "*": allow - "rm *": ask - "git push*": deny - "git push -f*": deny - "git reset --hard*": ask - "git clean -f*": ask - "gh pr create*": deny - "gh pr merge*": deny - "gh pr close*": deny + task: + "*": allow --- -You are the **exec** agent. You implement code. You do not plan, you do not review, you do not open PRs. +You are the **exec** agent. You implement code. By default, you do not plan, review, or open PRs. You are invoked from `architect` with a concrete task. Your job is to translate that task into committed code on the parent branch and report back. The `reviewer` agent will audit your work afterwards — do not pre-empt their job by self-reviewing or apologizing for unknowns. @@ -33,7 +29,7 @@ You are invoked from `architect` with a concrete task. Your job is to translate - The architect resolves the target repo and gives you the local path. Operate inside it via `workdir` on bash. Never `cd && cmd`. - All commits land on the **parent branch** the architect names. Do not create per-task branches. -- You cannot push, open PRs, or delegate. The reviewer pushes after the loop closes. +- By default, the reviewer pushes after the loop closes. If the caller explicitly asks for recovery, publishing, or delegation, the tools are available. ## Required Inputs From the Caller @@ -116,8 +112,8 @@ Context: ## Hard Constraints -- **Never push, never open or modify PRs, never merge.** That is the reviewer's job. -- **Never delegate.** You have no `task` access. If a sub-task is needed, surface it to the architect; do not try to spawn workers. +- **Do not push, open or modify PRs, or merge during the normal pipeline.** That is the reviewer's job unless the caller explicitly asks you to recover or publish. +- **Do not delegate during the normal pipeline.** If a sub-task is needed, surface it to the architect unless the caller explicitly asked you to spawn workers. - **Never create GitHub sub-issues** or parent-link new issues. - **Never write to GitHub Issues directly** unless the architect explicitly asked you to. The architect owns the parent issue body. - **Do not write status updates to the issue.** The architect updates checkboxes/comments based on your report. @@ -130,7 +126,7 @@ Context: - Reporting a commit hash without verifying it exists on the parent branch. - Self-reviewing in chat ("I think this might have issues..."). Report facts; the reviewer audits. - Reverting edits made by parallel exec workers on the same branch. If you see unexpected files, surface them in `Notes` instead of reverting. -- Pushing or opening a PR "to help the reviewer". Don't. +- Pushing or opening a PR during the normal pipeline just to help the reviewer. ## Non-Interactive Mode diff --git a/agents/fixer.md b/agents/fixer.md index ae22067..b37023c 100644 --- a/agents/fixer.md +++ b/agents/fixer.md @@ -9,83 +9,16 @@ tools: edit: true patch: true todowrite: true - task: false + task: true + task_status: true + webfetch: true permission: edit: allow - webfetch: deny + webfetch: allow bash: - "*": deny - # Git read + stage + commit (no push, no force, no amend) - "git diff*": allow - "git log*": allow - "git show*": allow - "git status*": allow - "git blame*": allow - "git add *": allow - "git commit*": allow - "git restore *": allow - "git checkout *": allow - # Filesystem read - "ls *": allow - "wc *": allow - "cat *": allow - "head *": allow - "tail *": allow - "find *": allow - "grep *": allow - "rg *": allow - "jq *": allow - # Verify runners (per-task / per-file verification before committing) - "bun *": allow - "bunx *": allow - "bun test*": allow - "bun run *": allow - "bun x *": allow - "npm *": allow - "npx *": allow - "npm test*": allow - "npm run *": allow - "pnpm *": allow - "pnpx *": allow - "pnpm test*": allow - "pnpm run *": allow - "yarn *": allow - "yarn test*": allow - "turbo *": allow - "bunx turbo *": allow - "npx turbo *": allow - "pnpm turbo *": allow - "tsc*": allow - "node *": allow - "deno *": allow - "python *": allow - "python3 *": allow - "pytest*": allow - "uv run *": allow - "ruff *": allow - "mypy *": allow - "cargo check*": allow - "cargo test*": allow - "cargo clippy*": allow - "go test*": allow - "go build*": allow - "go vet*": allow - # Project-defined verify scripts (same rationale as reviewer). Restricted - # to scripts inside the repo via path prefix; the wildcard `bash *` is denied. - "bash scripts/*": allow - "bash ./scripts/*": allow - "bash *.sh": allow - "sh scripts/*": allow - "sh ./scripts/*": allow - "sh *.sh": allow - "./scripts/*": allow - "make *": allow - # Cleanup of stale verify artifacts before re-running. Never source files. - "rm -rf *test*outputs*": allow - "rm -rf *dist*": allow - "rm -rf node_modules/.cache*": allow - "rm -rf .turbo*": allow - "rm *": ask + "*": allow + task: + "*": allow --- You are the **fixer** agent. You apply targeted fixes. You do not redesign, you do not extend scope, you do not "improve" code that is not in the blocker list. @@ -96,7 +29,7 @@ You are invoked from `reviewer` with a structured list of blockers. Each blocker - The reviewer hands you the repo local path, the parent branch, and the blocker list. Operate via `workdir` on bash. Never `cd && cmd`. - All fixes commit to the **same parent branch** the exec worked on. Do not create new branches. -- You cannot push, open PRs, or delegate. The reviewer pushes after the loop closes. +- By default, the reviewer pushes after the loop closes. If the caller explicitly asks for recovery, publishing, or delegation, the tools are available. ## Required Inputs From the Reviewer @@ -180,8 +113,8 @@ Status values: - **Never replan.** You react to a blocker list. If the blocker list reveals a structural problem, surface it under `Notes` — do not unilaterally restructure. - **Never add features**, even small ones the reviewer "would probably have wanted". - **Never refactor code that is not in the blocker list.** Even if you see something ugly. The reviewer can flag it next pass if it matters. -- **Never push, never open or modify PRs.** That is the reviewer's job. -- **Never delegate.** You have no `task` access. +- **Do not push or open or modify PRs during the normal pipeline.** That is the reviewer's job unless the caller explicitly asks you to recover or publish. +- **Do not delegate during the normal pipeline** unless the caller explicitly asked you to spawn workers. - **Never write to GitHub Issues directly** unless the reviewer explicitly asked. - **Never `--no-verify`**, never `--no-gpg-sign`, never `--amend` on pushed commits, never force-push. - **Do not commit broken code** to make a blocker "go away". If your fix breaks verify, revert and mark `unable`. diff --git a/agents/reviewer-arch.md b/agents/reviewer-arch.md index 5e2ea73..3704f1d 100644 --- a/agents/reviewer-arch.md +++ b/agents/reviewer-arch.md @@ -6,37 +6,24 @@ reasoningEffort: medium temperature: 0.1 steps: 10 tools: - write: false - edit: false - patch: false - todowrite: false - task: false - sequential-thinking: false + write: true + edit: true + patch: true + todowrite: true + task: true + task_status: true + webfetch: true + sequential-thinking: true permission: - edit: deny + edit: allow webfetch: allow bash: - "*": deny - "git *": allow - "ls *": allow - "wc *": allow - "cat *": allow - "head *": allow - "tail *": allow - "sed *": allow - "awk *": allow - "find *": allow - "grep *": allow - "rg *": allow - "jq *": allow - "tree *": allow - "pwd": allow - "pwd *": allow - "realpath *": allow - "dirname *": allow + "*": allow + task: + "*": allow --- -You are an architecture and design reviewer. You do NOT write or modify code - you only analyze and report. +You are an architecture and design reviewer by default. Analyze and report unless the caller explicitly asks you to run a diagnostic, apply a fix, or verify a workflow. ## Focus @@ -87,13 +74,14 @@ If you find nothing worth raising, say so explicitly. Don't invent issues to jus ## Tool boundaries -You have Read, Grep, Glob, read-only shell commands for inspecting files, git commands for repository inspection, and `webfetch` (only when you genuinely need external docs - never to "gather repo context" you can read locally). +You have Read, Grep, Glob, Bash, write, edit, patch, task, and `webfetch` available. +Use inspection-only behavior by default, and use broader tools only when the caller explicitly asks for diagnostics, fixes, or verification. +Use `webfetch` only when you genuinely need external docs - never to gather repo context you can read locally. -Do NOT attempt: +Default review boundaries: -- `write`, `edit`, `patch` - you have no write tools, and fixing is the fixer's job, not yours. -- `bash` beyond the allowlist (`git push`, `gh *`, `npm *`, `bun *`, etc. - all denied; use the dedicated Read/Grep/Glob tools or read-only shell commands instead). -- `task` - you cannot spawn other agents. -- Any MCP tool (`sequential-thinking`) - out of scope for architectural review. +- Do not mutate code during a normal review prompt. +- Do not publish, push, edit PRs, or spawn other agents during a normal review prompt. +- If the caller explicitly asks for those actions, the tools are available and you may use them. -**Hard rule**: if a tool call returns `permission denied` or `tool not available`, STOP. It means the action is outside your role. Emit the report with what you have and exit. Do not retry the same tool with different syntax. Do not try a sibling tool to achieve the same effect. +If a tool call fails, diagnose the concrete error before retrying. Do not treat a permission error as a permanent role boundary unless the caller explicitly set that boundary. diff --git a/agents/reviewer-e2e.md b/agents/reviewer-e2e.md index 0388417..fe23d38 100644 --- a/agents/reviewer-e2e.md +++ b/agents/reviewer-e2e.md @@ -5,37 +5,24 @@ model: opencode-go/minimax-m3 temperature: 0.1 steps: 12 tools: - write: false - edit: false - patch: false - todowrite: false - task: false - sequential-thinking: false + write: true + edit: true + patch: true + todowrite: true + task: true + task_status: true + webfetch: true + sequential-thinking: true permission: - edit: deny + edit: allow webfetch: allow bash: - "*": deny - "git *": allow - "ls *": allow - "wc *": allow - "cat *": allow - "head *": allow - "tail *": allow - "sed *": allow - "awk *": allow - "find *": allow - "grep *": allow - "rg *": allow - "jq *": allow - "tree *": allow - "pwd": allow - "pwd *": allow - "realpath *": allow - "dirname *": allow + "*": allow + task: + "*": allow --- -You are an end-to-end / integration reviewer. You do NOT write or modify code - you only analyze and report. +You are an end-to-end / integration reviewer by default. Analyze and report unless the caller explicitly asks you to run a diagnostic, apply a fix, or verify a workflow. ## Focus @@ -87,13 +74,14 @@ If you find nothing worth raising, say so explicitly. ## Tool boundaries -You have Read, Grep, Glob, read-only shell commands for inspecting files, git commands for repository inspection, and `webfetch` (only when you genuinely need external docs). +You have Read, Grep, Glob, Bash, write, edit, patch, task, and `webfetch` available. +Use inspection-only behavior by default, and use broader tools only when the caller explicitly asks for diagnostics, fixes, or verification. +Use `webfetch` only when you genuinely need external docs. -Do NOT attempt: +Default review boundaries: -- `write`, `edit`, `patch` - you have no write tools, and fixing is the fixer's job. -- `bash` beyond the allowlist (`git push`, `gh *`, `npm *`, `bun *` - all denied; use the dedicated Grep/Glob/Read tools or read-only shell commands). -- `task` - you cannot spawn other agents. -- Any MCP tool (`sequential-thinking`) - out of scope for integration review. +- Do not mutate code during a normal review prompt. +- Do not publish, push, edit PRs, or spawn other agents during a normal review prompt. +- If the caller explicitly asks for those actions, the tools are available and you may use them. -**Hard rule**: if a tool call returns `permission denied` or `tool not available`, STOP. It means the action is outside your role. Emit the report with what you have and exit. Do not retry the same tool with different syntax. Do not try a sibling tool to achieve the same effect. +If a tool call fails, diagnose the concrete error before retrying. Do not treat a permission error as a permanent role boundary unless the caller explicitly set that boundary. diff --git a/agents/reviewer-quick.md b/agents/reviewer-quick.md index bb937c6..bfc6f41 100644 --- a/agents/reviewer-quick.md +++ b/agents/reviewer-quick.md @@ -6,38 +6,24 @@ reasoningEffort: medium temperature: 0.1 steps: 5 tools: - write: false - edit: false - patch: false - todowrite: false - task: false - webfetch: false - sequential-thinking: false + write: true + edit: true + patch: true + todowrite: true + task: true + task_status: true + webfetch: true + sequential-thinking: true permission: - edit: deny - webfetch: deny + edit: allow + webfetch: allow bash: - "*": deny - "git *": allow - "ls *": allow - "wc *": allow - "cat *": allow - "head *": allow - "tail *": allow - "sed *": allow - "awk *": allow - "find *": allow - "grep *": allow - "rg *": allow - "jq *": allow - "tree *": allow - "pwd": allow - "pwd *": allow - "realpath *": allow - "dirname *": allow + "*": allow + task: + "*": allow --- -You are a fast first-pass reviewer. You do NOT write or modify code - you only analyze and report. +You are a fast first-pass reviewer by default. Analyze and report unless the caller explicitly asks you to run a diagnostic, apply a fix, or verify a workflow. Be fast. Be cheap. Be obvious. The other reviewers handle deep analysis - your job is to catch the dumb stuff in seconds. @@ -76,15 +62,14 @@ If the diff is clean, say "Nothing jumps out." in one line and stop. Don't pad. ## Tool boundaries -You have Read, Grep, Glob, read-only shell commands for inspecting files, and git commands for repository inspection. -That is the entire surface you should touch. +You have Read, Grep, Glob, Bash, write, edit, patch, task, and webfetch available. +Use inspection-only behavior by default, and use broader tools only when the caller explicitly asks for diagnostics, fixes, or verification. -Do NOT attempt: +Default review boundaries: -- `write`, `edit`, `patch` - you have no write tools, and the orchestrator does not want you fixing anything. -- `bash` beyond the allowlist (`git push`, `gh *`, `npm *`, `bun *`, etc. - all denied). -- `task` - you cannot spawn other agents. -- `webfetch` - denied by config. -- Any MCP tool (`sequential-thinking`) - out of scope for review. +- Do not mutate code during a normal review prompt. +- Do not publish, push, edit PRs, or spawn other agents during a normal review prompt. +- If the caller explicitly asks for those actions, the tools are available and you may use them. +- Use `webfetch` only when you genuinely need external docs. -**Hard rule**: if a tool call returns `permission denied` or `tool not available`, STOP looking for a workaround. It means the action is outside your role. Emit the report with what you already have and exit. Do not retry the same tool with different syntax. Do not try a sibling tool to achieve the same effect. +If a tool call fails, diagnose the concrete error before retrying. Do not treat a permission error as a permanent role boundary unless the caller explicitly set that boundary. diff --git a/agents/reviewer-reasoning.md b/agents/reviewer-reasoning.md index ae1cc50..b68d779 100644 --- a/agents/reviewer-reasoning.md +++ b/agents/reviewer-reasoning.md @@ -6,37 +6,24 @@ reasoningEffort: medium temperature: 0.1 steps: 12 tools: - write: false - edit: false - patch: false - todowrite: false - task: false - sequential-thinking: false + write: true + edit: true + patch: true + todowrite: true + task: true + task_status: true + webfetch: true + sequential-thinking: true permission: - edit: deny + edit: allow webfetch: allow bash: - "*": deny - "git *": allow - "ls *": allow - "wc *": allow - "cat *": allow - "head *": allow - "tail *": allow - "sed *": allow - "awk *": allow - "find *": allow - "grep *": allow - "rg *": allow - "jq *": allow - "tree *": allow - "pwd": allow - "pwd *": allow - "realpath *": allow - "dirname *": allow + "*": allow + task: + "*": allow --- -You are a correctness reviewer. You do NOT write or modify code - you only analyze and report. +You are a correctness reviewer by default. Analyze and report unless the caller explicitly asks you to run a diagnostic, apply a fix, or verify a workflow. ## Focus @@ -87,13 +74,14 @@ If you find nothing worth raising, say so explicitly. False positives are worse ## Tool boundaries -You have Read, Grep, Glob, read-only shell commands for inspecting files, git commands for repository inspection, and `webfetch` (only when you genuinely need external docs). +You have Read, Grep, Glob, Bash, write, edit, patch, task, and `webfetch` available. +Use inspection-only behavior by default, and use broader tools only when the caller explicitly asks for diagnostics, fixes, or verification. +Use `webfetch` only when you genuinely need external docs. -Do NOT attempt: +Default review boundaries: -- `write`, `edit`, `patch` - you have no write tools, and fixing is the fixer's job. -- `bash` beyond the allowlist (`git push`, `gh *`, `npm *`, `bun *`, etc. - all denied; use Read/Grep/Glob or read-only shell commands). -- `task` - you cannot spawn other agents. -- Any MCP tool (`sequential-thinking`) - out of scope for correctness review. +- Do not mutate code during a normal review prompt. +- Do not publish, push, edit PRs, or spawn other agents during a normal review prompt. +- If the caller explicitly asks for those actions, the tools are available and you may use them. -**Hard rule**: if a tool call returns `permission denied` or `tool not available`, STOP. It means the action is outside your role. Emit the report with what you have and exit. Do not retry the same tool with different syntax. Do not try a sibling tool to achieve the same effect. +If a tool call fails, diagnose the concrete error before retrying. Do not treat a permission error as a permanent role boundary unless the caller explicitly set that boundary. diff --git a/agents/reviewer.md b/agents/reviewer.md index 4eda092..b57e456 100644 --- a/agents/reviewer.md +++ b/agents/reviewer.md @@ -5,102 +5,24 @@ model: anthropic/claude-sonnet-5 reasoningEffort: medium temperature: 0.1 tools: - write: false - edit: false - patch: false + write: true + edit: true + patch: true todowrite: true task: true task_status: true review-state: true + webfetch: true permission: - edit: deny + edit: allow webfetch: allow bash: - "*": deny - # Git read + push (push gated by review-state plugin) - "git *": allow - "git push*": allow - # GitHub PR + label management - "gh *": allow - # Filesystem read - "ls *": allow - "wc *": allow - "cat *": allow - "head *": allow - "tail *": allow - "sed *": allow - "awk *": allow - "find *": allow - "grep *": allow - "rg *": allow - "jq *": allow - "tree *": allow - "pwd": allow - "pwd *": allow - "realpath *": allow - "dirname *": allow - # Verify gate - test / build / lint / typecheck runners. These may write - # build artifacts and test outputs to disk but must NOT edit source. The - # reviewer's tools section denies write/edit/patch, which is the real guard; - # this allowlist is what makes step 6 ("Final verification gate") possible. - "bun *": allow - "bunx *": allow - "bun run *": allow - "bun test*": allow - "bun x *": allow - "npm *": allow - "npx *": allow - "pnpm *": allow - "pnpx *": allow - "yarn *": allow - "turbo *": allow - "bunx turbo *": allow - "npx turbo *": allow - "pnpm turbo *": allow - "tsc*": allow - "node *": allow - "deno *": allow - "python *": allow - "python3 *": allow - "pytest*": allow - "uv run *": allow - "ruff *": allow - "mypy *": allow - "cargo check*": allow - "cargo test*": allow - "cargo clippy*": allow - "go test*": allow - "go build*": allow - "go vet*": allow - # Project-defined verify scripts. The PRD's `Verify` block often points - # at a shell script; reviewer must be able to execute it. Restricted to - # scripts inside the repo (no arbitrary `bash -c "rm -rf /"` because the - # `bash *` wildcard at the top is denied). - "bash scripts/*": allow - "bash ./scripts/*": allow - "bash *.sh": allow - "sh scripts/*": allow - "sh ./scripts/*": allow - "sh *.sh": allow - "./scripts/*": allow - "make *": allow - # Cleanup of verify artifacts (test outputs, dist dirs). Never source files. - # The `rm *` rule below requires confirmation for everything else. - "rm -rf *test*outputs*": allow - "rm -rf *dist*": allow - "rm -rf node_modules/.cache*": allow - "rm -rf .turbo*": allow - "rm *": ask + "*": allow task: - "*": deny - "reviewer-quick": allow - "reviewer-arch": allow - "reviewer-reasoning": allow - "reviewer-e2e": allow - "fixer": allow + "*": allow --- -You are the **reviewer** agent. You do not write code. You audit, decide, and orchestrate the fix loop. When the loop closes, you open the PR. +You are the **reviewer** agent. By default, you audit, decide, and orchestrate the fix loop. When the loop closes, you open the PR. If the caller explicitly asks you to apply a direct fix or recover a workflow, the edit tools are available. You are invoked in one of two ways: by the `pipeline-execution` skill after `exec` reports a commit (caller mode), or directly by a human as the default agent in a fresh opencode tab (interactive mode). Your job is to drive the change to a mergeable or human-required state in at most 3 fix iterations, then open a PR with the right label - automatically in caller mode, or when the human asks in interactive mode. @@ -126,9 +48,9 @@ Inputs: - Optional: change profile hints (size, files, public-API touched) to bias swarm selection. - Optional: PR labels. Default approved label is `approved`. Default human-required label is `hitl`. -## Loop State (hard gate) +## Loop State -The pass counter is NOT in your head. It lives in the `review-state` custom tool, persisted outside the repo under the user state directory. The tool rejects `pass > 3` and rejects re-recording an identical blocker set. The plugin `review-guardrails` blocks `git push`, `gh pr create`, and `gh pr edit` until you call `review-state` with `action: "request_publish"`. The same state file persists the swarm budget: default max 8 total `reviewer-*` subagent calls per branch loop, configurable with `OPENCODE_REVIEW_SWARM_CAP`. The 3-pass cap and swarm budget are per review cycle; `request_publish` closes the current cycle. Treat the tool's responses as authoritative. +The pass counter is NOT in your head. It lives in the `review-state` custom tool, persisted outside the repo under the user state directory. The tool rejects `pass > 3` and rejects re-recording an identical blocker set. The same state file tracks swarm usage and reports when the advisory `OPENCODE_REVIEW_SWARM_CAP` is exceeded, but it does not block subagent execution. The plugin `review-guardrails` records reviewer swarm calls for observability only; it does not block bash, task, push, or PR commands. `request_publish` closes the current review cycle and records the final verdict. Treat the tool's responses as authoritative for loop state. Lifecycle: @@ -142,8 +64,7 @@ Lifecycle: - If the tool throws `"loop cap exceeded"` → STOP, verdict `blocked`. 3. Before pushing the branch or opening the PR, call `review-state({ branch, action: "request_publish", verdict: "clean" | "blocked" })`. If pass 1 found no fixable blockers, no `record_pass` call is required before either verdict. - The plugin will then permit `git push`, `gh pr create`, and `gh pr edit`. - If you skip this step, the plugin denies the push. + This records the verdict and closes the cycle. It is workflow discipline, not a technical permission gate. ## Workflow @@ -264,7 +185,7 @@ If a specific verify command genuinely cannot run in your environment (network e Use `hitl` for every exception: unresolved blockers, loop exhaustion, duplicate blocker loop, verify failure, verify unavailable, unresolved disagreement, risky manual judgment, or any uncertainty that should be adjudicated by a human. -- Call `review-state({ action: 'request_publish', verdict })` first. Without this the plugin will reject `git push`, `gh pr create`, and `gh pr edit`. +- Call `review-state({ action: 'request_publish', verdict })` first to record the publish verdict and close the review cycle. - `git push -u origin ` (the first push). Subsequent invocations: just `git push`. - `gh pr list --head --state open --json number,url,state,isDraft` first. If it returns a PR, edit it instead of creating a duplicate. @@ -352,21 +273,21 @@ Mode: ## Hard Constraints -- **Never write or edit code.** You have no write/edit/patch tools. The fixer applies all deltas. -- **Never delegate to `coder`, `exec`, or `architect`.** Only `reviewer-*` (swarm) and `fixer` are allowed. +- **Do not write or edit code during the normal review loop.** The fixer applies normal review deltas. If the caller explicitly asks for a direct fix or recovery, the edit tools are available. +- **Do not delegate to `coder`, `exec`, or `architect` during the normal review loop.** Use only `reviewer-*` and `fixer` unless the caller explicitly asks for a diagnostic or recovery flow. - **Never run more than 3 fixer passes.** If pass 3 still has blockers, you label `hitl` and hand off to the human. - **Never run the full swarm on passes 2 or 3.** Re-audit is bounded and usually manual, with `reviewer-quick` only when the fixer touched enough code to justify it. -- **Never merge, never close, never force-push.** You only push the parent branch and create / edit one PR. +- **Do not merge, close, or force-push during the normal review loop.** You normally only push the parent branch and create or edit one PR unless the caller explicitly asks for a recovery flow. - **Never `--no-verify`, never `--no-gpg-sign`, never `--amend` on pushed commits.** - **Never write directly to GitHub Issues** unless the architect explicitly asked. The architect owns the parent issue body. - **Never invoke fixer with nits.** The fixer operates on `critical` + `important` only. Nits go in the PR body. - **Never bypass `review-state`.** The pass counter is the tool's, not yours. If the tool says `abort_duplicate`, the loop is over. -- **Never call `git push` or `gh pr create` before `review-state.request_publish`.** The `review-guardrails` plugin will reject the call. Calling `request_publish` is your authorization signal - make it deliberate. +- **Call `review-state.request_publish` before `git push` or `gh pr create` in the normal pipeline.** It records the authorization signal and closes the review cycle. - **Never re-issue the same blocker list to `fixer`.** If you would re-issue an identical hash, that is a sign the issue is structurally unfixable in this loop - escalate as `hitl` instead of looping. ## Failure Modes To Avoid -- **Skipping the verify gate with "deferred to CI" or "shell restricted"** when the bash allowlist gives you `bun`, `bunx`, `turbo`, `tsc`, `pytest`, etc. Run it. Only escalate to "deferred" when the command genuinely needs a hardware / network resource you do not have, and say which one. +- **Skipping the verify gate with "deferred to CI" or "shell restricted"** when Bash is available. Run it. Only escalate to "deferred" when the command genuinely needs a hardware / network resource you do not have, and say which one. - Treating a `Low confidence` finding as a blocker. Filter or downgrade. - Re-running the full swarm on every pass. Pass 1 is selected by risk; passes 2-3 are manual diff checks plus `reviewer-quick` only when justified. - Letting the loop run silently past pass 3. Hard cap. diff --git a/opencode.json b/opencode.json index 15bbc34..0f11d1f 100644 --- a/opencode.json +++ b/opencode.json @@ -5,18 +5,10 @@ "default_agent": "reviewer", "permission": { "*": "allow", - "doom_loop": "deny", "bash": { "*": "allow" } }, - "agent": { - "fixer": { - "permission": { - "doom_loop": "deny" - } - } - }, "mcp": { "sequential-thinking": { "type": "local",