Skip to content

Dry-walkthrough's required git check-ignore is blocked on protected deliverables #4262

Description

@Trecek

Bug

dry-walkthrough explicitly requires git check-ignore -v {path} for every plan-prescribed deliverable, but recipe_read_guard blocks that metadata-only command whenever the deliverable is a protected recipe, skill, or agent path.

The validator is therefore unable to perform its required committability check on protected deliverables that can appear in AutoSkillit plans. This is a narrow compatibility defect: it does not require reading file content and must not weaken the guard for content-bearing commands.

Production evidence

At both the executing 0.10.870 revision and current source, dry-walkthrough/SKILL.md:152 says:

Plan-prescribed deliverable output paths are committable (not gitignored) — run
git check-ignore -v {path} on each deliverable path

During the #4253 Part A walkthrough, Codex session 019f61e9-0c69-7cf2-a35d-09a823f11bf5 attempted to check protected recipe deliverables and received the recipe_read_guard denial. Later retries attempted mixed variants, but only the first all-check-ignore chain and the direct classifier reproduction below are used as proof.

The production-shaped denial at 2026-07-14T18:47:55.543Z included multiple explicit paths and ordinary || true/; chaining. That exact shape matters: a fix that permits only a single-path toy command would not repair the observed workflow.

The clean command was reproduced directly against the current shared classifier:

True    git check-ignore -v src/autoskillit/recipes/remediation.yaml
False   git diff --stat -- src/autoskillit/recipes/remediation.yaml

True means command_has_blocked_protected_path_read() classifies the required check-ignore command as a protected-content read even though Git emits only ignore-rule metadata and the queried pathname.

Mixed incident commands that also attempted content access are not used as proof; those denials were correct.

Runtime provenance

The five Codex invocations that did not verify the plan report executing AutoSkillit 0.10.870; the workspace clone was 0.10.871. Both contain #4207 / PR #4213 merge 61d7df986, whose shared protected-path metadata allowlist intentionally covers git add, safe git diff, safe git status, and wc -l, but not git check-ignore.

This is not a pre-fix/stale-runtime recurrence. It is an uncovered command in the staged guard remediation.

Root cause

The skill command contract and guard allowlist are maintained independently.

hooks/_command_classification.py restricts protected-path Git metadata operations to _PROTECTED_PATH_METADATA_GIT_SUBCOMMANDS = {"add", "diff", "status"}. Anything else is denied without a subcommand-specific proof that the form cannot emit protected file content. Existing guard tests cover the allowlist's local examples, but no compatibility fixture feeds the literal git check-ignore -v {path} form mandated by dry-walkthrough through the guard.

Required outcome

  1. Add a narrow protected-path classifier for the required grammar: git check-ignore (-v|--verbose) [--no-index] [--] LITERAL_PATH....
  2. Permit only forms whose output is limited to ignore provenance/status for the named path or paths. Support the ordinary safe || true exit-status normalization used by the workflow.
  3. Reject Git global options/config that can redirect reads or repository context, including -c, -C, --git-dir, --work-tree, and --bare; also reject --stdin, any non-literal path source, unsafe redirections, substitutions, visible shell-variable derivations, malformed quoting, wrappers that alter interpretation, and mixed safe+unsafe chains. The classifier cannot infer values inherited from shell state, so claims and fixtures must be limited to information visible in the submitted command.
  4. Preserve denial for content-bearing protected-path reads, including git show REV:path, patch output, blame/grep, direct readers, interpreter reads, and indirect shell recovery techniques.
  5. Preserve hook/server classification parity wherever both layers enforce protected-path reads.
  6. Add an explicit checked compatibility fixture for the literal command prescribed by dry-walkthrough; do not attempt to infer arbitrary commands from free-form skill prose.

Existing ownership and boundaries

Issue/PR Existing ownership Residual owned here
#3966 / PR #3969 Introduced the exact dry-walkthrough committability mandate and its planner/recipe checks Preserve that delivered contract; repair only its guard incompatibility.
#4151 Audit-impl handling/copying of gitignored transient deliverables Do not change audit behavior or make gitignored output acceptable; this issue only lets dry-walkthrough execute the existing committability check.
#4207 / PR #4213 Shared classifier plus narrow safe git add, diff/status, and wc -l forms The explicitly prescribed git check-ignore -v {path} form omitted from that staged allowlist.
#3479 / PR #3554 test_runner_guard, including quoted/read-only searches falsely classified as pytest execution Strictly out of scope. Its existing recurrence section already owns the `rg 'pytest
#4222 / PR #4250 Command-position parsing across other guards/shared helpers Do not reopen or duplicate those delivered changes.
#3659 Catalog awareness in dry-walkthrough Catalog ownership/delivery is tracked separately.
#4253 / PR #4259 Staged output-cap/order implementation Strictly out of scope; historical run evidence only.
#4226 / #3962 Provider capacity/retry Strictly out of scope.

Acceptance criteria

  • git check-ignore -v src/autoskillit/recipes/remediation.yaml is allowed in a headless dry-walkthrough and returns ordinary Git ignore metadata/status.
  • Equivalent explicit paths under protected recipe, skill, and agent namespaces are covered, including multiple literal paths in one command.
  • The observed safe git check-ignore ... || true form is allowed; a safe check followed by any content-bearing segment causes the entire submitted command to be denied.
  • Nonexistent, tracked, untracked, and ignored path results preserve normal git check-ignore exit/status semantics without exposing file content.
  • Git global-config/repository redirection, --stdin or other non-literal path sources, visible substitution/variable derivation, unsafe redirection, malformed, wrapper, and mixed-chain bypass fixtures remain denied. Include git -c core.excludesFile=PROTECTED_SKILL_PATH check-ignore -v PATH as a negative control.
  • Content-bearing protected-path commands remain denied.
  • Hook and server guard results agree for every fixture where both enforcement layers apply.
  • A checked fixture binds the skill's prescribed git check-ignore form to guard compatibility.
  • No changes are made to open_kitchen response exceeds Claude Code MCP output cap — step graph truncated from orchestrator context, causing out-of-order step execution (review_approach before rectify) #4253, catalog delivery, multipart authority, provider retry, stamp/verdict behavior, or PreToolUse guard: block direct pytest invocation in headless sessions #3479's test-runner scope.
  • task test-check and pre-commit run --all-files pass.

Investigation

Evidence was gathered from the five Part A Codex invocations that did not verify the plan, the exact dry-walkthrough instruction and shared classifier at runtime/current revisions, the #4207 delivery, direct safe/unsafe classifier controls, and the existing #3479 recurrence body. The original combined guard draft was rejected by an adversarial reviewer because it duplicated #3479 and treated merely reproducible Git metadata forms as workflow requirements. The filing agent independently verified both objections and narrowed this issue to the one explicit unowned command contract.

Related new issues

Routing

Route through recipe:remediation. Although the positive case is narrow, safe shell-form classification requires adversarial bypass design and shared enforcement parity.

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugExisting behavior is brokenrecipe:remediationRoute: investigate/decompose before implementation

    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