3ec601d4da
The clone's opencode.json inherited the main repo's top-level 'model' field (zai/glm-5) via 'git clone'. The tier2-autonomous agent has its own 'model: minimax-coding-plan/MiniMax-M3' override, so the default agent path was technically correct, but any other agent spawned without an explicit model (or if the user manually switched to build/plan) would have used zai/glm-5 instead of MiniMax-M3. Fix: 1. Add top-level 'model: minimax-coding-plan/MiniMax-M3' to conductor/tier2/opencode.json.fragment. 2. setup_tier2_clone.ps1 merge now overrides 'model' from the fragment (was only overriding agent, permission, default_agent). 3. Added test_config_fragment_has_top_level_model (default-on) to assert the fragment's model field. 4. Added test_setup_script_overrides_model (opt-in TIER2_SANDBOX_TESTS=1) to assert the merge code. All 17 tests pass (14 default-on + 3 opt-in). Verified: re-ran setup against the live clone; opencode.json's top-level 'model' is now minimax-coding-plan/MiniMax-M3.
154 lines
7.9 KiB
PowerShell
154 lines
7.9 KiB
PowerShell
# scripts/tier2/setup_tier2_clone.ps1
|
|
<#
|
|
.SYNOPSIS
|
|
One-time bootstrap for the Tier 2 autonomous sandbox.
|
|
.DESCRIPTION
|
|
Clones the main repo to C:\projects\manual_slop_tier2\, sets origin
|
|
to the main repo's local path, copies the agent/command/opencode.json
|
|
templates, installs the git hooks, creates the app-data temp dir with
|
|
restricted ACLs, and creates a "Tier 2 (Sandboxed)" desktop shortcut.
|
|
Idempotent: re-running updates templates and re-fetches, but does not
|
|
destroy existing feature branches in the clone.
|
|
.PARAMETER WhatIf
|
|
Show what would happen without making changes.
|
|
.PARAMETER MainRepoPath
|
|
Path to the main repo. Default: C:\projects\manual_slop
|
|
.PARAMETER Tier2ClonePath
|
|
Path to the Tier 2 clone. Default: C:\projects\manual_slop_tier2
|
|
#>
|
|
[CmdletBinding(SupportsShouldProcess = $true)]
|
|
param(
|
|
[string]$MainRepoPath = "C:\projects\manual_slop",
|
|
[string]$Tier2ClonePath = "C:\projects\manual_slop_tier2",
|
|
[string]$AppDataDir = "$env:LOCALAPPDATA\manual_slop\tier2"
|
|
)
|
|
|
|
$ErrorActionPreference = "Stop"
|
|
|
|
# Resolve to absolute paths
|
|
$MainRepoPath = (Resolve-Path $MainRepoPath).Path
|
|
$AppDataFailuresDir = "$env:LOCALAPPDATA\manual_slop\tier2_failures"
|
|
|
|
if ($PSCmdlet.ShouldProcess("Bootstrap Tier 2 clone at $Tier2ClonePath")) {
|
|
Write-Host "[tier2-bootstrap] starting bootstrap"
|
|
Write-Host "[tier2-bootstrap] main repo: $MainRepoPath"
|
|
Write-Host "[tier2-bootstrap] tier2 clone: $Tier2ClonePath"
|
|
|
|
# 1. Clone the main repo (if not already present)
|
|
if (-not (Test-Path $Tier2ClonePath)) {
|
|
Write-Host "[tier2-bootstrap] cloning $MainRepoPath -> $Tier2ClonePath"
|
|
git clone $MainRepoPath $Tier2ClonePath
|
|
if ($LASTEXITCODE -ne 0) { throw "git clone failed" }
|
|
} else {
|
|
Write-Host "[tier2-bootstrap] clone already exists, skipping clone"
|
|
}
|
|
|
|
# 2. Set origin to the main repo's local path (if not already)
|
|
Push-Location $Tier2ClonePath
|
|
try {
|
|
$currentOrigin = git remote get-url origin 2>$null
|
|
if ($currentOrigin -ne $MainRepoPath) {
|
|
Write-Host "[tier2-bootstrap] setting origin to $MainRepoPath"
|
|
git remote set-url origin $MainRepoPath
|
|
} else {
|
|
Write-Host "[tier2-bootstrap] origin already set correctly"
|
|
}
|
|
|
|
# 3. Copy templates
|
|
Write-Host "[tier2-bootstrap] copying templates"
|
|
New-Item -ItemType Directory -Force -Path "$Tier2ClonePath\.opencode\agents" | Out-Null
|
|
New-Item -ItemType Directory -Force -Path "$Tier2ClonePath\.opencode\commands" | Out-Null
|
|
Copy-Item -Force "$MainRepoPath\conductor\tier2\agents\tier2-autonomous.md" "$Tier2ClonePath\.opencode\agents\tier2-autonomous.md"
|
|
Copy-Item -Force "$MainRepoPath\conductor\tier2\commands\tier-2-auto-execute.md" "$Tier2ClonePath\.opencode\commands\tier-2-auto-execute.md"
|
|
|
|
# Merge opencode.json.fragment into the clone's opencode.json.
|
|
# The clone inherits a copy of the main repo's opencode.json (via
|
|
# `git clone`), which has top-level `permission.edit: ask` and
|
|
# `permission.bash: ask`. Those would be unsafe in the sandbox: the
|
|
# build/plan default agents could read/write anywhere on disk, and
|
|
# there is no file-system allowlist at the top level. We replace
|
|
# the top-level `permission` with the hardened sandbox version
|
|
# (deny-all + allowlist for the sandbox dirs + the tier2-autonomous
|
|
# agent's permission block). The agent's `permission` overrides the
|
|
# top-level for that agent's tool calls.
|
|
$cloneConfig = "$Tier2ClonePath\opencode.json"
|
|
$fragment = Get-Content "$MainRepoPath\conductor\tier2\opencode.json.fragment" -Raw | ConvertFrom-Json
|
|
if (Test-Path $cloneConfig) {
|
|
$existing = Get-Content $cloneConfig -Raw | ConvertFrom-Json
|
|
if (-not $existing.agent) { $existing | Add-Member -MemberType NoteProperty -Name agent -Value ([PSCustomObject]@{}) }
|
|
$existing.agent | Add-Member -MemberType NoteProperty -Name "tier2-autonomous" -Value $fragment.agent."tier2-autonomous" -Force
|
|
if (-not $existing.permission) { $existing | Add-Member -MemberType NoteProperty -Name permission -Value ([PSCustomObject]@{}) }
|
|
$existing.permission = $fragment.permission
|
|
$existing | Add-Member -MemberType NoteProperty -Name default_agent -Value "tier2-autonomous" -Force
|
|
$existing | Add-Member -MemberType NoteProperty -Name model -Value $fragment.model -Force
|
|
$existing | ConvertTo-Json -Depth 10 | Set-Content $cloneConfig
|
|
} else {
|
|
Copy-Item -Force "$MainRepoPath\conductor\tier2\opencode.json.fragment" $cloneConfig
|
|
$existing = $fragment
|
|
}
|
|
|
|
# Override the MCP server's command + PYTHONPATH to point at the
|
|
# Tier 2 clone's files. The inherited config points to the main
|
|
# repo's scripts/mcp_server.py, which loads the main repo's
|
|
# project_root (C:\projects\manual_slop) and reads the main repo's
|
|
# mcp_paths.toml (which allowlists C:\projects\gencpp). When Tier 2
|
|
# calls manual-slop_read_file on a clone path, the MCP server
|
|
# rejects it with "Allowed base directories are: manual_slop, gencpp".
|
|
# The fix: launch the MCP server from the clone's path with the
|
|
# clone's PYTHONPATH, and replace the clone's mcp_paths.toml with
|
|
# an empty one so the clone's MCP server has no extra_dirs.
|
|
if ($existing.mcp -and $existing.mcp.'manual-slop') {
|
|
$existing.mcp.'manual-slop'.command = @(
|
|
"$env:USERPROFILE\scoop\apps\uv\current\uv.exe",
|
|
"run",
|
|
"python",
|
|
"$Tier2ClonePath\scripts\mcp_server.py"
|
|
)
|
|
$existing.mcp.'manual-slop'.environment.PYTHONPATH = "$Tier2ClonePath\src"
|
|
$existing | ConvertTo-Json -Depth 10 | Set-Content $cloneConfig
|
|
}
|
|
$cloneMcpPaths = "$Tier2ClonePath\mcp_paths.toml"
|
|
@"
|
|
[allowed_paths]
|
|
extra_dirs = []
|
|
"@ | Set-Content -Path $cloneMcpPaths -NoNewline
|
|
Write-Host "[tier2-bootstrap] MCP server pointed at clone; mcp_paths.toml reset to empty extra_dirs"
|
|
|
|
# 4. Install git hooks
|
|
Write-Host "[tier2-bootstrap] installing git hooks"
|
|
Copy-Item -Force "$MainRepoPath\conductor\tier2\githooks\pre-push" "$Tier2ClonePath\.git\hooks\pre-push"
|
|
Copy-Item -Force "$MainRepoPath\conductor\tier2\githooks\post-checkout" "$Tier2ClonePath\.git\hooks\post-checkout"
|
|
|
|
# 5. Create app-data dir with restricted ACLs
|
|
Write-Host "[tier2-bootstrap] creating app-data dir: $AppDataDir"
|
|
New-Item -ItemType Directory -Force -Path $AppDataDir | Out-Null
|
|
New-Item -ItemType Directory -Force -Path $AppDataFailuresDir | Out-Null
|
|
$acl = Get-Acl $AppDataDir
|
|
$acl.SetAccessRuleProtection($true, $false)
|
|
$userRule = New-Object System.Security.AccessControl.FileSystemAccessRule(
|
|
$env:USERNAME, "FullControl", "ContainerInherit,ObjectInherit", "None", "Allow"
|
|
)
|
|
$acl.AddAccessRule($userRule)
|
|
Set-Acl $AppDataDir $acl
|
|
Set-Acl $AppDataFailuresDir (Get-Acl $AppDataDir)
|
|
|
|
# 6. Create desktop shortcut
|
|
Write-Host "[tier2-bootstrap] creating desktop shortcut"
|
|
$shell = New-Object -ComObject WScript.Shell
|
|
$shortcut = $shell.CreateShortcut("$env:USERPROFILE\Desktop\Tier 2 (Sandboxed).lnk")
|
|
$shortcut.TargetPath = "pwsh.exe"
|
|
$shortcut.Arguments = "-File `"$MainRepoPath\scripts\tier2\run_tier2_sandboxed.ps1`""
|
|
$shortcut.WorkingDirectory = $Tier2ClonePath
|
|
$shortcut.Description = "Open OpenCode in the Tier 2 sandboxed clone"
|
|
$shortcut.Save()
|
|
|
|
} finally {
|
|
Pop-Location
|
|
}
|
|
|
|
Write-Host "[tier2-bootstrap] done"
|
|
Write-Host "[tier2-bootstrap] next steps:"
|
|
Write-Host "[tier2-bootstrap] 1. Double-click 'Tier 2 (Sandboxed)' on your desktop"
|
|
Write-Host "[tier2-bootstrap] 2. Type: /tier-2-auto-execute <track-name>"
|
|
}
|