7.3 KiB
issue, issue_title
| issue | issue_title |
|---|---|
| 435 | fix(pi-permission-system): strip shell comment lines from bash commands before matching |
Retro: #435 — strip shell comment lines from bash commands before matching
Stage: PR Review (2026-06-19T17:06:21Z)
Session summary
Third-party PR #435 from @rnavarro (Robert Navarro) fixes a real gap: when an agent prepends a # description comment line before a bash command, both the surface match value and the session-approval suggestion tokenize the comment instead of the command, so an explicitly-approved pattern (e.g. nvm ls) fails to match and the suggestion is built from # list… tokens.
The PR adds stripBashCommentLines() to bash-arity.ts and applies it in normalizeInput (input-normalizer.ts) and suggestBashPattern (pattern-suggest.ts).
The operator reviewed the diff and chose to adopt mostly as-is — merge-rebase the branch unchanged, with any future tweaks landing as commits on top — classified as a non-breaking fix:.
Evaluation
The underlying problem is real and reproducible.
normalizeInput (input-normalizer.ts:94) returns the raw multi-line command as the bash match value, and suggestBashPattern (pattern-suggest.ts:26) splits the trimmed command on whitespace — a leading # … line shifts the leading tokens onto the comment in both paths.
The approach is sound and right-sized; there is little to simplify:
stripBashCommentLineslands inbash-arity.ts, the module already responsible for bash command structure (ARITY/prefix), exported-for-testability in the same style as its siblings — good convention fit.- The
/^\s*#/line filter is conservative: it strips only lines whose first non-whitespace character is#, soecho "#foo"and inline trailing comments (nvm ls # note) are left intact. It cannot over-strip into a permission bypass — gating now operates on the real command rather than a comment-confused value, which is security-positive. - Both applications are necessary, not redundant.
normalizeInputstrips for the match value while preserving the original inresultExtras.command;suggestBashPatternmust strip independently becausederiveSuggestionValue(handlers/gates/tool.ts:21) feeds itcheck.command— the preserved original. The fallbackstripBashCommentLines(command) || commandkeeps an all-comment command evaluating against its literal text. - No new parameters threaded, no schema/config/docs surface touched, no speculative types — nothing over-built to collapse.
Breaking call: non-breaking fix:.
It makes a previously-prompted (comment-defeated) command auto-allow only when it matches a pattern the user explicitly approved, aligning behavior with intent rather than loosening policy.
# nvm ls\nrm -rf / still strips to rm -rf / and still will not match an nvm ls pattern, so no bypass is introduced.
Verification on the branch: tsc clean, lint exit 0 (3 pre-existing biome infos in an unrelated path test), 2033 tests pass, fallow dead-code clean.
Decision and attribution
Direction: adopt mostly as-is — rebase-merge PR #435 unchanged; follow-up changes (if any) as commits on top.
Contributor: Robert Navarro <crshman@gmail.com> (@rnavarro).
Because the branch is merged as-is, his commits carry his authorship directly.
Any follow-up commit we author on top must end its body with a blank line followed by:
Co-authored-by: Robert Navarro <crshman@gmail.com>
The PR merge auto-closes #435; a close/thank-you comment credits @rnavarro by name and links the merge SHA.
Stage: Final Retrospective (2026-06-19T18:16:36Z)
Session summary
This session reviewed third-party PR #435, merged it as-is per the operator's call, then cut and published the pi-permission-system 14.0.1 release.
The review and verification were clean, but the release-please PR (#436) was merged with the wrong method — gh pr merge --merge instead of the project's established --rebase — landing it as merge bubble 57561321, the first non-linear release since the rebase convention was adopted.
Observations
What went well
- The evaluation traced
deriveSuggestionValue(handlers/gates/tool.ts:21) to confirm the secondstripBashCommentLinescall insuggestBashPatternwas necessary (it receives the preserved originalcheck.command), not redundant. This prevented a wrong "collapse this duplication" recommendation and correctly judged the PR as right-sized. - Verification ran incrementally and at the right boundaries:
tsc/lint/test/fallow dead-codeon the branch before merging, then CI on the merge SHA, the release tag viarelease_watch, and finally the published npm version — no end-loaded verification gap.
What caused friction (agent side)
missing-context— chose the release-please merge method by sampling two oldchore: release maincommits (821403f9,3bca3468) that happened to predate the rebase switch, instead of checking the authoritative sources:defaultMergeMethod: rebase(.pi/extensions/pi-github-tools/config.json), the most-recent releases (2de8bf49,279f0410— both linearparents=1), or the ship-prompt guidance (ship-no-issue.md:48,ship-issue.md:111). The rebase convention was set deliberately incacc724f("chore: default release-please PR merges to rebase"); every release since isparents=1. Impact: release #436 landed as merge bubble57561321(parents=2) — the first non-linear release sincecacc724f, contradicting the documented convention. Already pushed, tagged, and published, so not cleanly reversible; no functional harm (14.0.1 published correctly), but a permanent history-shape inconsistency.other—npm view(to confirm the published version) was blocked by the repo's pnpm-only guard. Impact: one wasted tool call; recovered immediately by reading the registry viacurl. No change warranted — thenever npm or npxrule already exists and the guard enforced it correctly.
What caused friction (user side)
- The PR-review closing summary echoed the operator's own gate phrasing ("any future tweaks land as commits on top") without stating that adopt-as-is meant done, nothing queued. The operator had to ask "what future tweaks?" and "am I on to /ship-issue?" to disambiguate. Opportunity, not criticism: an adopt-as-is summary should state explicitly that no follow-up work is queued.
Root cause — release-method miss
The authoritative merge-method guidance lives only inside the ship prompts (ship-no-issue.md, ship-issue.md), which were not loaded because this release was cut from an extended PR-review session, not a /ship-issue run.
With no in-context rule, the agent inferred the method from a small, unrepresentative history sample.
The release_pr_merge tool would have used rebase (per config) but refused on merge_state: UNSTABLE (release branches never get status checks), and the agent's fallback chose --merge rather than the prompts' prescribed --rebase.
Changes made
AGENTS.md— added a release-please rebase-merge invariant after the "Release batching" paragraph in the Multi-session lifecycle section: preferrelease_pr_merge, fall back togh pr merge --rebase(never--merge) on theUNSTABLE-no-checks refusal, and do not infer the method from pre-cacc724fhistory. Closes the gap for releases cut outside/ship-issue.