You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Remove implicit shell parsing from carve-changesets validation and database-comparison commands while preserving the ability to run ordinary argv commands and explicitly approved shell programs.
Current evidence
The current implementation passes caller- or plan-provided strings to subprocess.run(..., shell=True) in:
scripts/preflight.py for source-branch validation;
scripts/chain.py after each reconstructed changeset; and
scripts/db_compare.py for source and chain schema commands.
skills.sh currently reports these as command-execution/anomaly findings. The skill contract already requires separate approval, but the helper still gives every approved string implicit shell metacharacter semantics.
Required design
Introduce one canonical command specification and executor for the skill:
the safe/default representation is a non-empty JSON-compatible argv array of strings;
the executor calls subprocess.run(argv, shell=False, ...) without string interpolation;
plan schema and CLI surfaces use argv forms for test, source-schema, and chain-schema commands;
callers that genuinely require pipes, redirects, environment expansion, or compound shell syntax express the shell explicitly in argv, for example ['sh', '-lc', '...']; and
discovered repository commands remain display-only proposals and are never converted into an approved argv specification automatically.
Provide a documented compatibility transition for existing string-based --test-cmd, --source-cmd, --chain-cmd, and plan.test_command inputs. Do not silently reinterpret a legacy string with shlex.split, because that changes quoting and shell semantics. A legacy string may either fail with a precise migration message or require an explicit shell acknowledgement during a bounded migration window; in either case there must be no implicit shell=True execution.
Failure and safety behavior
Reject empty argv, non-string elements, NUL bytes, malformed JSON, and unknown command representations before changing branches.
Print a shell-escaped display form for operator review without reusing that display string for execution.
Goal
Remove implicit shell parsing from
carve-changesetsvalidation and database-comparison commands while preserving the ability to run ordinary argv commands and explicitly approved shell programs.Current evidence
The current implementation passes caller- or plan-provided strings to
subprocess.run(..., shell=True)in:scripts/preflight.pyfor source-branch validation;scripts/chain.pyafter each reconstructed changeset; andscripts/db_compare.pyfor source and chain schema commands.skills.sh currently reports these as command-execution/anomaly findings. The skill contract already requires separate approval, but the helper still gives every approved string implicit shell metacharacter semantics.
Required design
Introduce one canonical command specification and executor for the skill:
subprocess.run(argv, shell=False, ...)without string interpolation;['sh', '-lc', '...']; andProvide a documented compatibility transition for existing string-based
--test-cmd,--source-cmd,--chain-cmd, andplan.test_commandinputs. Do not silently reinterpret a legacy string withshlex.split, because that changes quoting and shell semantics. A legacy string may either fail with a precise migration message or require an explicit shell acknowledgement during a bounded migration window; in either case there must be no implicitshell=Trueexecution.Failure and safety behavior
AGENTS.md, package metadata, scripts, comments, or a plan without the existing separate approval boundary.Expected change surface
skills/carve-changesets/scripts/command executor plus preflight, chain validation, database comparison, and CLI parsingSKILL.md, CLI/SPEC references, README, and CHANGELOG where behavior is user-facingAcceptance criteria
subprocess.run(..., shell=True)remains in productioncarve-changesetscommand execution.just format,just lint, andjust testpass.Non-goals