Repository navigation
fix(hooks): strip shebang argument so pipx launchers resolve (#2629) - #2687
ousamabenyounes wants to merge 4 commits into
Conversation
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Graphify review — findings
This PR fixes shebang parsing in the git post-commit hook and the interpreter-detection probes across the various agent skill files (graphify/hooks.py, skill-*.md, and their generated tools/skillgen expected fixtures). It adds a step that strips a trailing interpreter argument (e.g., the -E in pipx's #!/.../python -E) so the leading path word is kept before the character allowlist check. The change spans a CHANGELOG entry and the corresponding hook test file (tests/test_hooks.py), touching many parallel skill-template copies that share the same snippet.
No blocking issues surfaced. 3 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1133 functions depend on the 987 functions this change touches.
Health — grade B; 7 existing hotspot(s) in the area this change touches (pre-existing, not introduced here):
dispatch_command()— 2 callers, 117 callees (high)install()— 25 callers, 6 callees (high)dispatch_install_cli()— 2 callers, 31 callees (high)uninstall()— 9 callers, 5 callees (high)status()— 6 callers, 5 callees (medium)uninstall_all()— 2 callers, 13 callees (high)test_poisoned_manifest_is_healed()— 0 callers, 6 callees (medium)
Verification — 1133 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 994 function(s) in the blast radius were not formally verified this run
43eff52 to
cd98290
Compare
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Graphify review — findings
This PR fixes handling of Python interpreter shebangs in the git post-commit hook (graphify/hooks.py) and across the generated per-agent skill files. The change strips any trailing interpreter argument (e.g. pipx's #!/.../python -E) from the parsed shebang before it's checked against the path allowlist, so the valid interpreter path is retained rather than falling back to python3. The same logic is applied consistently across all the skill-*.md files, the skillgen tool fragments/expected outputs, and a CHANGELOG entry (#2629) is added. Tests in test_hooks.py are also touched, presumably to cover the new shebang-argument stripping behavior. Reviewers should check that the shell parameter-expansion edits are duplicated identically across every skill file and that the generated/expected skillgen fixtures stay in sync with the source templates.
No blocking issues surfaced. 4 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1166 functions depend on the 1017 functions this change touches.
Health — this change adds coupling hotspots:
- new:
dispatch_command()— 2 callers, 117 callees - new:
install()— 33 callers, 7 callees - new:
dispatch_install_cli()— 2 callers, 31 callees - new:
status()— 8 callers, 6 callees - new:
uninstall()— 9 callers, 5 callees - new:
uninstall_all()— 2 callers, 13 callees - new:
test_poisoned_manifest_is_healed()— 0 callers, 6 callees
Verification — 1166 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1024 function(s) in the blast radius were not formally verified this run
· 7 more finding(s) on lines outside this diff (see the check run).
cd98290 to
ff8aa7a
Compare
|
Updated this PR at What changed since the previous version:
Validation:
|
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 5 advisory finding(s) below merit a look before merge.
Graphify review — findings
Fixes the git post-commit hook and skill interpreter probes to strip a shebang argument (#!/.../python -E from pipx) before treating it as a path, so the valid interpreter no longer trips the character allowlist and silently falls back to a python3 without graphify. The in-repo .graphify_python resolution now also verifies the chosen interpreter can import graphify and drops to python3 only when it can't, applied consistently across graphify/hooks.py and every generated skill file.
Worth a look
- env -S shebang can be accepted as interpreter path —
graphify/hooks.py:88· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- env -S shebangs are parsed as the env option instead of the interpreter —
graphify/hooks.py:90· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- tr -d '#!' strips all '#' and '!' chars from shebang, corrupting env-based paths —
graphify/skill-copilot.md:78· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Valid env -S shebangs are parsed as the interpreter name '-S' —
graphify/skill-copilot.md:79· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- Graphify python guard verifies PATH-controlled interpreter —
tools/skillgen/fragments/shell/interpreter-guard-posix.md:12· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1580 functions depend on the 1352 functions this change touches.
Health — this change adds coupling hotspots:
- new:
dispatch_command()— 2 callers, 122 callees - new:
install()— 33 callers, 7 callees - new:
render()— 13 callers, 5 callees - new:
dispatch_install_cli()— 2 callers, 31 callees - new:
status()— 8 callers, 6 callees - new:
audit_coverage()— 8 callers, 6 callees - new:
uninstall()— 9 callers, 5 callees - new:
main()— 3 callers, 11 callees - …and 4 more — each is listed as a finding
Verification — 1580 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1424 function(s) in the blast radius were not formally verified this run
· 12 more finding(s) on lines outside this diff (see the check run).
ff8aa7a to
d3b92f9
Compare
|
Updated this PR at What changed since the previous version:
Validation:
|
There was a problem hiding this comment.
Graphify reviewed this change.
Looks safe to merge — no coupling regressions and no blocking issues, checked against the code graph (not a self-assessment).
Graphify review — findings
Fixes the git post-commit hook and every skill interpreter probe to derive the Python path from a graphify shebang correctly: they strip a shebang argument (so pipx's #!/.../python -E resolves instead of tripping the path allowlist), parse /usr/bin/env -S ... launchers alongside plain /usr/bin/env, and reject bare PATH-controlled env names like python so an unverified interpreter can't be trusted. When the shebang yields no usable path, the skills fall back to python3 and verify import graphify before caching it in .graphify_python, ending the "could not locate a Python with graphify installed" message printed after every commit (#2629).
No blocking issues surfaced. 15 lower-confidence candidates did not survive cross-model review.
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1674 functions depend on the 1503 functions this change touches.
Health — this change adds coupling hotspots:
- new:
dispatch_command()— 2 callers, 123 callees - new:
install()— 33 callers, 7 callees - new:
render()— 13 callers, 5 callees - new:
dispatch_install_cli()— 2 callers, 31 callees - new:
status()— 8 callers, 6 callees - new:
audit_coverage()— 8 callers, 6 callees - new:
uninstall()— 9 callers, 5 callees - new:
main()— 3 callers, 11 callees - …and 4 more — each is listed as a finding
Verification — 1674 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1510 function(s) in the blast radius were not formally verified this run
· 12 more finding(s) on lines outside this diff (see the check run).
d3b92f9 to
09edf3d
Compare
|
Rebased onto the current The only conflict was in Validation on the rebased tree:
|
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 3 advisory finding(s) below merit a look before merge.
Graphify review — findings
Fixes graphify's Python-interpreter discovery in the post-commit hook and every generated skill probe (#2629): shebang parsing now strips a trailing interpreter argument (so pipx's #!/.../python -E resolves instead of tripping the path allowlist), handles /usr/bin/env -S ... launchers, and rejects bare PATH-controlled env names like python before verification. Skill probes additionally import-check the resolved interpreter and fall back to python3 only when it lacks graphify, so a valid pipx interpreter no longer silently degrades to a graphify-less python3 that prints "could not locate a Python with graphify installed" after each commit. Regenerates the expected skill fixtures and adds shebang-parsing tests to match.
Worth a look
- env -S parser treats env options as the interpreter —
graphify/hooks.py:84· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- env-name shebang like
#!/usr/bin/env pythonis dropped, losing valid interpreter —graphify/skill-kilo.md:84· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- PowerShell/core generator not updated to match shebang parsing change —
tools/skillgen/gen.py· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1690 functions depend on the 1511 functions this change touches.
Health — this change adds coupling hotspots:
- new:
install()— 37 callers, 7 callees - new:
dispatch_command()— 2 callers, 124 callees - new:
render()— 13 callers, 5 callees - new:
dispatch_install_cli()— 2 callers, 31 callees - new:
status()— 8 callers, 6 callees - new:
audit_coverage()— 8 callers, 6 callees - new:
uninstall()— 9 callers, 5 callees - new:
main()— 3 callers, 11 callees - …and 4 more — each is listed as a finding
Verification — 1690 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1526 function(s) in the blast radius were not formally verified this run
· 12 more finding(s) on lines outside this diff (see the check run).
…y-Labs#2629) pipx writes the graphify launcher shebang with an argument (`#!/.../python -E`). The interpreter probes used the whole shebang as a path; the space tripped the character allowlist, the valid interpreter was discarded, and the probe silently fell back to a `python3` without graphify — printing "could not locate a Python with graphify installed" after every commit and leaving the graph stale. Strip any interpreter argument (and resolve `/usr/bin/env python`) before using the shebang as a path, in the post-commit hook probe and both skill interpreter probes (regenerated via skillgen).
Extend the pipx shebang-argument fix to aider/devin monolith sources and the Devin subcommand guard, verify stripped guard candidates before writing .graphify_python, and move the changelog entry to Unreleased. RED->GREEN: red-devin-second-guard.log fails when the Devin guard fix is removed; green-review-blockers6.log passes 6 targeted tests. Full run-ci final5 matches baseline failure identities with 5110 passes vs 5106 baseline.
Handle /usr/bin/env -S shebang launchers, strip only the leading shebang marker, and reject env-derived bare interpreter names before verification. Regenerate skill artifacts and tighten the monolith drift guard coverage.
09edf3d to
1f2a393
Compare
There was a problem hiding this comment.
Graphify reviewed this change.
Worth a look — the grounded gate found no coupling regressions or blocking issues, but 2 advisory finding(s) below merit a look before merge.
Graphify review — findings
Fixes interpreter detection in the git post-commit hook and every skill's Python-probe so a pipx launcher (#!/.../python -E) resolves instead of silently degrading to a python3 without graphify. The probes now parse /usr/bin/env -S ... launchers, strip any trailing shebang argument before treating the shebang as a path, reject PATH-controlled bare env names (e.g. python) so only real interpreter paths are trusted, and verify the fallback candidate actually imports graphify before caching it in .graphify_python. Regenerated skill fixtures and expanded hook/skillgen tests cover the shebang-argument, launcher-quoting, and per-platform fallback paths.
Worth a look
- Relative shebang interpreter paths are trusted and executed —
graphify/hooks.py:94· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
- env -S with multiple flags loses interpreter path after first-word strip —
graphify/hooks.py:83· Escalate · medium- agreed by 2 of 2 members but NOT verified (no proof, no reproducing execution) — consensus is not a verdict; needs human review
Analysis details — impact, health, verification
Impact & health
Graphify review
Impact — 1741 functions depend on the 1552 functions this change touches.
Health — this change adds coupling hotspots:
- new:
main()— 98 callers, 3 callees - new:
install()— 37 callers, 7 callees - new:
dispatch_command()— 2 callers, 125 callees - new:
render()— 13 callers, 5 callees - new:
dispatch_install_cli()— 2 callers, 31 callees - new:
status()— 8 callers, 6 callees - new:
audit_coverage()— 8 callers, 6 callees - new:
uninstall()— 9 callers, 5 callees - …and 5 more — each is listed as a finding
Verification — 1741 functions in the blast radius were not formally verified this run (proofs are advisory here).
Gate & verification
graphify gate
PASS — objectively clean (no health regressions, tests not run — proofs not run this pass (advisory)). Grounded, not self-assessed.
Advisory (not blocking):
- verification_scope: 1567 function(s) in the blast radius were not formally verified this run
Test selection
Test selection
300 of 300 test file(s) selected (100%) via static blast radius.
Escalated to a full run for safety — the selection is not trustworthy on its own (see below). CI should run the whole suite.
tests/test_affected_cli.py— impact, full-run-safetytests/test_affected_member_seed.py— full-run-safetytests/test_agents_platform.py— impact, full-run-safetytests/test_analyze.py— full-run-safetytests/test_anthropic_custom_endpoint.py— full-run-safetytests/test_antigravity_install.py— full-run-safetytests/test_apm_fallback_version.py— full-run-safetytests/test_architecture_doc.py— full-run-safetytests/test_astro_extraction.py— full-run-safetytests/test_astro_import_ids.py— full-run-safetytests/test_atomic_canvas_export.py— full-run-safetytests/test_atomic_version_stamp.py— full-run-safetytests/test_atomic_writes.py— full-run-safetytests/test_backend_env_isolation.py— full-run-safetytests/test_backend_extras.py— full-run-safetytests/test_benchmark.py— full-run-safetytests/test_benchmark_raw_graph.py— full-run-safetytests/test_build.py— full-run-safetytests/test_build_merge_dedup_scope.py— full-run-safetytests/test_build_merge_hyperedges_and_prune.py— full-run-safetytests/test_build_merge_shrink_guard.py— full-run-safetytests/test_builtin_global_type_refs.py— full-run-safetytests/test_cache.py— full-run-safetytests/test_callflow_html.py— full-run-safetytests/test_cargo_introspect.py— full-run-safetytests/test_cargo_missing_manifest.py— full-run-safetytests/test_carried_hyperedge_remap.py— full-run-safetytests/test_case_sensitive_resolution.py— full-run-safetytests/test_charmap_encoding.py— full-run-safetytests/test_chunking.py— full-run-safetytests/test_cjs_module_extension.py— full-run-safetytests/test_claude_cli_backend.py— full-run-safetytests/test_claude_md.py— full-run-safetytests/test_cli_broken_pipe.py— full-run-safetytests/test_cli_export.py— full-run-safetytests/test_cli_help.py— full-run-safetytests/test_cluster.py— full-run-safetytests/test_cobol_extractor.py— full-run-safetytests/test_codebuddy.py— impact, full-run-safetytests/test_community_hub_labels.py— full-run-safetytests/test_community_labels_skill.py— full-run-safetytests/test_confidence.py— full-run-safetytests/test_corrupt_graph_json.py— full-run-safetytests/test_cpp_nested_and_cli.py— full-run-safetytests/test_cpp_objc_cross_file_calls.py— full-run-safetytests/test_cpp_preprocess.py— full-run-safetytests/test_cross_extension_reexport_self_cycle.py— full-run-safetytests/test_cross_language_call_resolution.py— full-run-safetytests/test_cross_repo_external_call_guards.py— full-run-safetytests/test_cross_repo_member_calls.py— full-run-safety- … and 250 more
non-code file(s) changed (
CHANGELOG.md,graphify/skill-agents.md,graphify/skill-aider.md,graphify/skill-amp.md,graphify/skill-claw.md…) → running the full suite for safety (a code graph can't see config/fixture/data deps)
changed code file(s) with no mapped test (
CHANGELOG.md,graphify/skill-agents.md,graphify/skill-aider.md,graphify/skill-amp.md,graphify/skill-claw.md…) — a coverage gap or a missing link — running the full suite rather than only the selected tests
Selection is safe under the controlled-regression assumption; always-run tests + a periodic full run are the backstops. Advisory — it never changes the check verdict.
· 13 more finding(s) on lines outside this diff (see the check run).
Windows drive and UNC spellings are relative on POSIX and must not be executed from a launcher's shebang. Preserve rooted Windows paths in MSYS, MinGW and Cygwin shells. Merge v8 without dropping either set of hook regressions and retire obsolete monolith drift rules. Refs Graphify-Labs#2629 Co-Authored-By: Codex <noreply@openai.com>
|
Updated at Validation: original upstream repro RED, 10 relative-path cases RED → GREEN, 273 focused tests pass; all four local Python matrix runs match baseline with 34 added passes and the same two environment failures. Generator checks, Ruff, 20 Pi guard tests and 100% changed-code coverage pass; static/security findings match baseline. Fresh Claude expert review passed and GitHub Actions passed on this SHA; Graphify formal verification returned neutral (no functions verified). |
Summary
Addresses the argument-bearing launcher case in #2629. pipx launchers such as
#!/absolute/venv/bin/python -Enow resolve to their interpreter instead of treating the argument as part of its path.The hook, generated POSIX skills, subcommand guard, and Aider/Devin source fragments remove only the leading shebang marker, handle
/usr/bin/env [-S] /absolute/python [args], strip interpreter arguments, validate the path and verify the interpreter. Relative interpreter paths and PATH-controlled env names are rejected before execution. The hook accepts Windows drive/UNC spellings only in an MSYS, MinGW or Cygwin shell; those spellings are relative filenames on POSIX hosts. Regenerated skill outputs match their source fragments.Merged current
v8while preserving the PR regressions and upstream hook coverage. The #2629 changelog entry is under Unreleased.Scope: the later
#!/bin/shtrampoline variant and whitespace-containing interpreter paths remain outside this change.Parser boundary: additional
envoptions and assignments fall through to the existing interpreter probes. Native PowerShell probes discover tool interpreters directly and do not parse launcher shebangs.Test verification (RED → GREEN)
On unmodified current
v8, with only the original regression tests copied in:The relative-path reviewer counterexample fails on the previous PR implementation in all five affected probe sources, for both direct and env-S launchers:
The retired relative-path drift forms also reproduce 3 failing checks before their removal.
Six Windows-shaped relative-path cases also fail on POSIX before platform qualification, then pass with the fix.
Final focused hook and generator suite:
Full validation:
./run-ci.sh finalpassed its blocking upstream-baseline comparison on all four CI Python versions.The two local environment failures are unchanged:
test_atomic_replace_temp_name_is_bounded_and_does_not_embed_long_filename(long allocation path) andtest_unix_socket_is_rejected(AF_UNIX path too long). Every added test passes on every matrix version.All five skillgen validators, Ruff, CLI/install smoke and 20 Pi guard tests pass. Pyright (637 diagnostics), Bandit (12 findings), and pip-audit (44 advisories) match upstream exactly. Supplemental unused-import checking matches the existing single
dataclasses.fieldfinding.Changed executable coverage: Python 4/4 statement anchors; Bash xtrace 29/29 source-command lines across the hook and authoritative fragments, including both Devin probes. The hook’s Windows rooted/relative path grammar checks pass. Native Windows execution was not part of the Linux CI matrix.