mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
feat: vendor permission system source
This commit is contained in:
@@ -0,0 +1,93 @@
|
||||
---
|
||||
issue: 435
|
||||
issue_title: "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:
|
||||
|
||||
- `stripBashCommentLines` lands in `bash-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 `#`, so `echo "#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.
|
||||
`normalizeInput` strips for the match value while preserving the original in `resultExtras.command`; `suggestBashPattern` must strip independently because `deriveSuggestionValue` (`handlers/gates/tool.ts:21`) feeds it `check.command` — the preserved original.
|
||||
The fallback `stripBashCommentLines(command) || command` keeps 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:
|
||||
|
||||
```text
|
||||
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 second `stripBashCommentLines` call in `suggestBashPattern` was *necessary* (it receives the preserved original `check.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-code` on the branch *before* merging, then CI on the merge SHA, the release tag via `release_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 old `chore: release main` commits (`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 linear `parents=1`), or the ship-prompt guidance (`ship-no-issue.md:48`, `ship-issue.md:111`).
|
||||
The rebase convention was set deliberately in `cacc724f` ("chore: default release-please PR merges to rebase"); every release since is `parents=1`.
|
||||
Impact: release #436 landed as merge bubble `57561321` (`parents=2`) — the first non-linear release since `cacc724f`, 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 via `curl`.
|
||||
No change warranted — the `never npm or npx` rule 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
|
||||
|
||||
1. `AGENTS.md` — added a release-please rebase-merge invariant after the "Release batching" paragraph in the Multi-session lifecycle section: prefer `release_pr_merge`, fall back to `gh pr merge --rebase` (never `--merge`) on the `UNSTABLE`-no-checks refusal, and do not infer the method from pre-`cacc724f` history.
|
||||
Closes the gap for releases cut outside `/ship-issue`.
|
||||
Reference in New Issue
Block a user