15 KiB
Track Plan: Tier 2 Sandbox - Move State/Failures Off AppData
Goal: move failcount state and failure-report locations inside the Tier 2 clone; remove all AppData references from Tier 2 conventions, permissions, scripts, docs, and tests. Scope: 11 source files + 3 test files + 1 doc + 1 workflow.md section + 1 .gitignore. Convention: 1-space Python indentation. CRLF where the file is already CRLF (do not normalize).
Phase 1: Move the default state and failure-report paths
Focus: change the Python defaults so load/save use scripts/tier2/state/... and scripts/tier2/failures/... when no env-var override is set.
Task 1.1: Update scripts/tier2/failcount.py:_state_dir default
- WHERE:
scripts/tier2/failcount.py:117-123(the_state_dir(track_name)function). - WHAT: change the default
basefromr"C:\Users\Ed\AppData\Local\manual_slop\tier2"toPath.cwd() / "scripts" / "tier2" / "state"(computed when the function is called;Pathimport already present at line 11). - HOW: rewrite the function as:
def _state_dir(track_name: str) -> Path: base_str = os.environ.get("TIER2_STATE_DIR") if base_str: return Path(base_str) / track_name return Path.cwd() / "scripts" / "tier2" / "state" / track_name - SAFETY: preserve the env-var escape hatch (
TIER2_STATE_DIR); preserve thePathreturn type. The function has no other callers. - COMMIT:
fix(tier2): move failcount state default inside Tier 2 clone (scripts/tier2/state/)
Task 1.2: Update scripts/tier2/write_report.py:_failures_dir default
- WHERE:
scripts/tier2/write_report.py:20-23(the_failures_dir()function). - WHAT: change the default from
r"C:\Users\Ed\AppData\Local\manual_slop\tier2_failures"toPath.cwd() / "scripts" / "tier2" / "failures". - HOW: rewrite the function as:
def _failures_dir() -> Path: base_str = os.environ.get("TIER2_FAILURES_DIR") if base_str: return Path(base_str) return Path.cwd() / "scripts" / "tier2" / "failures" - SAFETY: preserve
TIER2_FAILURES_DIRenv-var override; preserve thePathreturn type. Callers arecompute_report_path,compute_stopped_flag_path, andwrite_failure_report(all in the same file). - COMMIT:
fix(tier2): move failure-report default inside Tier 2 clone (scripts/tier2/failures/)
Task 1.3: scripts/tier2/run_track.py chdir before state calls
- WHERE:
scripts/tier2/run_track.py:run_init(around line 78, beforesave_state) andrun_track.py:run_report(around line 100, beforewrite_failure_report). - WHAT: add
os.chdir(repo_path)soPath.cwd()in_state_dir/_failures_dirresolves to the repo root. - HOW: add
import osat the top (the file already importsargparse,subprocess,sys,datetime,pathlib); addos.chdir(repo_path)as the first line ofrun_initandrun_report. - SAFETY:
os.chdiris process-global; this is acceptable becauserun_track.pyis the CLI entry point, not a library. The chdir is idempotent within a single invocation. - COMMIT:
fix(tier2): chdir to repo_path in run_track before state/report calls
Task 1.4: Add scripts/tier2/state/ and scripts/tier2/failures/ to .gitignore
- WHERE:
.gitignore(top-level). Currently excludesscripts/generatedon line 11. - WHAT: add
scripts/tier2/state/andscripts/tier2/failures/after thescripts/generatedline. - HOW: edit the file in place.
- SAFETY: these are track-isolated scratch dirs; committing them would pollute the tree.
- COMMIT:
chore(tier2): gitignore scripts/tier2/state/ and scripts/tier2/failures/
Phase 2: Update OpenCode permissions and agent/command prompts
Focus: remove AppData allow rules from the OpenCode JSON fragment; update the agent prompt and slash command to say "NEVER USE APPDATA".
Task 2.1: conductor/tier2/opencode.json.fragment — remove AppData allow rules
- WHERE: lines 10-11, 16-17, 62-63, 68-69 (the
permission.readandpermission.writeblocks at top level and at thetier2-autonomousagent level). - WHAT: delete the two
C:\\Users\\Ed\\AppData\\Local\\manual_slop\\tier2\\**andC:\\Users\\Ed\\AppData\\Local\\manual_slop\\tier2_failures\\**allow rules. The remaining allow rule (the Tier 2 clone path) is unchanged. - HOW: four targeted
edit_filecalls (one perread/writeblock × top-level/agent). - SAFETY: keep the existing
*AppData\\Local\\Temp\\*bash deny rule. Do NOT modify the bash rules in this task — that's Task 2.2. - COMMIT:
fix(tier2): remove AppData allow rules from OpenCode permission JSON
Task 2.2: conductor/tier2/opencode.json.fragment — add *AppData\\* bash deny
- WHERE: the
permission.bashblock at top level (line 46) and at thetier2-autonomousagent level (line 73). - WHAT: add
"*AppData\\*": "deny"after the existing"*AppData\\Local\\Temp\\*": "deny"rule. The broader pattern catchesLocal,LocalLow,Roaming, and any other subdir. - HOW: two targeted edits.
- SAFETY: the rule denies any bash command containing
AppData\. Legitimate Tier 2 work does not write there. Combined with Task 2.1 (no allow rules), this is belt-and-suspenders. - COMMIT:
fix(tier2): add *AppData\\* bash deny rule (broader than just Temp)
Task 2.3: conductor/tier2/agents/tier2-autonomous.md — replace AppData convention
- WHERE: line 47 (the "Temp files" bullet under "Conventions (MUST follow - added 2026-06-17)").
- WHAT: replace the entire bullet. The new bullet says: "All scratch, state, audit-output, and intermediate files MUST live inside the Tier 2 clone (the OpenCode
*deny rule blocks everything else). Default locations:scripts/tier2/state/<track>/state.jsonfor failcount state,scripts/tier2/failures/for failure reports,scripts/tier2/artifacts/<track>/for throwaway scripts. TheC:\Users\Ed\AppData\...tree is OFF-LIMITS for any read, write, or shell command. The OpenCode*AppData\\*bash deny rule enforces this." - HOW: edit_file on the bullet's full text.
- SAFETY: preserve the env-var escape-hatch language (TIER2_STATE_DIR / TIER2_FAILURES_DIR are honored if set).
- COMMIT:
docs(tier2): agent prompt - replace AppData convention with inside-clone convention
Task 2.4: conductor/tier2/commands/tier-2-auto-execute.md — replace AppData convention
- WHERE: line 46 (the "Temp files" bullet under "Conventions (MUST follow - added 2026-06-17)").
- WHAT: identical change to Task 2.3, applied to the slash command prompt. Also update line 19 ("Check for a previous run" — the path is
<app-data>/tier2/<track-name>/state.json) and line 25 (step 3 in Protocol — "Initialize failcount state at<app-data>/tier2/<track-name>/state.json") to referencescripts/tier2/state/<track-name>/state.json. - HOW: three edit_file calls.
- SAFETY: the slash command prompt is what the Tier 2 agent reads; if it still says
<app-data>, the agent will continue trying to use AppData. - COMMIT:
docs(tier2): slash command - replace AppData paths with inside-clone paths
Phase 3: Update bootstrap scripts
Focus: setup_tier2_clone.ps1 and run_tier2_sandboxed.ps1 stop creating/referencing AppData dirs.
Task 3.1: scripts/tier2/setup_tier2_clone.ps1 — remove AppData dir creation
- WHERE: lines 23 (
$AppDataDir), 30 ($AppDataFailuresDir), 122-133 (theNew-Item/Get-Acl/Set-Aclblock). - WHAT: delete the
$AppDataDirand$AppDataFailuresDirparameter / variable declarations and the entire "Create app-data dir with restricted ACLs" step block. Update the docstring (lines 6-9) to remove the "creates the app-data temp dir with restricted ACLs" sentence. - HOW: three edit_file calls.
- SAFETY: the script must still create the Tier 2 clone, copy templates, install git hooks, and create the desktop shortcut. The deleted step is purely about AppData dirs.
- COMMIT:
fix(tier2): setup_tier2_clone.ps1 - stop creating AppData dirs
Task 3.2: scripts/tier2/run_tier2_sandboxed.ps1 — remove AppData dir references
- WHERE: lines 20-21 (
$AppDataDir,$AppDataFailuresDir), line 7 (docstring), line 77 (the "Set explicit ACLs on the Tier 2 clone + app-data dir" comment). - WHAT: delete the
$AppDataDir/$AppDataFailuresDirvariable declarations and any ACL-set logic that references them. Update the docstring (line 7) to remove "app-data dir" from the list. - HOW: four edit_file calls.
- SAFETY: the restricted-token + Job-Object + launch logic must stay intact.
- COMMIT:
fix(tier2): run_tier2_sandboxed.ps1 - remove AppData dir references
Phase 4: Update tests
Focus: flip the slash-command-spec tests so they assert "no AppData refs" instead of "AppData refs required"; update test_no_temp_writes.py docstring and fix-message.
Task 4.1: tests/test_tier2_slash_command_spec.py:test_agent_denies_temp_writes
- WHERE: lines 82-91 (the entire
test_agent_denies_temp_writesfunction). - WHAT: flip the assertions. Replace:
with:
assert 'AppData\\Local\\Temp' in content, "agent prompt must include Temp deny rule in frontmatter bash" assert 'AppData\\Local\\manual_slop\\tier2' in content or 'app-data' in content.lower(), "agent prompt must point agent at the app-data dir for temp files"Update the docstring to mention the 2026-06-18 reversal.assert 'AppData\\Local\\Temp' in content, "agent prompt must include Temp deny rule in frontmatter bash" assert "*AppData\\\\*" in content or "AppData\\\\*" in content, "agent prompt must include the broader AppData deny rule" assert "scripts/tier2/state" in content, "agent prompt must point agent at scripts/tier2/state for failcount state" assert "scripts/tier2/failures" in content, "agent prompt must point agent at scripts/tier2/failures for failure reports" assert "AppData\\Local\\manual_slop\\tier2" not in content, "agent prompt must NOT reference the AppData tier2 dir (2026-06-18 hard ban)" - HOW: edit_file on the function body and docstring.
- SAFETY: the
*AppData\\*substring check matches the literal JSON bash key"*AppData\\*". Be careful with Python string-escape semantics — use a raw string or a literal substring that survives the JSON double-escape. - COMMIT:
test(tier2): slash_command_spec - assert no AppData refs, point at inside-clone
Task 4.2: tests/test_tier2_slash_command_spec.py:test_command_denies_temp_writes (or the equivalent for the command file)
- WHERE: the parallel test for the slash command prompt (likely also in
tests/test_tier2_slash_command_spec.py). - WHAT: apply the same flip as Task 4.1 to the command prompt content.
- HOW: edit_file.
- SAFETY: keep the Temp deny assertion; add the new inside-clone-pointing assertions; remove the AppData-required assertion.
- COMMIT:
test(tier2): slash_command_spec - command prompt assert no AppData refs
Task 4.3: tests/test_no_temp_writes.py docstring + fix message
- WHERE: lines 1-15 (the docstring) and line 33 (the fix-message string).
- WHAT: replace the AppData paths in the docstring (lines 6-7) with
scripts/tier2/state/andscripts/tier2/failures/. Replace the fix-message suggestion on line 33 (C:\\Users\\Ed\\AppData\\Local\\manual_slop\\tier2\\ instead of %TEMP%.) withscripts/tier2/state/ or scripts/tier2/failures/ instead of %TEMP%.. - HOW: edit_file.
- SAFETY: the audit script's behavior is unchanged; only the human-facing strings change.
- COMMIT:
test(tier2): no_temp_writes - replace AppData refs in docstring + fix message
Phase 5: Update user-facing docs and workflow
Focus: docs/guide_tier2_autonomous.md and conductor/workflow.md stop referencing AppData.
Task 5.1: docs/guide_tier2_autonomous.md — replace AppData refs
- WHERE: line 24 (bootstrap step 5), line 59 (the "4 hard bans" table row), line 72 (failure report location), lines 119-129 (Troubleshooting section).
- WHAT: replace each
C:\Users\Ed\AppData\Local\manual_slop\tier2...reference with the newscripts/tier2/state/.../scripts/tier2/failures/...paths. - HOW: multiple edit_file calls (one per paragraph that contains an AppData path).
- SAFETY: the guide's structure and other content stay intact; only path strings change.
- COMMIT:
docs(tier2): guide_tier2_autonomous - replace AppData paths with inside-clone paths
Task 5.2: conductor/workflow.md — update hard bans table
- WHERE: line 386 (the row "File access outside Tier 2 clone + app-data dir").
- WHAT: replace with "File access outside Tier 2 clone (AppData, Temp, Documents, etc. all denied at the OpenCode
*level + targeted*AppData\\*deny)." - HOW: edit_file.
- SAFETY: the surrounding 3-layer-enforcement table structure stays.
- COMMIT:
docs(tier2): workflow.md hard bans - AppData denied (no exception)
Task 5.3: scripts/tier2/write_track_completion_report.py — update report output
- WHERE: lines 262, 264 (the "Filesystem boundary" and "Failcount monitored" rows in the generated report).
- WHAT: replace the AppData path strings with
scripts/tier2/state/.../scripts/tier2/failures/.... - HOW: two edit_file calls.
- SAFETY: the generated report's structure stays; only path strings change. The report's downstream consumers (the user reading it after a Tier 2 run) need to see the actual paths the next run will use.
- COMMIT:
fix(tier2): write_track_completion_report - use inside-clone paths in output
Phase 6: Conductor verification
Focus: ensure the test suite still passes after the changes; register the track in conductor/tracks.md.
Task 6.1: Run targeted test batches
- COMMAND:
uv run python scripts/run_tests_batched.py --tier tier-1-unit-core tests/test_failcount.py tests/test_tier2_report_writer.py tests/test_tier2_slash_command_spec.py tests/test_no_temp_writes.py - EXPECTED: all 4 test files pass. The
test_failcountandtest_tier2_report_writerenv-var tests pass because they monkeypatch the env var (FR7's backward-compat requirement). Thetest_tier2_slash_command_spectests pass because the new assertions match the updated agent prompt and slash command. Thetest_no_temp_writestest passes because the audit script's behavior didn't change. - COMMIT: no commit (this is a verification step).
Task 6.2: Run the static analyzer batch
- COMMAND:
uv run python scripts/audit_no_temp_writes.py --strict - EXPECTED:
CLEAN: no script under ./scripts/ emits to %TEMP%and exit code 0. The audit's exclusion list (scripts/tier2/artifacts) covers the throwaway scripts that may still have AppData path strings. - COMMIT: no commit.
Task 6.3: Register the track in conductor/tracks.md
- WHERE: append a new entry block following the precedent set by
tier2_autonomous_sandbox_20260616. - WHAT: add the link, spec, plan, metadata, status, and a one-line summary.
- COMMIT:
conductor(tracks): register tier2_no_appdata_20260618 (shipped)(after Phase 1-5 commit SHAs are recorded).
End-of-Track Report (added 2026-06-17 convention)
On Phase 6 completion, write docs/reports/TRACK_COMPLETION_tier2_no_appdata_20260618.md following the precedent set by docs/reports/TRACK_COMPLETION_tier2_autonomous_sandbox_20260616.md. Update conductor/tracks/tier2_no_appdata_20260618/state.toml to status = "completed".