Skip to content

Rectify: Run-Cmd Path Capture Non-Empty Content Guard — PART A ONLY#3342

Merged
Trecek merged 8 commits into
developfrom
bridge-investigation-emits-success-with-0-byte-file-when-iss/3324
May 30, 2026
Merged

Rectify: Run-Cmd Path Capture Non-Empty Content Guard — PART A ONLY#3342
Trecek merged 8 commits into
developfrom
bridge-investigation-emits-success-with-0-byte-file-when-iss/3324

Conversation

@Trecek
Copy link
Copy Markdown
Collaborator

@Trecek Trecek commented May 30, 2026

Summary

The bridge_investigation step in remediation.yaml emits success with a 0-byte file because awk exits 0 regardless of output, the > redirect always creates the file, and neither the recipe engine nor the semantic rule layer validates that run_cmd steps producing file-path captures point to non-empty files.

Part A delivers the infrastructure prerequisite and the direct fix: unifying RecipeStep.capture with CampaignDispatch.capture so both use typed CaptureEntrySpec objects (enabling value_type="path"), strengthening _validate_capture_value to reject 0-byte files, upgrading bridge_investigation's capture to type: path, and adding a test -s bash guard. Part B will cover the new semantic rule run-cmd-path-capture-requires-nonempty-guard — implement as a separate task.

Closes #3324

Implementation Plan

Plan file: /home/talon/projects/autoskillit-runs/remediation-20260530-130305-956155/.autoskillit/temp/rectify/rectify_run_cmd_path_capture_guard_2026-05-30_130305_part_a.md

🤖 Generated with Claude Code via AutoSkillit

Token Usage Summary

Step Model count uncached output cache_read peak_ctx turns cache_write time
rectify* opus[1m] 1 5.8k 36.1k 4.7M 146.8k 458 224.5k 30m 20s
review_approach* sonnet 1 62 7.3k 250.6k 55.5k 99 43.1k 5m 56s
dry_walkthrough* opus 2 98 21.4k 2.0M 83.4k 195 167.7k 10m 25s
implement* sonnet 2 780 53.3k 9.2M 142.0k 325 164.9k 18m 26s
audit_impl* sonnet 2 1.7k 18.0k 512.8k 59.7k 58 83.6k 8m 41s
prepare_pr* sonnet 1 202.3k 5.6k 309.9k 31.0k 35 43.9k 1m 51s
compose_pr* sonnet 1 60.5k 1.8k 244.9k 31.0k 16 15.6k 43s
review_pr* sonnet 1 166 42.7k 1.1M 101.8k 81 86.3k 9m 26s
resolve_review* opus 1 77 15.5k 2.2M 84.4k 79 69.1k 10m 34s
resolve_pre_review_conflicts* opus 1 33 4.8k 601.4k 49.3k 34 33.1k 1m 39s
Total 271.5k 206.5k 21.1M 146.8k 931.8k 1h 38m

* Step used a non-Anthropic provider; caching behavior may differ.

Token Efficiency

Step LoC Changed cache_read/LoC cache_write/LoC output/LoC
rectify 0
review_approach 0
dry_walkthrough 0
implement 293 31332.9 562.8 181.9
audit_impl 0
prepare_pr 0
compose_pr 0
review_pr 0
resolve_review 36 61635.5 1919.4 431.0
resolve_pre_review_conflicts 559 1075.9 59.2 8.6
Total 888 23737.4 1049.4 232.5

Model Usage Breakdown

Model steps uncached output cache_read cache_write time
opus[1m] 1 5.8k 36.1k 4.7M 224.5k 30m 20s
sonnet 6 265.5k 128.7k 11.6M 437.5k 45m 4s
opus 3 208 41.7k 4.8M 269.9k 22m 39s

Trecek and others added 8 commits May 30, 2026 15:13
…ation (Part A Phase 1)

- 1a: test_extract_path_type_rejects_zero_byte_file — fails until _validate_capture_value checks st_size
- 1b: test_extract_path_type_accepts_nonempty_file — regression guard for non-empty path acceptance
- 1c: test_remediation_bridge_investigation_capture_is_path_typed — fails until capture uses type: path
- 1d: test_remediation_bridge_investigation_cmd_has_nonempty_guard — fails until test -s added to cmd
- 1e: test_recipe_step_shorthand_capture_parses_to_capture_entry_spec — fails until RecipeStep.capture unified to CaptureEntrySpec

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
… (Part A Phase 2)

- 2a: RecipeStep.capture/capture_list typed as dict[str, CaptureEntrySpec] (was dict[str, str])
- 2b: _parse_step loader calls _parse_capture_spec() for both capture and capture_list fields
- 2c: 16 source sites updated to access .from_ on CaptureEntrySpec (rules_cmd, rules_dataflow,
  validator, rules_graph_output, rules_graph_review, rules_graph_routes, rules_verdict,
  rules_dataflow_handoff, rules_clone, _analysis_bfs, _analysis_detectors, rules_merge_context)
- 2d: 9 test files updated to use .from_ accessor for capture value comparisons
- 2f: _validate_capture_value raises CaptureValueTypeError for 0-byte path files (st_size == 0)
- 2g: bridge_investigation capture upgraded to long-form type: path
- 2h: bridge_investigation cmd adds test -s guard to fail on empty awk output
- 2i: remediation.json recompiled, contract cards regenerated

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
…_init__

Tests constructing RecipeStep with capture={"key": "string"} broke after
the type changed to dict[str, CaptureEntrySpec]. Add _coerce_capture_dict
in __post_init__ to auto-wrap raw strings. Also fix test assertions that
used 'in' operator on CaptureEntrySpec objects or compared against raw dicts.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
…(Part B)

Adds defense layer 3: a new @semantic_rule that statically flags run_cmd
steps echoing path-typed captures without a test -s or [ -s non-empty file
guard in the cmd. Catches the 0-byte file pattern at recipe validation time
during development.

Includes 4 tests covering: missing guard → WARNING, test -s guard passes,
string-typed capture not flagged, [ -s bracket guard passes.
- Return fresh empty dict instead of raw untyped input on falsy path
- Add isinstance guard for CaptureEntrySpec in else branch
- Raise TypeError for unexpected value types instead of silent pass-through

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
The semantic rule now inspects both step.capture and step.capture_list
for path-typed entries, preventing silent bypass when path captures are
declared in capture_list.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace separate exists() + stat() syscalls with a single stat() call
inside try/except. This atomically checks both existence and size,
preventing unhandled FileNotFoundError when a file disappears between
the two calls.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Replace weakened .from_-only assertions with full CaptureEntrySpec
equality checks that also verify value_type defaults to 'string',
matching the established pattern in test_io_parsing.py.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
@Trecek Trecek force-pushed the bridge-investigation-emits-success-with-0-byte-file-when-iss/3324 branch from 44c2d1b to 3f30760 Compare May 30, 2026 22:14
@Trecek Trecek added this pull request to the merge queue May 30, 2026
Merged via the queue into develop with commit d1ea137 May 30, 2026
3 checks passed
@Trecek Trecek deleted the bridge-investigation-emits-success-with-0-byte-file-when-iss/3324 branch May 30, 2026 22:25
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant