diff --git a/.gitignore b/.gitignore index 3991c41..2d84cd5 100644 --- a/.gitignore +++ b/.gitignore @@ -1 +1,8 @@ -AgentsReport/ +# Ignore all agent-report workspace CONTENTS, but keep per-agent report +# directories seeded (.gitkeep) so sanctioned report writes never need mkdir. +# NOTE: `AgentsReport/` + `!AgentsReport/` would NOT work: un-ignoring the +# directory makes every plain file inside trackable, and git cannot +# re-include files under an ignored parent directory. +AgentsReport/** +!AgentsReport/**/ +!AgentsReport/**/.gitkeep diff --git a/AgentsReport/architect/.gitkeep b/AgentsReport/architect/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/builder/.gitkeep b/AgentsReport/builder/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/designer/.gitkeep b/AgentsReport/designer/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/detective/.gitkeep b/AgentsReport/detective/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/explorer/.gitkeep b/AgentsReport/explorer/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/maintainer/.gitkeep b/AgentsReport/maintainer/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/orchestrator/.gitkeep b/AgentsReport/orchestrator/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/philosopher/.gitkeep b/AgentsReport/philosopher/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/reviewer/.gitkeep b/AgentsReport/reviewer/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/tester/.gitkeep b/AgentsReport/tester/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/toolsmith/.gitkeep b/AgentsReport/toolsmith/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/AgentsReport/writer/.gitkeep b/AgentsReport/writer/.gitkeep new file mode 100644 index 0000000..e69de29 diff --git a/agents/architect.md b/agents/architect.md index eaecccf..86eab62 100644 --- a/agents/architect.md +++ b/agents/architect.md @@ -2,22 +2,38 @@ name: architect description: Evidence-driven architecture and scope decision agent for defining boundaries, ownership, interfaces, and implementation direction mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: edit: "**": deny - "**/AgentsReport/**": allow + "AgentsReport/architect/**": allow bash: "*": deny "git status*": allow "git log*": allow "git diff*": allow "git show*": allow + "git blame*": allow + "git reflog*": allow + "git merge-base*": allow + "git rev-parse*": allow "git branch --list*": allow "git branch -a*": allow "git branch -r*": allow - "git rev-parse*": allow "git ls-files*": allow "git ls-tree*": allow + "head*": allow + "tail*": allow + "wc*": allow + "sort*": allow + "grep*": allow + "rg*": allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/agents/builder.md b/agents/builder.md index 7d47d7d..855f438 100644 --- a/agents/builder.md +++ b/agents/builder.md @@ -2,7 +2,16 @@ name: builder description: Scope-controlled implementation agent for approved changes mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: + edit: allow + bash: allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/agents/designer.md b/agents/designer.md index bd371e7..f85f836 100644 --- a/agents/designer.md +++ b/agents/designer.md @@ -2,10 +2,38 @@ name: designer description: Evidence-driven UI/UX design agent responsible for visual design, interaction patterns, accessibility, and user experience specifications mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: - edit: allow + edit: + "**": deny + "AgentsReport/designer/**": allow bash: "*": deny + "git status*": allow + "git log*": allow + "git diff*": allow + "git show*": allow + "git blame*": allow + "git reflog*": allow + "git merge-base*": allow + "git rev-parse*": allow + "git branch --list*": allow + "git branch -a*": allow + "git branch -r*": allow + "git ls-files*": allow + "git ls-tree*": allow + "head*": allow + "tail*": allow + "wc*": allow + "sort*": allow + "grep*": allow + "rg*": allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/agents/detective.md b/agents/detective.md index 065dcb2..fc987ee 100644 --- a/agents/detective.md +++ b/agents/detective.md @@ -2,9 +2,37 @@ name: detective description: Evidence-first, hypothesis-driven root-cause investigator for technical failures and suspicious behavior mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: - edit: deny - bash: ask + edit: + "**": deny + "AgentsReport/detective/**": allow + bash: + "git status*": allow + "git log*": allow + "git diff*": allow + "git show*": allow + "git blame*": allow + "git reflog*": allow + "git merge-base*": allow + "git rev-parse*": allow + "git branch --list*": allow + "git branch -a*": allow + "git branch -r*": allow + "git ls-files*": allow + "git ls-tree*": allow + "head*": allow + "tail*": allow + "wc*": allow + "sort*": allow + "grep*": allow + "rg*": allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/agents/explorer.md b/agents/explorer.md index c42ecf5..9668c70 100644 --- a/agents/explorer.md +++ b/agents/explorer.md @@ -2,22 +2,38 @@ name: explorer description: Read-only, evidence-first investigator for understanding unfamiliar systems, repositories, and technical problems mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: edit: "**": deny - "**/AgentsReport/**": allow + "AgentsReport/explorer/**": allow bash: "*": deny "git status*": allow "git log*": allow "git diff*": allow "git show*": allow + "git blame*": allow + "git reflog*": allow + "git merge-base*": allow + "git rev-parse*": allow "git branch --list*": allow "git branch -a*": allow "git branch -r*": allow - "git rev-parse*": allow "git ls-files*": allow "git ls-tree*": allow + "head*": allow + "tail*": allow + "wc*": allow + "sort*": allow + "grep*": allow + "rg*": allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/agents/maintainer.md b/agents/maintainer.md index 9ed9f21..e40c4bb 100644 --- a/agents/maintainer.md +++ b/agents/maintainer.md @@ -2,7 +2,16 @@ name: maintainer description: Scope-aware maintenance agent for keeping an existing system consistent with its established standards mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: + edit: allow + bash: allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/agents/orchestrator.md b/agents/orchestrator.md index f833634..20b4e74 100644 --- a/agents/orchestrator.md +++ b/agents/orchestrator.md @@ -2,7 +2,13 @@ name: orchestrator description: Coordination agent that routes work across specialist agents while preserving scope, evidence, and handoff discipline mode: primary +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: + edit: allow + bash: allow task: allow --- diff --git a/agents/philosopher.md b/agents/philosopher.md index 523597a..058e5f4 100644 --- a/agents/philosopher.md +++ b/agents/philosopher.md @@ -2,10 +2,39 @@ name: philosopher description: Evidence-driven discovery agent that finds the purpose, meaning, and soul of a project before any technical work begins mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: - edit: allow + edit: + "**": deny + "philosophy.md": allow + "AgentsReport/philosopher/**": allow bash: "*": deny + "git status*": allow + "git log*": allow + "git diff*": allow + "git show*": allow + "git blame*": allow + "git reflog*": allow + "git merge-base*": allow + "git rev-parse*": allow + "git branch --list*": allow + "git branch -a*": allow + "git branch -r*": allow + "git ls-files*": allow + "git ls-tree*": allow + "head*": allow + "tail*": allow + "wc*": allow + "sort*": allow + "grep*": allow + "rg*": allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/agents/reviewer.md b/agents/reviewer.md index 95cdf54..37c993f 100644 --- a/agents/reviewer.md +++ b/agents/reviewer.md @@ -2,22 +2,38 @@ name: reviewer description: Read-only, adversarial review agent that verifies completed implementations, maintenance changes, and tooling against approved scope and requirements before acceptance mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: edit: "**": deny - "**/AgentsReport/**": allow + "AgentsReport/reviewer/**": allow bash: "*": deny "git status*": allow "git log*": allow "git diff*": allow "git show*": allow + "git blame*": allow + "git reflog*": allow + "git merge-base*": allow + "git rev-parse*": allow "git branch --list*": allow "git branch -a*": allow "git branch -r*": allow - "git rev-parse*": allow "git ls-files*": allow "git ls-tree*": allow + "head*": allow + "tail*": allow + "wc*": allow + "sort*": allow + "grep*": allow + "rg*": allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/agents/tester.md b/agents/tester.md index 35f7da2..9b1d47e 100644 --- a/agents/tester.md +++ b/agents/tester.md @@ -2,9 +2,16 @@ name: tester description: Evidence-driven testing specialist responsible for test strategy, test architecture, test implementation, and quality verification mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: edit: allow bash: allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/agents/toolsmith.md b/agents/toolsmith.md index 93ba845..884385b 100644 --- a/agents/toolsmith.md +++ b/agents/toolsmith.md @@ -2,7 +2,38 @@ name: toolsmith description: Practical automation and tooling agent for turning repeated problems into reliable mechanical prevention mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: + edit: + "**": deny + "AgentsReport/toolsmith/**": allow + bash: + "*": deny + "git status*": allow + "git log*": allow + "git diff*": allow + "git show*": allow + "git blame*": allow + "git reflog*": allow + "git merge-base*": allow + "git rev-parse*": allow + "git branch --list*": allow + "git branch -a*": allow + "git branch -r*": allow + "git ls-files*": allow + "git ls-tree*": allow + "head*": allow + "tail*": allow + "wc*": allow + "sort*": allow + "grep*": allow + "rg*": allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/agents/writer.md b/agents/writer.md index 9f2874e..c4cd057 100644 --- a/agents/writer.md +++ b/agents/writer.md @@ -2,20 +2,40 @@ name: writer description: Evidence-driven documentation specialist responsible for creating technical documentation, API references, user guides, ADRs, and release notes mode: subagent +# NOTE: Bash permission rules apply to EACH command segment independently (tree-sitter split); +# pipelines need every segment allowlisted incl. tails (head/wc/sort/grep/rg). Prefer single commands. +# CAVEAT: an in-session "always allow" approval injects pattern:* allow that overrides these denies +# for every agent until the server restarts. permission: - edit: allow + edit: + "**": deny + "docs/**": allow + "README*": allow + "AgentsReport/writer/**": allow bash: "*": deny "git status*": allow "git log*": allow "git diff*": allow "git show*": allow + "git blame*": allow + "git reflog*": allow + "git merge-base*": allow + "git rev-parse*": allow "git branch --list*": allow "git branch -a*": allow "git branch -r*": allow - "git rev-parse*": allow "git ls-files*": allow "git ls-tree*": allow + "head*": allow + "tail*": allow + "wc*": allow + "sort*": allow + "grep*": allow + "rg*": allow + webfetch: deny + websearch: deny + skill: deny task: deny --- diff --git a/scripts/install.sh b/scripts/install.sh index b601eb5..18b747b 100755 --- a/scripts/install.sh +++ b/scripts/install.sh @@ -77,6 +77,31 @@ if (( copied != EXPECTED_COUNT )); then echo "ERROR: expected $EXPECTED_COUNT agent files, but installed $copied. Aborting." >&2 exit 1 fi + +# Decision-4 gate 1 — byte integrity: every installed file must be a faithful +# copy of its repo source (install is cp -p, no transformation). Copy-all first, +# verify-all after: a failure aborts with a nonzero exit and no success message. +for src_path in "${AGENT_FILES[@]}"; do + name="$(basename "$src_path")" + if ! cmp -s "$src_path" "$TARGET/$name"; then + echo "ERROR: integrity check failed for '$name' ($TARGET/$name differs from $src_path). Aborting." >&2 + exit 1 + fi +done + +# Decision-4 gate 2 — engine semantics: the installed definitions are only +# trustworthy if the target opencode engine matches the documented matching +# rules. Verifier exits: 0 = pass, 1 = engine drift, 2 = no usable runtime. +echo "==> Verifying permission-engine semantics..." +rc=0 +sh "$ROOT/scripts/verify-permission-patterns.sh" || rc=$? +case "$rc" in + 0) ;; + 1) echo "ERROR: ENGINE DRIFT — opencode permission matching does NOT match documented semantics; installed definitions cannot be trusted. Aborting." >&2; exit 1 ;; + 2) echo "ERROR: NO RUNTIME — no opencode binary found (set OPENCODE_BIN to override); install completed but CANNOT be verified. Aborting." >&2; exit 2 ;; + *) echo "ERROR: verifier exited unexpectedly (rc=$rc). Aborting." >&2; exit "$rc" ;; +esac + echo "==> Installed $copied/$EXPECTED_COUNT agents." if (( backed_up > 0 )); then diff --git a/scripts/verify-permission-patterns.sh b/scripts/verify-permission-patterns.sh new file mode 100755 index 0000000..6fba408 --- /dev/null +++ b/scripts/verify-permission-patterns.sh @@ -0,0 +1,27 @@ +#!/bin/sh +# verify-permission-patterns.sh — test loaded opencode permission-engine semantics in a +# THROWAWAY temp HOME. Prints PASS/FAIL per rule; exit 0=all pass, 1=drift, 2=no runtime. +set -u +T="$(mktemp -d)" || exit 2; trap 'rm -rf "$T"' EXIT +# BIN resolution: OPENCODE_BIN env override -> 'opencode' on PATH -> no runtime (exit 2). +BIN="" +if [ -n "${OPENCODE_BIN:-}" ] && [ -r "$OPENCODE_BIN" ]; then + BIN="$OPENCODE_BIN" +elif command -v opencode >/dev/null 2>&1; then + BIN="$(command -v opencode)" +fi +[ -n "$BIN" ] || { echo "FAIL no opencode runtime found (set OPENCODE_BIN or put 'opencode' on PATH)" >&2; exit 2; } +if grep -aqF '.replace(/\*/g,".*").replace(/\?/g,".")' "$BIN"; then + echo "PASS engine-signature present in loaded binary" +else + echo "FAIL engine-signature NOT found — binary changed, re-extract Wildcard.match"; exit 1 +fi +command -v node >/dev/null || { echo "FAIL no node runtime"; exit 2; } +HOME="$T" node -e ' +function m(i,p){if(i)i=i.replaceAll("\\","/");if(p)p=p.replaceAll("\\","/"); +let l=p.replace(/[.+^${}()|[\]\\]/g,"\\$&").replace(/\*/g,".*").replace(/\?/g,"."); +if(l.endsWith(" .*"))l=l.slice(0,-3)+"( .*)?";return new RegExp("^"+l+"$","s").test(i)} +const C=[["**/AgentsReport/**","AgentsReport/toolsmith/x.md",false],["AgentsReport/**","AgentsReport/toolsmith/x.md",true], +["**","AgentsReport/x.md",true],["git status*","git status",true],["*","head",true],["edit","edit",true]]; +let f=0;for(const[p,i,w]of C){const g=m(i,p);g===w||f++;console.log((g===w?"PASS":"FAIL")+" match("+JSON.stringify(i)+","+JSON.stringify(p)+")="+g+" want "+w)} +process.exit(f?1:0)' && echo "RESULT: engine matches documented semantics" || echo "RESULT: DRIFT — re-read binary"