Skip to content

recipe_read_guard false positives on git add/diff/wc referencing recipe files — burns context, contributed to assess exhaustion #4207

Description

@Trecek

Problem

recipe_read_guard produces false positives that block legitimate read-only or staging commands (git add, git diff, wc -l) when their arguments reference recipe file paths. Sessions burn context on denied-command retries; the round-3 pipeline-health report for the #4204 remediation (dispatch 2046994e) explicitly lists this as a contributing factor to the assess step's context exhaustion.

Evidence

Pipeline-health report ~/.local/share/autoskillit/logs/health-reports/2046994e-7771-489f-a21f-e6ef49f51b7b_health_report.json (2026-07-07): INFORMATIONAL finding on the implement step group — "recipe_read_guard false positive: blocks git add/git diff/wc -l commands referencing [recipe files]"; report summary — "recipe_read_guard false-positives contributed to exhaustion."

That remediation legitimately edits bundled recipe YAMLs (the #4204 fix wires a step into 4 recipes), so sessions must be able to stage/diff those files. The guard's purpose (prevent reading raw recipe YAML in place of load_recipe) should not match git plumbing/staging or line-count operations on files the session itself is editing.

Expected behavior

The guard distinguishes content-consumption reads (cat/less/head of recipe YAML — deny) from VCS staging/diff and metadata operations (git add, git diff, git status, wc -l — allow), or at minimum allows them within a worktree whose plan declares recipe files in scope.

References

Prior Remediation Attempt (round 1)

Attempted via dispatch 613cb784-aaf0-4f53-858e-e4257b4a7f59 on 2026-07-08 using the queue-mandated remediation-fable fleet command.

Outcome: zero engineering progress. The run cloned the repo, claimed this issue, and created branch recipe-read-guard-false-positives-on-git-add-diff-wc-referen/4207, but failed before the investigate step could run. No implementation worktree existed and the branch had 0 commits ahead of origin/develop.

Failure: run_skill investigate failed with an ingredient lock enforcement error and returned no content field. Raw rollout shows the L2 invoked run_skill without order_id, then MCP rejected it with: INGREDIENT LOCK ENFORCED: Step 'investigate' is locked out by pipeline ''. Pass order_id to scope the check, or call lock_ingredients(unlock=[...]) to release.

Blocking infra issue filed: #4212. Next round guidance: do not reimplement this issue from scratch until #4212 is fixed and merged. After #4212 is staged, re-dispatch this issue normally; ignore the deleted zero-commit branch from round 1.

Prior Remediation Attempt (round 2 driver workaround)

Operator asked whether a workaround was possible after #4212 was filed. Driver is re-dispatching with the same queue command plus --backend claude-code to force the intended Claude L2 path and avoid the Codex L2 environment propagation failure captured in #4212.

Guidance for this round: continue with the actual #4207 investigation and implementation. The round-1 guidance to wait for #4212 applies only to default/unqualified dispatches that spawn a Codex L2 and fail before investigate.

Round 2 outcome: the forced --backend claude-code workaround did not enter the recipe. Dispatch 6c6450e4-07c1-4f7f-b369-e5cec6dd5c81, session cb9e894b-1d2d-494e-bb6a-ff43284e51f3, failed in 8s before clone/claim because Claude Code returned HTTP 429 weekly quota: You have hit your weekly limit; resets Jul 12, 10am America/Los_Angeles.

Next guidance: after the Anthropic quota resets, this exact forced-Claude workaround is the safest driver-level retry. Until then, do not retry fable/Claude remediation dispatches. The default/unqualified dispatch path is still blocked by #4212.

Unclogger Action Before Round 3

Timestamp: 2026-07-08 11:39 PDT.

Driver verified no fleet dispatch process was running (pgrep -af autoskillit fleet run|fleet run remediation|remediation-fable returned no live fleet process; a second ps scan matched only the scan command itself).

The stale overlay .autoskillit/temp/.hook_config_overlay.json was backed up to .autoskillit/temp/codex-loop/hook_config_overlay.20260708T113815-0700.bak.json. The live overlay contained a global empty-string scope locking investigate=false, review_approach=true, and audit_impl=true. Code evidence in src/autoskillit/server/tools/tools_execution.py shows unscoped run_skill scans all locked_steps; this exact empty scope caused the round-1 rejection recorded above.

Surgical cleanup performed: removed only the empty-string entries from locked_ingredients and locked_steps; all named historical pipeline locks were left intact. Verification: jq now reports has_empty_locked_ingredients=false and has_empty_locked_steps=false.

Next guidance: retry this issue with the default queue command. If it reaches investigate, continue normal fleet routing. If default dispatch still fails before the first recipe step, route by the new envelope and continue unclogger handling under #4212.

Prior Remediation Attempt (round 3 default retry after overlay cleanup)

Timestamp: 2026-07-08 11:45 PDT.

Attempted default remediation-fable dispatch after clearing the stale empty-string/global ingredient-lock scope. Dispatch 9760a52e-49a9-4f44-a0e4-9d8163fa0ced, session 019f4307-f496-7ca1-a3ff-52448b3acd60. Dispatch log: .autoskillit/temp/codex-loop/4207-dispatch-round3-20260708-114020.log. Raw rollout: /home/talon/.codex/sessions/2026/07/08/rollout-2026-07-08T11-40-22-019f4307-f496-7ca1-a3ff-52448b3acd60.jsonl.

Outcome: zero engineering progress again. The run reached clone/claim/create-branch, then failed before investigate. Clone: /home/talon/projects/autoskillit-runs/remediation-20260708-114044-664157. Branch recipe-read-guard-false-positives-on-git-add-diff-wc-referen/4207 was 0 commits ahead of origin/develop, had no PR, and was deleted from GitHub after verification.

Failure: run_skill investigate returned success=false without content due to ingredient lock enforcement. Raw rollout shows the unscoped L2 call was denied by a stale named lock: INGREDIENT LOCK ENFORCED: Step 'investigate' is locked out by pipeline '4171'.

Unclogger follow-up: verified no fleet dispatch process was running, backed up the overlay to .autoskillit/temp/codex-loop/hook_config_overlay.20260708T114218-0700.pre-named-investigate-clear.bak.json, then removed stale investigate entries from the named lock scopes. Verification after cleanup: false_locks=[], empty_locked_ingredients=false, and empty_locked_steps=false.

Next guidance: retry default remediation-fable again. If it reaches investigate, continue normal fleet routing. If another pre-investigate lock rejection occurs, treat it as #4212 state still not unclogged and inspect the new denial before retrying.

Prior Remediation Attempt (round 4 default retry after all false locks cleared)

Timestamp: 2026-07-08 12:05 PDT.

Attempted default remediation-fable dispatch after clearing both the empty-string/global false lock and stale named investigate=false locks. Dispatch ed699bb3-80ab-46e6-9fc0-9fb6193aeb39, session 019f430d-d168-7270-bd58-191009e4475a. Dispatch log: .autoskillit/temp/codex-loop/4207-dispatch-round4-20260708-114645.log. Parent rollout: /home/talon/.codex/sessions/2026/07/08/rollout-2026-07-08T11-46-46-019f430d-d168-7270-bd58-191009e4475a.jsonl.

Outcome: zero engineering progress, but a different blocker. The run reached clone/claim/create-branch, proving the stale lock cleanup worked. Clone: /home/talon/projects/autoskillit-runs/remediation-20260708-114719-094221. Branch recipe-read-guard-false-positives-on-git-add-diff-wc-referen/4207 was clean, 0 commits ahead of origin/develop, and had no PR.

Failure: the Codex L2 launched the fable-pinned investigate child. Child session 019f430e-d4c8-7201-9105-6773fa46baa6 exited in 4.6s with zero writes and root error: The 'fable' model is not supported when using Codex with a ChatGPT account. Child rollout: /home/talon/.local/share/autoskillit/logs/codex-sessions/2026/07/08/rollout-2026-07-08T11-47-53-019f430e-d4c8-7201-9105-6773fa46baa6.jsonl.

Operator correction: Anthropic-backed routes are not available and should not be used. The Codex backend is part of what this queue is hardening, so default remediation-fable through Codex and forced Claude L2 dispatch are both known-bad routes right now.

Next guidance: do not retry default remediation-fable for this issue while it would route through Codex and launch model=fable; do not force --backend claude-code while Anthropic quota is unavailable. Continue in UNCLOGGER mode and run the remediation steps manually, preserving recipe order and documenting each step. A read-only explorer scoped the #4207 fix: update both the hook guard and server-side guard so protected-path denials distinguish content-reading commands from VCS/metadata commands; add focused hook and server tests; run task test-check.

Manual Unclogger Remediation (local, uncommitted)

Timestamp: 2026-07-08 12:35 PDT.

Because both active fleet routes were blocked (#4212/default Codex path and Anthropic-backed Claude L2), the driver followed the handoff's UNCLOGGER route and applied the #4207 fix manually in the working tree. No commit or PR was created because the active session instructions still prohibit commits unless explicitly authorized.

Changed files:

  • src/autoskillit/hooks/_command_classification.py: added shared protected-path command classification for recipe/skill/agent paths. It allows narrow non-content operations (git add, safe git diff metadata forms, safe git status, wc -l) and denies content-producing Git modes (git diff patches, git status -v, git add -p, --pathspec-from-file), heredocs, shell substitutions, variable reuse in chains, and unsafe chained reads.
  • src/autoskillit/hooks/guards/recipe_read_guard.py: now uses the shared classifier while preserving the stdlib-only hook contract.
  • src/autoskillit/hooks/__init__.py: exports the shared classifier through the package gateway for server-layer import rules.
  • src/autoskillit/server/_guards.py: uses the same classifier so MCP run_cmd and hook behavior stay aligned.
  • tests/infra/test_recipe_read_guard.py and tests/server/test_tools_run_cmd_invariants.py: added allow/deny regression tests for VCS/metadata commands and bypass attempts.

Validation:

  • Focused guard/classifier suite: PYTEST_TEST_PATHS="tests/infra/test_recipe_read_guard.py tests/server/test_tools_run_cmd_invariants.py tests/hooks/test_command_classification.py tests/hooks/test_command_classification_git.py" task test-check → PASS, 198 passed. Output: temp/test-2026-07-08_122843.txt.
  • pre-commit run --all-files → PASS.
  • Full gate: task test-check → PASS, 29,766 passed, 604 skipped, 44 xfailed. Output: temp/test-2026-07-08_122911.txt.

Subagent review notes: the first review caught overly broad Git allow-lists and heredoc/chain bypasses; the second review caught newline/background/substitution, --pathspec-from-file, and content-producing diff flag gaps. Those findings were addressed before the final passing gate.

Next guidance: a commit/PR is needed to land this local fix, but do not commit unless the active operator explicitly authorizes it. After this fix is committed and merged/staged, continue the queue with #4199 via the manual UNCLOGGER route unless a safe non-Anthropic fleet route exists.

Manual Recipe Tail and PR Landing

Timestamp: 2026-07-08 14:25 PDT.

Operator authorized landing the manual fix through the recipe-style PR tail. Driver followed the remediation tail manually because the normal fleet path remained blocked by Codex/fable routing and Anthropic-backed L2 routes were unavailable.

Actions:

  • Created branch recipe-read-guard-safe-metadata-4207.
  • Committed the local fix as f66bb1df4.
  • Ran prepare-pr through run_skill with step_provider=minimax; session 54e72807-b4ae-4cd3-8c56-1965acdbce51; prep file .autoskillit/temp/prepare-pr/pr_prep_2026-07-08_131643.md.
  • Ran compose-pr through run_skill with step_provider=minimax; session 99538444-e85f-49e4-87fe-b354aaf18296; opened PR Recipe Read Guard Metadata Command Remediation #4213.
  • Ran local review-pr iteration 0 through run_skill; session 157248d7-958e-43b8-b514-fe7da1214551; verdict changes_requested.
  • Ran resolve-review iteration 0 through run_skill; session d62f08cd-ce49-4742-bd61-bc1f9aceaa20; verdict real_fix; added four review-fix commits.
  • Ran local review-pr iteration 1 through run_skill; session a7004ce3-c28a-4f3d-94f2-6a6421b15783; verdict approved_with_comments.
  • Ran resolve-review iteration 1 through run_skill; session 32f40b61-8604-4c84-8bde-0f1ce030061d; verdict real_fix; added three review-fix commits.
  • Restored .autoskillit/temp/.hook_config_overlay.json by removing only the temporary quota_guard.disabled=true session override after backing it up, because the override caused quota hook tests to fail by design.
  • Enqueued PR Recipe Read Guard Metadata Command Remediation #4213 into the develop merge queue after local tests and branch CI passed.

Validation:

  • pre-commit run --all-files passed after review fixes.
  • First full task test-check after review fixes failed only in quota hook tests because the session-level quota guard override was still active; output temp/test-2026-07-08_140432.txt.
  • After removing the temporary quota override, focused quota hook rerun passed: PYTEST_TEST_PATHS="tests/hooks/test_quota_check.py tests/hooks/test_quota_post_check.py" task test-check -> 61 passed; output temp/test-2026-07-08_140908.txt.
  • Full task test-check passed: 29,773 passed, 604 skipped, 44 xfailed; output temp/test-2026-07-08_140917.txt.
  • GitHub Actions branch CI passed: run 28975593797, head 58aca9183c08ad8480f40bddccd53fd075e8a47e.

Outcome:

Next guidance: continue the queue with #4199. Until a safe non-Anthropic fleet route exists, use the manual UNCLOGGER recipe-step discipline from the handoff.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugExisting behavior is brokenrecipe:remediationRoute: investigate/decompose before implementationstagedImplementation staged and waiting for promotion to main

    Type

    No type

    Fields

    No fields configured for issues without a type.

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions