28 KiB
Track Specification: Tier 2 Autonomous Sandbox (unattended track execution with bounded blast radius)
Track ID: tier2_autonomous_sandbox_20260616
Status: Planned (spec pending user review)
Priority: A (user-blocking; eliminates the manual permission: ask bottleneck for well-regularized tracks)
Owner: Tier 2 Tech Lead (per conductor/workflow.md)
Type: feature (meta-tooling — adds a new execution mode to the existing MMA workflow, not to the Manual Slop app itself)
Scope: ~7 new files in main repo + 1 sibling clone at C:\projects\manual_slop_tier2\ (one-time bootstrap)
Parent tracks: opencode_config_overhaul_20260310 (shipped; established the agent profile scaffolding this track extends)
Sibling tracks: none (independent)
Note on effort estimates: this spec measures effort by scope only (N files, M sites, N tests). The user / Tier 2 agent decides the actual pacing.
0. TL;DR
This track adds an unattended execution mode for Tier 2: you open
OpenCode in a sibling clone (C:\projects\manual_slop_tier2\), type
/tier-2-auto-execute <track-name>, and Tier 2 runs the track
autonomously — no permission: ask prompts — while a 3-layer
defense-in-depth enforcement stack prevents it from touching the
filesystem outside its clone + an app-data temp dir, and from running
destructive git operations (git restore, git push*, git checkout,
git reset). If Tier 2 can't make progress (3 red-phase failures, 3
green-phase failures, or 30 minutes with no commit/green), it stops
early, writes a failure report, and notifies you. You review the
feature branch with Tier 1 in the main repo, then merge.
Scope: 7 new files in main repo (mostly config + scripts + 1 small Python module), 4 new test files, 1 PowerShell wrapper, 1 bootstrap script, 1 user guide. ~600 lines of new code.
1. Overview
1.1 The State Before This Track (as of 88e44d1c)
The current OpenCode configuration has these properties:
- One repo, two modes via agent profile.
opencode.json:11setsdefault_agent: "tier2-tech-lead". Tier 1 and Tier 2 are distinguished by which agent profile the user selects in the OpenCode session, not by which directory they're in. - Permission bottleneck on Tier 2.
.opencode/agents/tier2-tech-lead.md:6-9setspermission: { edit: "ask", bash: "ask", 'manual-slop_*': allow }. Everyeditand everybashcall from Tier 2 prompts the user for approval. For well-regularized tracks (TDD red/green/refactor with atomic per-task commits, e.g., the upcomingresult_migration_*tracks), this is noise — the user has already pre-approved the track plan, and the per-task approval doesn't add safety, it just adds 50+ clicks per track. - No filesystem boundary enforcement. Tier 2 has the same
filesystem access as the user. There is nothing preventing Tier 2 (or
a delegated Tier 3 worker) from reading
C:\Users\Ed\.aws\credentialsor writing to a different project entirely. - No git ban enforcement. Nothing prevents Tier 2 from running
git restore,git push origin,git checkout -- <file>, orgit reset --hard. These are the four operations the user has called out as "destructive to its progress or affects the origin server" in the original ask. - No failure threshold / give-up mechanism. A stuck Tier 2 runs until the user notices or the agent self-terminates. There is no "3 red-phase attempts without progress → stop and write a report" guardrail.
- One OpenCode session at a time. The main repo's OpenCode session is the only execution environment. Tier 2 cannot run in parallel with Tier 1 review.
1.2 The Goal
Add a second execution mode for Tier 2 that is:
- Autonomous — no
permission: askprompts foreditorbash - Sandboxed — file access is restricted to the Tier 2 clone + an app-data temp dir, enforced at 3 independent layers (OpenCode permission system, Windows restricted token + ACLs, git hooks)
- Bounded — a one-shot run with a failure threshold; stuck runs stop early and write a report
- Reviewable — the run produces a feature branch in the clone; the user fetches it back to main and reviews with Tier 1
- Opt-in to the app's test suite — the sandbox / bootstrap / smoke
tests are env-var-gated so the default
uv run pytestrun stays app-focused and fast
The main repo (the Tier 1 control plane) is not modified —
opencode.json stays the same (Tier 1 still has permission: ask),
and the existing MMA agents stay the same.
1.3 What the User Experiences
One-time bootstrap (the user runs once):
cd C:\projects\manual_slop
pwsh scripts/tier2/setup_tier2_clone.ps1
Per-track invocation (the user's normal flow from now on):
cd C:\projects\manual_slop_tier2- Open OpenCode in that directory (the "Tier 2 Sandboxed" desktop shortcut the bootstrap created)
- In the OpenCode session, type:
/tier-2-auto-execute result_migration_review_pass - Tier 2 fetches the spec, creates
tier2/result_migration_review_passbranch, runs the plan, commits per task - On success: prints a summary. On give-up: writes a failure report and prints its path.
cd C:\projects\manual_slop(back to main)git fetch C:/projects/manual_slop_tier2 tier2/result_migration_review_pass- Review the diff with Tier 1 (interactive)
git merge --no-ff tier2/result_migration_review_passto main
No permission: ask prompts in step 4. If a Tier 2 tool call
attempts a banned operation, the OpenCode permission system denies it;
if a delegated Tier 3 worker tries to escape via a Python subprocess,
the Windows ACLs deny it; if a git push somehow slips through, the
pre-push hook blocks it. Three independent layers, all enforcing the
same ban list.
2. Current State Audit (as of 88e44d1c)
2.1 Already Implemented (DO NOT re-implement)
- OpenCode agent profile scaffolding —
.opencode/agents/tier{1,2,3,4}-*.md:1-200and theopencode.json:1-50config file. Thetier2-autonomousagent profile this track adds follows the same pattern. - Slash command pattern —
.opencode/commands/conductor-implement.md:1-100is the existing pattern for slash commands. Thetier-2-auto-execute.mdcommand follows the same structure (front matteragent:anddescription:, markdown body with protocol). - Conductor track convention —
conductor/tracks/<id>/{spec,plan}.mdandmetadata.jsonperconductor/workflow.md"State.toml Template" + "Track Dependencies and Execution Order" sections. This track's artifacts follow that pattern. - Project-level test opt-in convention — the
live_guifixture intests/conftest.pyand the existing env-var-gated tests (e.g., theRUN_LIVE_GUI=1pattern intests/test_live_*.py). TheTIER2_SANDBOX_TESTS=1opt-in gate for this track's sandbox tests follows the same shape. - PowerShell-based tooling —
scripts/already contains PowerShell-adjacent Python scripts. The new wrapper is a pure PowerShell script, consistent withpywin32-based operations on Windows. scripts/audit_*.pypattern — the 4 existing audit scripts (audit_exception_handling.py,audit_weak_types.py,audit_main_thread_imports.py,audit_no_models_config_io.py) are the project's enforcement mechanism. This track does not introduce a new audit (the failcount thresholds are TOML-config, not statically checkable), but follows thescripts/audit_<name>.pynaming for any future addition.
2.2 Gaps to Fill (This Track's Scope)
Gap 1: A second clone as the Tier 2 execution environment.
The main repo (C:\projects\manual_slop\) currently doubles as both
the Tier 1 control plane and the Tier 2 execution environment. The
fix is a sibling clone at C:\projects\manual_slop_tier2\ with
origin set to the main repo's local path (no remote). The clone is
where the feature branch lives; the user fetches the branch back into
main for review.
Gap 2: A tier2-autonomous agent profile with deny rules.
The existing tier2-tech-lead agent has permission: ask for edit
and bash. The fix is a new tier2-autonomous agent profile (in the
Tier 2 clone's opencode.json) with:
permission.edit: allowpermission.bash: { "*": "allow", "git push*": "deny", "git checkout*": "deny", "git restore*": "deny", "git reset*": "deny" }permission.read/permission.writerestricted to the Tier 2 clone +C:\Users\Ed\AppData\Local\manual_slop\tier2\
Gap 3: A sandboxed launcher (Windows restricted token + ACLs).
OpenCode's permission system is process-level. A determined Tier 3
worker calling os.system("...") from a delegated Python script
could in principle bypass OpenCode. The fix is a PowerShell wrapper
that:
- Acquires a Windows restricted token (drops
SeBackupPrivilege,SeRestorePrivilege,SeTakeOwnershipPrivilege,SeDebugPrivilege,SeLoadDriverPrivilege) - Sets explicit ACLs on the Tier 2 clone + app-data temp dir (allow the restricted token, deny everything else)
- Wraps the process tree in a Job Object (no breakaway)
- Launches OpenCode + the MCP server under the restricted token via
CreateProcessWithTokenW
Gap 4: A tier-2-auto-execute slash command.
The existing slash commands are conductor-style ("start
implementation", "create track"). The new slash command takes a
<track-name> argument, fetches the spec from origin/main, creates
a tier2/<track-name> branch via git switch -c (NOT git checkout),
runs the plan via Tier 2, monitors the failcount, and reports back.
Gap 5: A failure threshold + give-up mechanism (failcount.py).
The current Tier 2 has no built-in "I can't make progress" detection. A stuck agent burns tokens until the user notices. The fix is a pure Python module that tracks three orthogonal signals:
red_phase_failures(3 = give up)green_phase_failures(3 = give up)no_progress_minutes(30 = give up)
Whichever signal hits its threshold first triggers give-up. The module is pure logic, fully unit-testable, with a TOML config for threshold overrides.
Gap 6: A failure report writer + flag file + notification.
When give-up fires, the system needs to:
- Write a markdown report to
C:\Users\Ed\AppData\Local\manual_slop\tier2_failures\<track>_<utc-timestamp>.mdwith: header, tasks completed, current task state, last 3 failures, failcount state, git log, recommendation - Create a
.STOPPEDflag file alongside the report - Print a clear "TRACK ABORTED" banner in the OpenCode session with the report path
- Optionally: Windows toast notification (opt-in via
--toastflag)
Gap 7: Git hooks as defense-in-depth (Layer 3).
The OpenCode permission system is the primary enforcement for git bans.
A pre-push hook (pre-push in the clone's .git/hooks/) is the
backup that catches git push origin* even if the OpenCode deny rule
is somehow misconfigured. A post-checkout hook logs any checkout of
tracked files to a detection log.
Gap 8: A user guide for bootstrap + invocation + manual verification.
The user needs to know:
- How to run the bootstrap once
- How to invoke the slash command
- What the failure report looks like
- How to review and merge the feature branch
- How to manually verify the sandbox blocks the banned operations
3. Goals
- Eliminate the
permission: askbottleneck for well-regularized tracks. The user clicks zero times during a normal Tier 2 run (excluding the "did Tier 2 give up?" check at the end). - Enforce the 4 hard git bans (
git restore,git push*,git checkout,git reset) at 3 independent layers (OpenCode, Windows OS, git hooks). A bypass of one layer is caught by another. - Enforce the filesystem boundary (Tier 2 clone + app-data temp only) at 2 independent layers (OpenCode path allowlist, Windows ACLs). Even a delegated Python subprocess can't read outside the allowlist.
- Bound the blast radius with a failure threshold. A stuck Tier 2 stops within ~30 minutes and writes a report, instead of running indefinitely.
- Keep the default test run app-focused. All sandbox/bootstrap/
smoke tests are env-var-gated;
uv run pytestwith no env vars stays fast and never touches the Windows ACL subsystem. - Keep Tier 1 unchanged. The main repo's
opencode.jsonis not modified. Tier 1 retains itspermission: askworkflow.
4. Functional Requirements
4.1 Bootstrap (one-time, user-driven)
FR1.1: scripts/tier2/setup_tier2_clone.ps1 (new) clones the
main repo to C:\projects\manual_slop_tier2\, sets
origin = C:\projects\manual_slop, copies the agent/command/
opencode.json templates to the clone, installs the git hooks into
the clone's .git/hooks/, creates the app-data temp dir
C:\Users\Ed\AppData\Local\manual_slop\tier2\ with restricted ACLs,
and creates a "Tier 2 (Sandboxed)" desktop shortcut.
FR1.2: The bootstrap is idempotent — re-running it does not
destroy an existing clone's feature branches (it git fetch origin
and pulls the latest templates, but does not git reset the clone).
FR1.3: The bootstrap dry-run mode (-WhatIf) shows what would
happen without making changes. Required for safety.
4.2 The tier2-autonomous agent profile
FR2.1: .opencode/agents/tier2-autonomous.md (template) in main
repo; copied to Tier 2 clone during bootstrap. Defines the
autonomous-mode agent with the deny rules in §2.2 Gap 2.
FR2.2: The agent's temperature: 0.4 (matches Tier 2 Tech Lead).
The agent uses git switch -c <branch> for new branches and
git switch <branch> for switching — git checkout is banned
project-wide.
FR2.3: The agent prompt includes the failcount monitoring
contract: "After each task commit, check
<app-data>/tier2/<track>/state.json via the failcount module. If
should_give_up returns true, write the failure report and stop."
4.3 The sandboxed launcher
FR3.1: scripts/tier2/run_tier2_sandboxed.ps1 (new) is the
entry point that opens OpenCode in the Tier 2 clone under a
restricted token.
FR3.2: The wrapper acquires a restricted token via .NET
(CreateRestrictedToken), sets ACLs on the Tier 2 clone + app-data
dir to grant the restricted token read/write, wraps the process
tree in a Job Object, and launches OpenCode + the MCP server under
the restricted token via CreateProcessWithTokenW.
FR3.3: The wrapper is the target of the "Tier 2 (Sandboxed)"
desktop shortcut created during bootstrap. Right-click → Properties
shows the command: pwsh -File C:\projects\manual_slop\scripts\tier2\run_tier2_sandboxed.ps1.
4.4 The slash command
FR4.1: .opencode/commands/tier-2-auto-execute.md (template) in
main repo; copied to Tier 2 clone during bootstrap. Takes a
required <track-name> argument.
FR4.2: The slash command:
- Reads
conductor/tracks/<track-name>/spec.md+plan.mdfrom the current branch (after agit fetch origin main) - Creates a
tier2/<track-name>branch viagit switch -c tier2/<track-name> origin/main - Initializes the failcount state file at
<app-data>/tier2/<track-name>/state.json - Delegates the plan to the tier2-autonomous agent
- After each task commit, checks failcount; on give-up, writes the report and stops
- On success, prints a summary (branch name, N commits, M tasks)
FR4.3: The slash command's protocol is duplicated in a CLI
entry point (scripts/tier2/run_track.py) so the smoke e2e test
can invoke the same logic without spinning up an OpenCode session.
FR4.4: The slash command supports --resume to continue a
previously-give-up track from the last completed task (state is in
the state.json file). Default behavior: refuse to resume, ask for
explicit confirmation.
4.5 The failcount module
FR5.1: scripts/tier2/failcount.py (new) is a pure-Python module
with no external deps. Exposes:
class FailcountState— the signal state dataclassclass FailcountConfig— threshold loader (from TOML or defaults)def should_give_up(state: FailcountState, config: FailcountConfig, now: datetime) -> Result[bool, ErrorInfo]def record_red_failure(state: FailcountState) -> FailcountStatedef record_green_failure(state: FailcountState) -> FailcountStatedef record_green_success(state: FailcountState, now: datetime) -> FailcountState(resets no_progress)def record_commit(state: FailcountState, now: datetime) -> FailcountState(resets no_progress)def to_dict(state) -> dict,def from_dict(d) -> FailcountStatedef load_state(track_name: str) -> Result[FailcountState, ErrorInfo]def save_state(track_name: str, state: FailcountState) -> Result[None, ErrorInfo]
FR5.2: Default thresholds (override via failcount.toml):
red_phase_threshold: 3green_phase_threshold: 3no_progress_minutes: 30
FR5.3: should_give_up returns True if ANY signal hits its
threshold. The now parameter is injectable for testing.
FR5.4: record_green_success and record_commit reset the
no_progress_minutes timer. They do NOT reset the red/green
failure counters (those only reset on the next progress signal of
the same type — e.g., a red failure is reset by a green test that
eventually passes).
4.6 The failure report writer
FR6.1: scripts/tier2/write_report.py (new) takes a track name,
branch name, state, and a list of TaskResult records, and writes
the markdown report to
C:\Users\Ed\AppData\Local\manual_slop\tier2_failures\<track>_<utc-timestamp>.md.
FR6.2: The report contains the 7 sections in order:
- Header (track, branch, started-at, stopped-at, duration, give-up signal)
- Tasks completed (list with task IDs, commit SHAs, summaries)
- Current task state (where it stopped: task ID, phase, worker output, test failure)
- Last 3 failures (truncated to 50 lines, full output in
..._full.log) - Failcount state at give-up
- Git state (
git log --oneline tier2/<track> ^origin/main) - Recommendation (heuristic-based: "track too complex", "spec needs clearer plan", "external dependency missing", "review carefully")
FR6.3: A .STOPPED flag file is created at
C:\Users\Ed\AppData\Local\manual_slop\tier2_failures\<track>.STOPPED.
FR6.4: The report writer returns the report path on success
(via Result[str, ErrorInfo]).
4.7 The git hooks (Layer 3)
FR7.1: conductor/tier2/githooks/pre-push (template) is a
shell/PowerShell script that refuses git push invocations to any
remote. The script returns exit code 1 with the message
"Tier 2 autonomous mode: git push is disabled. Push the branch
manually from the main repo after review."
FR7.2: conductor/tier2/githooks/post-checkout (template) is a
detection-only hook that logs any checkout of tracked files to
C:\Users\Ed\AppData\Local\manual_slop\tier2\tier2_checkout_log.txt
with a timestamp, the commit hash, and the affected paths.
FR7.3: The bootstrap script copies both hooks to the Tier 2
clone's .git/hooks/ and chmod +x (on Linux/WSL) or sets the
executable bit via icacls (on Windows).
4.8 The user guide
FR8.1: docs/guide_tier2_autonomous.md (new) covers:
- Why this exists (the
permission: askbottleneck) - One-time bootstrap procedure (with
-WhatIfinstructions) - Per-track invocation procedure
- The slash command arguments (
<track-name>,--resume,--toast) - The failure report layout (with screenshot/example)
- How to review and merge the feature branch
- The "Verify the sandbox" checklist (manual verification)
- Troubleshooting (common errors: origin not set, hooks not executable, failcount.toml missing)
FR8.2: The guide includes a "Verify the sandbox" section that walks the user through attempting each banned operation manually and confirming the denial. This is the user-driven checklist from the design.
4.9 The test suite (opt-in)
FR9.1: tests/test_failcount.py (new) — default-on. Unit
tests for the failure threshold module. The full test inventory:
test_initial_state_zerotest_red_phase_failure_incrementstest_green_success_resets_red_countertest_green_phase_failure_incrementstest_no_progress_advancestest_no_progress_resets_on_committest_no_progress_resets_on_greentest_threshold_fires_at_threetest_threshold_does_not_fire_at_twotest_multi_signal_independencetest_any_signal_triggerstest_state_persistence_round_triptest_configurable_thresholds
Target: 100% line + branch coverage on failcount.py.
FR9.2: tests/test_tier2_slash_command_spec.py (new) — default-on.
Loads the slash command markdown, verifies its protocol contract
(argument parsing, git commands, failcount check, report writing).
FR9.3: tests/test_tier2_setup_bootstrap.py (new) — opt-in
(TIER2_SANDBOX_TESTS=1). Runs setup_tier2_clone.ps1 against a
fixture workspace, verifies the side effects (clone exists, origin
set, templates copied, hooks installed, app-data dir created with
ACLs).
FR9.4: tests/test_tier2_sandbox_enforcement.py (new) —
opt-in (TIER2_SANDBOX_TESTS=1). The critical test: spawns the
wrapper in a subprocess, inside the sandboxed context attempts
each banned operation, verifies each is denied.
FR9.5: tests/test_tier2_report_writer.py (new) — opt-in
(TIER2_SANDBOX_TESTS=1). Invokes failcount until give-up,
verifies the report file is created at the right path with the
right 7 sections.
FR9.6: tests/test_tier2_smoke_e2e.py (new) — opt-in
(TIER2_SANDBOX_TESTS=1 TIER2_SMOKE=1). Runs the full pipeline
against a fixture workspace: bootstrap → invoke the CLI entry
point → verify the feature branch exists with 1 commit → verify
the report file is NOT created (success path).
5. Non-Functional Requirements
NFR1. Performance: the failcount module adds <1ms per check. The slash command's protocol adds <500ms to a typical Tier 2 task (spec fetch + branch creation + state init).
NFR2. Reliability: the failcount state is persisted after every
commit. A killed run can be resumed (or refused to resume) on the
next invocation. The state file uses atomic write (write to
state.json.tmp + os.replace) to survive crashes mid-write.
NFR3. Security:
- The 4 git bans are enforced at 3 independent layers (OpenCode permission system, Windows OS-level via restricted token, git hooks). A bypass of one layer is caught by another.
- The filesystem boundary is enforced at 2 independent layers (OpenCode path allowlist, Windows ACLs).
- The Tier 2 process tree is wrapped in a Job Object that prevents child process escape.
NFR4. Testability:
- The failcount module is pure logic, 100% unit-testable without any infrastructure.
- The slash command's protocol is duplicated in
scripts/tier2/run_track.py(CLI entry point) so the smoke e2e test runs without an OpenCode session. - All sandbox / bootstrap / smoke tests are env-var-gated
(
TIER2_SANDBOX_TESTS=1,TIER2_SMOKE=1).
NFR5. Auditability: every Tier 2 run writes to
C:\Users\Ed\AppData\Local\manual_slop\tier2\<track>\state.json
and (on give-up) C:\Users\Ed\AppData\Local\manual_slop\tier2_failures\<track>_<timestamp>.md.
The user can inspect the state at any time.
NFR6. UX: the user clicks zero times during a normal Tier 2 run. The "did Tier 2 give up?" check is passive (an OpenCode banner, an optional Windows toast, and a flag file the user can check on next Tier 1 session start).
NFR7. Backward compatibility: the main repo's opencode.json
is not modified. Tier 1 retains its permission: ask workflow.
The new agent profile (tier2-autonomous) is in the Tier 2 clone
only. The new slash command is in the Tier 2 clone only.
6. Architecture Reference
This track's design follows these existing patterns:
docs/guide_architecture.md§"Threading model" — the Tier 2 process tree runs in its own Job Object, isolated from the user's main session.docs/guide_mma.md§"Tier 2/3/4 lifecycles" — the Tier 2 Tech Lead's existing delegation patterns (Task tool to@tier3-worker,@tier4-qa) are preserved in the autonomous mode.docs/guide_meta_boundary.md— this track is squarely in the "Meta-Tooling" environment (it builds execution infrastructure for the agents), not the "Application" environment. No changes tosrc/*.py.docs/guide_testing.md§"Authoring robust live_gui tests"- the
live_guisession-scoped pattern — the smoke e2e test follows the same opt-in env-var-gated pattern.
- the
conductor/code_styleguides/python.md— 1-space indentation, CRLF line endings, no comments, strict type hints. All new Python code in this track follows this styleguide.conductor/code_styleguides/error_handling.md— the failcount module usesResult[T, ErrorInfo]per the convention (the 3 refactored baseline files use it; the convention is being rolled out across the codebase perdata_oriented_error_handling_20260606+ the upcomingresult_migration_20260616sub-tracks).
This track's NEW patterns (the contribution to the codebase):
- Sibling clone as execution mode switch — opening OpenCode in
a different directory IS the mode switch (no
mode:flag inopencode.json, no env var, just a directory). - 3-layer enforcement stack — OpenCode permission system +
Windows restricted token + git hooks. Documented in
docs/guide_tier2_autonomous.md(this track's new guide). - Bounded autonomous run with fail-loud — the failcount module is a general-purpose "I'm stuck" detector, applicable to any future autonomous run (not just Tier 2). The pattern is reusable for any sub-agent that has a contract to follow.
7. Out of Scope
- No changes to the Manual Slop app (
src/*.py). This is meta-tooling, not the app. The 4 audit scripts (audit_exception_handling.py,audit_weak_types.py,audit_main_thread_imports.py,audit_no_models_config_io.py) are not modified. - No changes to the main repo's
opencode.jsonor MMA agent profiles. The newtier2-autonomousprofile lives in the Tier 2 clone only. - No new top-level
src/<thing>.pyfiles. Per the file-naming convention (AGENTS.md§"File Size and Naming Convention"), the new code is inscripts/tier2/,conductor/tier2/, andtests/(all namespace-isolated by directory). - No changes to existing tracks or in-flight work. The
result_migration_20260616umbrella track, thedata_oriented_error_handling_20260606track, and theexception_handling_audit_20260616track are not affected. - No new audit script. The failcount thresholds are TOML config,
not statically checkable. If a future track adds a checkable
convention (e.g., "all CLI entry points must use Result[T]"),
the new audit script should follow the
scripts/audit_<name>.pypattern from the existing 4. - No WSL2 / Docker / Windows Sandbox variants. The user approved Approach 1 (OpenCode + Windows restricted token + git hooks, all native Windows). WSL2 was considered and deferred; the failure to run Dear PyGui/ImGui tests in WSL2 was the deciding factor.
- No parallel Tier 2 runs. The Tier 2 clone is a single workspace. Two parallel Tier 2 runs would conflict on the feature branch. If parallel runs become a need, that's a follow-up track.
- No
git pushto non-origin remotes. Even though the deny rule isgit push*(any push), the practical use case is "Tier 2 doesn't push at all; the user pushes after review." Adding a "push to a tier2-remote bare dir" workflow is a follow-up if needed. - No automated review of the feature branch. Tier 1 reviewing Tier 2's branch is a future track (out of scope here).
Spec ends. The implementation plan (plan.md + metadata.json)
will be written by the writing-plans skill in the next phase, after
the user reviews this spec.