mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 16:45:22 +00:00
108 lines
9.5 KiB
Markdown
108 lines
9.5 KiB
Markdown
---
|
|
issue: 490
|
|
issue_title: "pi-permission-system: floor other indirection wrappers (sudo/env/xargs/find -exec) to ask"
|
|
---
|
|
|
|
# Retro: #490 — floor other indirection wrappers to ask
|
|
|
|
## Stage: Planning (2026-07-12T17:44:03Z)
|
|
|
|
### Session summary
|
|
|
|
Planned Phase 10 Step 5: floor indirection wrappers (`sudo`/`env`/`xargs`/`find -exec`/`time`/`nohup`/`timeout`/`nice`/`fd -x`) to `ask`, extending #481's opaque-payload floor.
|
|
This is an evaluation issue, so the direction was confirmed via `ask_user` — the operator chose **floor-all** (over re-target-vs-floor hybrid), all listed wrappers, `find`/`fd` gated on an exec flag, plus `fd` coverage with a follow-up for other modern rewrites.
|
|
Plan filed at `docs/plans/0490-floor-indirection-wrappers.md`; follow-up #575 filed for surveying other exec-capable CLI rewrites.
|
|
|
|
### Observations
|
|
|
|
- The roadmap (`docs/architecture/architecture.md` Phase 10 Step 5) recorded an **earlier 2026-07-10 hybrid direction** ("re-target visible prefix wrappers, floor `xargs`/`find -exec`").
|
|
Today's floor-all decision **supersedes** it — the plan's Step 4 must rewrite the roadmap Step 5 note, the health-metrics "Indirection-wrapper coverage" row, and the `S5` Mermaid label, and mark the step `✅` in the implementation doc-update commit.
|
|
- AST probe (tree-sitter-bash) confirmed every wrapper parses as a **flat `command` node** with no boundary between the wrapper's own options and the inner command.
|
|
This is why re-targeting was rejected: it needs a per-wrapper option-arity table (`sudo -u X`, `env -u X`, `nice -n N`, `timeout N`), and a wrong table silently under-matches — the bypass class the package warns against.
|
|
The floor needs no such tables.
|
|
- Design decision: generalize #481's `BashCommand.opaque?: boolean` to a `wrapperKind?: "opaque-payload" | "indirection"` discriminant (illegal `{opaque, indirection}` state unrepresentable), with a sibling `<indirection-bash-wrapper>` sentinel; the `<opaque-bash-wrapper>` string is preserved byte-for-byte so #481 tests/docs stay green.
|
|
- The rename removes the `opaque` field, so all `opaque: true` test literals (in `program.test.ts`, `sync-commands.test.ts`, `bash-command.test.ts`) become compile errors and must migrate in the same (Step 1 refactor) commit — sequenced as a behavior-preserving refactor before the two `fix:` behavior steps.
|
|
- `find`/`fd` are exec-flag-conditional (bare searches run no subcommand and are common); the always-invoke set floors by `command_name` basename alone.
|
|
Accepted edge: bare `env`/`time`/`sudo -l` are floored too (least-privilege posture).
|
|
- Release: **ship independently** (Step 5 is `Release: independent`; a `fix:` that cuts its own release; not part of the "tool-kind-dispatch" batch).
|
|
- The advisory surface (`resolveBashAdvisoryCheck`) reuses the shared `resolveBashCommandCheck`, so the floor applies there for free — no separate change.
|
|
|
|
## Stage: Implementation — TDD (2026-07-12T14:04:00Z)
|
|
|
|
### Session summary
|
|
|
|
Implemented all four planned TDD cycles: a behavior-preserving refactor generalizing #481's `BashCommand.opaque?: boolean` to a `wrapperKind?: "opaque-payload" | "indirection"` discriminant, then two `fix:` commits adding the indirection floor (always-invoke `INDIRECTION_WRAPPER_NAMES`; `find`/`fd` exec-flag-gated `EXEC_CONDITIONAL_WRAPPERS`), then the docs/roadmap commit.
|
|
Test count went 2348 → 2374 (+26); `check`, root `lint`, and `fallow dead-code` all green.
|
|
Pre-completion reviewer returned **PASS** — ready for `/ship-issue`.
|
|
|
|
### Observations
|
|
|
|
- The floor half needed no code change beyond Step 1: the `WRAPPER_SENTINEL` `Record<WrapperKind, string>` was defined with both keys in the refactor, so once the classifier emitted `"indirection"`, `resolveBashCommandCheck` floored it automatically.
|
|
This made Step 2's floor tests green on arrival — the only new code was the classifier arm.
|
|
- **Deviation 1 (test omissions):** the plan floated an optional advisory indirection case in `bash-advisory-check.test.ts` and a `find`/`fd` floor case in `bash-command.test.ts` (Step 3).
|
|
Omitted both as redundant — the advisory path reuses `resolveBashCommandCheck`, and the `find`/`fd` floor is the identical `wrapperKind: "indirection"` path already covered by the Step 2 `sudo` floor test.
|
|
The distinguishing new behavior (exec-flag gating) is covered by the `program.test.ts` classifier tests.
|
|
Reviewer agreed the omissions are sound.
|
|
- **Deviation 2 (unplanned file):** updated `docs/retro/phase-10-decide-once-dispatch.md` — a grep found it recorded the superseded 2026-07-10 "re-target" direction as the roadmap's scheduling record; appended a supersession marker pointing to the plan.
|
|
Reviewer classified it a legitimate reverse-documentation fix.
|
|
- **Infra note:** the `rumdl-fmt` pre-commit hook failed to reinstall on the docs commit (transient `No route to host` fetching setuptools).
|
|
Ran `pnpm exec rumdl fmt` + `rumdl check` manually (clean), then committed the docs step with `--no-verify`.
|
|
Code commits were unaffected (markdown hooks skip when no `.md` files are staged).
|
|
- Roadmap Step 5 marked `✅` on both the heading and the `S5` Mermaid node in the docs commit (not deferred to ship), per the package skill.
|
|
- Reviewer verdict: **PASS**.
|
|
No warnings.
|
|
|
|
## Stage: Final Retrospective (2026-07-13T01:16:50Z)
|
|
|
|
### Session summary
|
|
|
|
Shipped #490 across three stages (plan → TDD → ship) with no rework: four commits (one `refactor`, two `fix`, one `docs`), +26 tests, PASS review, released as `pi-permission-system-v20.4.1`.
|
|
The session's spine was an evaluation-issue direction gate resolved via two `ask_user` rounds, then a mechanical, well-sequenced TDD execution that fell out cleanly because the design was grounded up front by an AST probe.
|
|
Friction was near-zero; the only agent-side slip was a wrong hand-typed timestamp in the TDD stage note, and the only external hiccup was a transient pre-commit-hook install failure that was handled correctly.
|
|
|
|
### Observations
|
|
|
|
#### What went well
|
|
|
|
- **Two-round `ask_user` absorbed a freeform operator addition.**
|
|
The wrapper-set question returned the multi-select plus freeform text ("fd, maybe other modern core tool rewrites").
|
|
The second round turned that into a crisp design decision (`find`/`fd` gated on an exec flag vs. always-invoke) and a filed follow-up (#575) — no guessing, no scope bleed.
|
|
- **Disposable AST probe grounded the design before committing to it.**
|
|
A throwaway `tree-sitter-bash` script confirmed every wrapper parses as a flat `command` node with no option/inner-command boundary — the concrete evidence that killed the re-target approach and justified floor-all.
|
|
This is the testing skill's "write a disposable exploratory script first" pattern paying off.
|
|
- **Design sequencing made later steps free.**
|
|
Defining `WRAPPER_SENTINEL` as a `Record<WrapperKind, string>` with both keys during the Step 1 refactor meant Step 2's floor behavior fell out with zero new gate code once the classifier emitted `"indirection"`.
|
|
- **Incremental verification throughout.**
|
|
`pnpm run check` / `vitest` ran after each TDD step, full suite + root `lint` + `fallow dead-code` after the last — no end-of-session verification pile-up.
|
|
|
|
#### What caused friction (agent side)
|
|
|
|
- `other` — the TDD stage note was written with a hand-typed timestamp (`2026-07-12T14:04:00Z`) that is earlier than the planning note (`17:44Z`) and the ship CI (`18:19Z`), so the breadcrumb ordering is internally inconsistent.
|
|
Impact: none functional — a misleading timestamp in one stage note; no rework.
|
|
Self-identified during this retro.
|
|
|
|
#### What caused friction (user side)
|
|
|
|
- None.
|
|
The operator's freeform "fd, maybe other modern rewrites" addition was a reasonable extension, cleanly resolved by a second `ask_user` round rather than a correction.
|
|
|
|
#### External / infra
|
|
|
|
- The `rumdl-fmt` prek hook could not reinstall during the two `docs:` commits (transient `No route to host` fetching `setuptools` via `uv`).
|
|
Handled correctly: ran `pnpm exec rumdl fmt` + `rumdl check` manually (clean), then committed with `--no-verify`.
|
|
This is a hook *install* failure (infra), not a hook *check* failure (a real lint issue) — the distinction is what made `--no-verify` the right call rather than a bypass of a real gate.
|
|
- The release-please PR surfaced `UNSTABLE` with a CI check still `IN_PROGRESS` (not the empty-rollup no-checks case); handled per `/ship-issue` step 6.4 — waited for the check via `ci_watch`, then retried `release_pr_merge` (succeeded), rather than falling back to `gh pr merge` while the check ran.
|
|
|
|
### Diagnostic details
|
|
|
|
- **Model-performance correlation** — one subagent dispatch: `pre-completion-reviewer` on `anthropic/claude-sonnet-5`, doing judgment-heavy review (design, cross-step invariants, deviation assessment).
|
|
Appropriate match; no mismatch.
|
|
- **Escalation-delay tracking** — no `rabbit-hole` friction; no sequence approached 5 consecutive tool calls on one error.
|
|
- **Unused-tool detection** — none; `colgrep` (planning exploration) and the disposable AST probe were the right tools and were used.
|
|
- **Feedback-loop gap analysis** — no gap; verification was incremental after every change, not deferred to the end.
|
|
|
|
### Changes made
|
|
|
|
1. `AGENTS.md` (after the `committed`-hook sentence in the Commits section) — added a note distinguishing a `prek` hook *install* failure (network error building the hook env → run the gate manually, then commit `--no-verify`) from a hook *check* failure (a real gate → fix it, never `--no-verify` past it).
|
|
2. `packages/pi-permission-system/docs/retro/0490-floor-indirection-wrappers.md` — this Final Retrospective stage entry.
|