From a55e93763b197563282c899b2a29007ae8fa99c4 Mon Sep 17 00:00:00 2001 From: Jason Walker <913443@dadeschools.net> Date: Mon, 6 Jul 2026 14:46:08 -0400 Subject: [PATCH] docs: add agent temp-artifact cleanup runbook and ignore patterns (closes #261) Failed subagent runs left throwaway helpers (_encode_commit_payload.py, _emit_payload.py) in the shared working tree, polluting git status and tripping gitea_lock_issue / pre-flight purity gates. Add an 'Agent temp artifact cleanup' runbook section requiring deletion of _encode_*/_emit_*/_inline_* helpers when the MCP commit completes or aborts, prefer scratchpad payload preparation, and document the cleanup checklist. Back it with .gitignore patterns so strays cannot block issue locks for later sessions, and pin both with doc-contract tests (including a repo-root scan asserting the artifacts stay removed). Co-Authored-By: Claude Opus 4.8 (1M context) --- .gitignore | 5 ++ docs/llm-workflow-runbooks.md | 27 +++++++ .../test_agent_temp_artifact_cleanup_docs.py | 76 +++++++++++++++++++ 3 files changed, 108 insertions(+) create mode 100644 tests/test_agent_temp_artifact_cleanup_docs.py diff --git a/.gitignore b/.gitignore index e6fe104..a458968 100644 --- a/.gitignore +++ b/.gitignore @@ -10,3 +10,8 @@ gitea-mcp*.json .vscode/ graphify-out/ branches/ +# Throwaway agent payload/encoding helpers (#261): delete after the MCP +# commit completes or aborts; ignored here so strays cannot block issue locks. +_encode_*.py +_emit_*.py +_inline_*.py diff --git a/docs/llm-workflow-runbooks.md b/docs/llm-workflow-runbooks.md index 1b04cb3..765c55f 100644 --- a/docs/llm-workflow-runbooks.md +++ b/docs/llm-workflow-runbooks.md @@ -459,6 +459,33 @@ touching anything. files, detected secret, or any production/deploy behavior — **stop, report the blocker, and take no mutating action.** Fail closed; never work around a gate. +## Agent temp artifact cleanup (#261) + +Failed or aborted subagent runs have left throwaway helper scripts in the +shared working tree — for example `_encode_commit_payload.py` and +`_emit_payload.py` from the #152 commit-recovery attempt. These artifacts are +never part of any issue scope. They pollute `git status`, which trips +fail-closed gates: `gitea_lock_issue` refuses to lock over tracked edits, and +the pre-flight purity checks attribute the dirt to the current session. + +Rules: + +- **Delete throwaway helpers as soon as the MCP commit completes or aborts.** + Any file matching `_encode_*`, `_emit_*`, or `_inline_*` created as a + scratch payload/encoding helper must be removed in the same session that + created it — success and failure paths alike. +- **Prefer the scratchpad.** Payload preparation belongs in the session + scratchpad or a temp directory, never inside the repository worktree + (see the shell-free commit payload work in #263). +- **`.gitignore` backstop.** The patterns `_encode_*.py`, `_emit_*.py`, and + `_inline_*.py` are ignored so a leftover helper cannot show up as an + untracked file and block issue locks for later sessions. Ignoring them is a + safety net, not permission to leave them behind. +- **Cleanup checklist before ending any author session:** run + `git status --porcelain` in the worktree you mutated; remove any + `_encode_*` / `_emit_*` / `_inline_*` files you created; report the removal + in the final run report like any other local mutation. + ## Task/role alignment (#167) The **requested task** decides what a session may do — not the credential it diff --git a/tests/test_agent_temp_artifact_cleanup_docs.py b/tests/test_agent_temp_artifact_cleanup_docs.py new file mode 100644 index 0000000..d7d3a60 --- /dev/null +++ b/tests/test_agent_temp_artifact_cleanup_docs.py @@ -0,0 +1,76 @@ +"""Docs checks for agent temp-artifact cleanup guidance (#261). + +Failed subagent runs left throwaway helper scripts (e.g. +``_encode_commit_payload.py``, ``_emit_payload.py``) in the shared working +tree, polluting ``git status`` and tripping issue-lock / preflight gates. +These tests keep the cleanup runbook guidance present and the ignore +patterns in place so stray helpers can never dirty a worktree again. +""" +from pathlib import Path + +REPO_ROOT = Path(__file__).resolve().parent.parent +RUNBOOK = REPO_ROOT / "docs" / "llm-workflow-runbooks.md" +GITIGNORE = REPO_ROOT / ".gitignore" + +ARTIFACT_PATTERNS = ("_encode_*.py", "_emit_*.py", "_inline_*.py") + + +def _runbook_text(): + assert RUNBOOK.is_file(), "missing docs/llm-workflow-runbooks.md" + return RUNBOOK.read_text(encoding="utf-8") + + +def _gitignore_text(): + assert GITIGNORE.is_file(), "missing .gitignore" + return GITIGNORE.read_text(encoding="utf-8") + + +def test_runbook_has_agent_temp_artifact_cleanup_section(): + text = _runbook_text() + assert "## Agent temp artifact cleanup" in text, ( + "runbook lacks an 'Agent temp artifact cleanup' section") + assert "#261" in text, "cleanup guidance does not reference issue #261" + + +def test_runbook_names_known_stray_artifacts(): + text = _runbook_text() + for name in ("_encode_commit_payload.py", "_emit_payload.py"): + assert name in text, f"runbook does not name stray artifact {name!r}" + + +def test_runbook_covers_all_artifact_patterns(): + text = _runbook_text() + for pattern in ("_encode_*", "_emit_*", "_inline_*"): + assert pattern in text, f"runbook does not cover pattern {pattern!r}" + + +def test_runbook_requires_cleanup_after_commit_or_abort(): + text = _runbook_text().lower() + assert "delete" in text or "remove" in text + for phase in ("completes", "aborts"): + assert phase in text, ( + f"runbook does not require cleanup when the MCP commit {phase}") + + +def test_runbook_explains_lock_and_preflight_impact(): + text = _runbook_text().lower() + assert "gitea_lock_issue" in text + assert "preflight" in text or "pre-flight" in text + + +def test_gitignore_blocks_agent_helper_patterns(): + lines = [ + ln.strip() for ln in _gitignore_text().splitlines() + if ln.strip() and not ln.strip().startswith("#") + ] + for pattern in ARTIFACT_PATTERNS: + assert pattern in lines, f".gitignore lacks pattern {pattern!r}" + + +def test_no_stray_agent_helpers_in_repo_root(): + """Acceptance: artifacts stay removed from default worktrees.""" + stray = [ + p.name for pattern in ARTIFACT_PATTERNS + for p in REPO_ROOT.glob(pattern) + ] + assert not stray, f"stray agent helper artifacts present: {stray}"