@@ -716,6 +716,108 @@ console.log('ok');
716716 fi
717717fi
718718
719+ # --- v0.10.1 Per-agent permission sandboxing. May-2026 community-patterns
720+ # research: 9/15 surveyed opencode.json files use agent.<name>.permission.write
721+ # to scope which paths each agent can touch — test-writer locked to test files,
722+ # docs locked to .md, etc. Maps directly onto the wizard's SDLC steps.
723+ # configure-backend exposes the two highest-signal sandboxes as boolean flags
724+ # that inject canonical permission blocks; users wanting custom glob patterns
725+ # edit opencode.json directly.
726+
727+ # --- T35: --sandbox-test-writer injects agent.test-writer.permission.write
728+ # restricting writes to test/spec files only
729+ if [ -x " $CONFIG " ]; then
730+ T=" $TMP_ROOT /t35" ; mkdir -p " $T "
731+ (cd " $T " && " $CONFIG " \
732+ --tier private_local --provider ollama --model qwen3-coder:30b \
733+ --sandbox-test-writer > /dev/null 2>&1 ) || true
734+ if [ -f " $T /opencode.json" ]; then
735+ ok=" $( node -e "
736+ const j=require('$T /opencode.json');
737+ const tw=j.agent && j.agent['test-writer'];
738+ if(!tw||!tw.permission||!tw.permission.write){console.log('missing-test-writer-perm');process.exit(1)}
739+ const w=tw.permission.write;
740+ if(w['**/*.test.*']!=='allow'){console.log('missing-test-allow');process.exit(1)}
741+ if(w['**/*.spec.*']!=='allow'){console.log('missing-spec-allow');process.exit(1)}
742+ if(w['*']!=='deny'){console.log('missing-default-deny');process.exit(1)}
743+ console.log('ok');
744+ " 2> /dev/null || echo ' failed' ) "
745+ if [ " $ok " = " ok" ]; then
746+ pass " --sandbox-test-writer injects canonical permission.write block"
747+ else
748+ fail " T35 — $ok "
749+ fi
750+ fi
751+ fi
752+
753+ # --- T36: --sandbox-docs injects agent.docs.permission.write
754+ # restricting writes to .md only
755+ if [ -x " $CONFIG " ]; then
756+ T=" $TMP_ROOT /t36" ; mkdir -p " $T "
757+ (cd " $T " && " $CONFIG " \
758+ --tier private_local --provider ollama --model qwen3-coder:30b \
759+ --sandbox-docs > /dev/null 2>&1 ) || true
760+ if [ -f " $T /opencode.json" ]; then
761+ ok=" $( node -e "
762+ const j=require('$T /opencode.json');
763+ const d=j.agent && j.agent.docs;
764+ if(!d||!d.permission||!d.permission.write){console.log('missing-docs-perm');process.exit(1)}
765+ const w=d.permission.write;
766+ if(w['**/*.md']!=='allow'){console.log('missing-md-allow');process.exit(1)}
767+ if(w['*']!=='deny'){console.log('missing-default-deny');process.exit(1)}
768+ console.log('ok');
769+ " 2> /dev/null || echo ' failed' ) "
770+ if [ " $ok " = " ok" ]; then
771+ pass " --sandbox-docs injects canonical permission.write block"
772+ else
773+ fail " T36 — $ok "
774+ fi
775+ fi
776+ fi
777+
778+ # --- T37: both sandboxes compose, plus Mixed-Mode reviewer — full v0.10.x
779+ # hybrid in one invocation
780+ if [ -x " $CONFIG " ]; then
781+ T=" $TMP_ROOT /t37" ; mkdir -p " $T "
782+ (cd " $T " && " $CONFIG " \
783+ --tier proprietary --provider anthropic --model claude-opus-4-7 \
784+ --reviewer-tier hosted_oss --reviewer-provider cerebras --reviewer-model gpt-oss-120b \
785+ --sandbox-test-writer --sandbox-docs > /dev/null 2>&1 ) || true
786+ if [ -f " $T /opencode.json" ]; then
787+ ok=" $( node -e "
788+ const j=require('$T /opencode.json');
789+ if(j.agent.review.model!=='cerebras/gpt-oss-120b'){console.log('reviewer-wrong');process.exit(1)}
790+ if(!j.agent['test-writer'].permission.write['**/*.test.*']){console.log('missing-tw-sandbox');process.exit(1)}
791+ if(!j.agent.docs.permission.write['**/*.md']){console.log('missing-docs-sandbox');process.exit(1)}
792+ console.log('ok');
793+ " 2> /dev/null || echo ' failed' ) "
794+ if [ " $ok " = " ok" ]; then
795+ pass " v0.10.x hybrid: reviewer + test-writer sandbox + docs sandbox compose"
796+ else
797+ fail " T37 — $ok "
798+ fi
799+ fi
800+ fi
801+
802+ # --- T38: sandbox flags absent → no agent.test-writer / agent.docs blocks
803+ # (regression guard — sandbox is opt-in only)
804+ if [ -x " $CONFIG " ]; then
805+ T=" $TMP_ROOT /t38" ; mkdir -p " $T "
806+ (cd " $T " && " $CONFIG " --tier private_local --provider ollama --model qwen3-coder:30b > /dev/null 2>&1 ) || true
807+ if [ -f " $T /opencode.json" ]; then
808+ ok=" $( node -e "
809+ const j=require('$T /opencode.json');
810+ if(j.agent && (j.agent['test-writer']||j.agent.docs)){console.log('unexpected-agent-block');process.exit(1)}
811+ console.log('ok');
812+ " 2> /dev/null || echo ' failed' ) "
813+ if [ " $ok " = " ok" ]; then
814+ pass " no sandbox flags → no test-writer/docs agent blocks (opt-in only)"
815+ else
816+ fail " T38 — $ok "
817+ fi
818+ fi
819+ fi
820+
719821echo " "
720822echo " === Results: $PASS passed, $FAIL failed ==="
721823[ " $FAIL " -eq 0 ] || exit 1
0 commit comments