mirror of
https://bitbucket.org/siakitem/my-pi.git
synced 2026-08-28 08:35:57 +00:00
99 lines
8.6 KiB
Markdown
99 lines
8.6 KiB
Markdown
---
|
||
issue: 282
|
||
issue_title: "Extract ToolPreviewFormatter from tool-input-preview.ts"
|
||
---
|
||
|
||
# Retro: #282 — Extract ToolPreviewFormatter from tool-input-preview.ts
|
||
|
||
## Stage: Planning (2026-05-30T18:00:00Z)
|
||
|
||
### Session summary
|
||
|
||
Produced a numbered implementation plan for extracting a `ToolPreviewFormatter` class from the flat `tool-input-preview.ts` module and threading it through the gate descriptor chain.
|
||
The plan covers 6 TDD cycles: extract the class, thread through `describeToolGate`/`formatAskPrompt`, wire construction in `PermissionGateHandler`, remove the module-level `vi.mock` in `permission-prompts.test.ts`, and update architecture docs.
|
||
Referenced the Phase 1 roadmap in the architecture doc and confirmed #285 (handleToolCall decomposition) is already completed.
|
||
|
||
### Observations
|
||
|
||
- The architecture doc's roadmap was comprehensive and directly translatable to a concrete implementation plan.
|
||
The dependency ordering (#285 before Phase 1 step 2) was verified correct by checking the current code — `permission-gate-handler.ts` already has the decomposed pipeline.
|
||
- The existing `tool.ts` gate test (`test/handlers/gates/tool.test.ts`) and `permission-prompts.test.ts` both need formatter injection but in different ways:
|
||
`tool.test.ts` needs a real formatter instance for `describeToolGate`; `permission-prompts.test.ts` needs to replace its module-level mock with direct injection.
|
||
- The `permission-prompts.test.ts` mock removal is not purely mechanical — tests that assert `toHaveBeenCalledWith` on the mocked `formatToolInputForPrompt` need rework to assert on the real result string.
|
||
The plan calls this out explicitly in step 5.
|
||
- Included `toolInputLogPreviewMaxLength` in `ToolPreviewFormatterOptions` even though the issue only lists two fields, because log-formatting methods (`formatGenericToolInputForLog`, `getToolInputPreviewForLog`, `getPermissionLogContext`) use it and they're all moving to the class.
|
||
If #266 decides not to expose it in config, the field defaults to 1000 and remains internal.
|
||
- No ambiguity worth asking the user about — the issue proposed clear steps.
|
||
|
||
## Stage: Implementation — TDD (2026-05-30T22:30:00Z)
|
||
|
||
### Session summary
|
||
|
||
Extracted `ToolPreviewFormatter` from `tool-input-preview.ts` and threaded it through the gate descriptor chain in 4 commits (test step 1, refactor steps 2–5 combined, style fix, docs).
|
||
All 68 test files pass with 1527 tests, a net gain of 7 tests over the 1520 baseline.
|
||
The `vi.mock` in `permission-prompts.test.ts` was removed; the formatter is now injected directly.
|
||
|
||
### Observations
|
||
|
||
- **Plan deviation — steps 2–5 folded into one commit.**
|
||
Removing the 7 config-dependent exports from `tool-input-preview.ts` immediately broke `tool.ts`, `permission-prompts.ts`, and their tests at the TypeScript level, making it impossible to commit the extraction without simultaneously updating all consumers.
|
||
The intermediate state was uncompilable, so the extraction, threading, test updates, and `vi.mock` removal all landed in one refactor commit.
|
||
Noted in the commit body.
|
||
- **ESLint `prefer-nullish-coalescing` in `sanitizeInlineText`.**
|
||
The `maxLength !== undefined ? maxLength : default` ternary in `tool-preview-formatter.ts` was caught by the pre-commit hook; fixed before committing by collapsing to `maxLength ?? this.options.toolTextSummaryMaxLength`.
|
||
- **Biome `useTemplate` warnings.**
|
||
Two string-concatenation lints in `tool-preview-formatter.test.ts` required a manual edit (unsafe auto-fix); patched with a separate `style:` commit.
|
||
- **Pre-completion reviewer WARNs (intentional):**
|
||
- `formatAskPrompt` accepts the full `ToolPreviewFormatter` rather than a narrower `{ formatToolInputForPrompt }` interface — documented in the plan as intentional for forward compatibility.
|
||
- `formatAskPrompt` silently returns empty preview when `formatter` is `undefined` — documented in the plan as safe default behavior.
|
||
- Pre-completion reviewer verdict: **PASS**.
|
||
|
||
## Stage: Final Retrospective (2026-05-31T02:49:40Z)
|
||
|
||
### Session summary
|
||
|
||
Shipped issue #282 cleanly: synced, ran root-level `pnpm run lint` and `pnpm fallow dead-code`, pushed, watched CI to `success`, and closed the issue with an implementation summary.
|
||
No release-please PR appeared because the change is a `refactor:` with no `feat:`/`fix:` commits — these changes will release with the next semantic commit to `pi-permission-system`.
|
||
This retrospective spans all three stages (Planning, TDD, Ship).
|
||
|
||
### Observations
|
||
|
||
#### What went well
|
||
|
||
- The ship stage was friction-free: every gate (`lint`, `fallow dead-code`, CI, issue close) passed on the first attempt.
|
||
- Incremental verification during TDD was strong — `pnpm run check` ran immediately after the export-removal edit and surfaced the three broken consumers (`tool.ts`, `permission-prompts.ts`, and their tests) at once, which is what made the steps 2–5 fold an obvious, deliberate decision rather than a surprise.
|
||
- The `pre-completion-reviewer` subagent caught the two `formatAskPrompt` design WARNs and correctly classified them as intentional-per-plan, so no churn resulted.
|
||
|
||
#### What caused friction (agent side)
|
||
|
||
1. `missing-context` — during Planning, the plan file added a `[#282]:` reference-link definition for the plan's own issue number, but the body never links to `[#282]` (a plan does not reference itself).
|
||
This tripped markdownlint MD053 (unused reference) and was not caught until the TDD baseline ran `pnpm run lint`, forcing a fixup commit (`b4c4b52a docs: fix unused link reference in plan 0282`).
|
||
The pre-commit hook runs `rumdl fmt` (formatting) but not `rumdl check` (linting), so the Planning commit passed its hook with the latent failure.
|
||
This is the **second** occurrence of link-reference-definition trouble in adjacent sessions — #285 needed `1e05657e docs(retro): remove duplicate link reference definitions in retro file`.
|
||
Impact: one fixup commit per session; user-caught risk avoided only because the next stage happened to lint.
|
||
2. `wrong-abstraction` — during TDD, a multi-edit on `tool-input-preview.ts` removed `getNonEmptyString` from the top-level import and replaced `getPromptPath`'s body with an inline `require("./common")` call instead of simply keeping the import.
|
||
Self-identified immediately by reading the file after the edit; fixed in two follow-up edits before any commit.
|
||
Impact: ~2 extra edits, no commit churn.
|
||
3. `missing-context` (minor) — during Planning, the agent tried to read the colgrep skill at `.pi/skills/colgrep/SKILL.md` and got `ENOENT`; the skill actually lives at `packages/pi-colgrep/skills/colgrep/SKILL.md`.
|
||
Most package skills sit under `.pi/skills/`, so the guessed path was a reasonable but wrong default.
|
||
Impact: one failed read, no rework.
|
||
|
||
#### What caused friction (plan side)
|
||
|
||
1. `premature-convergence` — the plan split the extraction (step 2, "pure extraction… not yet used by any consumer") from the consumer threading (step 4), but removing the seven exports from `tool-input-preview.ts` breaks every importer at the type level in the same commit, so the split was not buildable.
|
||
The existing `plan-issue.md` rule covers "an export that has a single call site (e.g., `index.ts`)" — it does not generalize to an export with multiple consumers plus their test files.
|
||
Impact: no rework (TDD folded steps 2–5 and noted the deviation), but the six-step structure was misleading and required a deviation note in the commit body and TDD retro.
|
||
|
||
### Diagnostic details
|
||
|
||
- **Feedback-loop gap analysis** — the Planning stage commits the plan without running `pnpm run lint:md`; the only markdown gate at commit time is the pre-commit `rumdl fmt`, which formats but does not flag MD053.
|
||
The unused link reference therefore survived until the TDD baseline lint.
|
||
Addressed indirectly by the markdown-conventions rule below (cheaper than adding a lint step to the Planning prompt).
|
||
- **Model-performance correlation** — the only subagent across all stages was the TDD-stage `pre-completion-reviewer` (judgment-heavy review); appropriate match, no mismatch.
|
||
- **Escalation-delay / unused-tool** — no rabbit-holes; no error sequence exceeded two tool calls; no missing subagent dispatch.
|
||
|
||
### Changes made
|
||
|
||
1. `.pi/skills/markdown-conventions/SKILL.md` — extended the reference-style links bullet with a link-reference hygiene sub-rule: every `[#N]:` definition needs a matching `[#N]` body reference (MD053), and do not define a link for the doc's own issue number.
|
||
2. `.pi/prompts/plan-issue.md` — broadened the TDD Order export-removal rule from "single call site" to any export removal, folding the extraction plus all consumer and consumer-test updates into one step regardless of call-site count.
|