| 1 | # Implementation Plans |
| 2 | |
| 3 | Generated by the `improve` skill on 2026-07-10, planned against commit |
| 4 | `c63cb120` (Slidev v52.16.0). Each plan is a **self-contained handoff** for an |
| 5 | executor with zero prior context: read the plan fully before starting, run its |
| 6 | verification commands, honor its STOP conditions, and update your row below when |
| 7 | done. |
| 8 | |
| 9 | Advisor constraint: these plans were produced read-only. No source code was |
| 10 | modified. The audit that produced them is summarized in each plan's "Why this |
| 11 | matters" / "Current state". Verification baseline for the whole repo: |
| 12 | `pnpm install && pnpm build && pnpm typecheck && pnpm lint && pnpm test` |
| 13 | (tests require a prior `pnpm build`; plan 003 adds a `verify` aggregate). |
| 14 | |
| 15 | ## Execution order & status |
| 16 | |
| 17 | Ordered by leverage and dependencies: cheap/low-risk wins first, then |
| 18 | high-confidence security + correctness, then larger refactors. You may cherry-pick |
| 19 | — only the edges in "Dependency notes" are hard requirements. |
| 20 | |
| 21 | | Plan | Title | Priority | Effort | Risk | Depends on | Status | |
| 22 | |------|-------|----------|--------|------|------------|--------| |
| 23 | | 001 | CI type-checks on every PR | P1 | S | LOW | — | DONE (plan file removed) | |
| 24 | | 002 | Cache the pnpm store in CI | P2 | S | LOW | — | DONE (plan file removed) | |
| 25 | | 003 | Aggregate `verify` script + document build-before-test | P2 | S | LOW | — | DONE (plan file removed) | |
| 26 | | 004 | Fix catalog drift (runtime deps on `catalog:dev`) | P2 | S | LOW | — | DONE (plan file removed) | |
| 27 | | 005 | Add contributor `AGENTS.md` | P3 | S | LOW | — | TODO | |
| 28 | | 006 | Replace `exec` shell-string with `execFile` (edit shortcut) | P2 | S | LOW | — | DONE (plan file removed) | |
| 29 | | 007 | Guarantee Chromium teardown on export failure | P1 | S | LOW | — | DONE (plan file removed) | |
| 30 | | 008 | Guard against circular `src:` slide imports | P1 | S | LOW | — | DONE (plan file removed) | |
| 31 | | 009 | `parseRangeString` lower-bound / NaN validation | P2 | S | LOW | — | TODO | |
| 32 | | 010 | Harden `getSlidePath` against unknown slide | P2 | S | LOW-MED | — | TODO | |
| 33 | | 011 | 404 on out-of-range slide-patch request | P3 | S | LOW | — | TODO | |
| 34 | | 012 | Fix no-op HMR `utils` refresh (missing `await`) | P2 | S | LOW | — | TODO | |
| 35 | | 013 | Free port + cleanup for build's temp servers | P2 | S-M | MED | — | TODO | |
| 36 | | 014 | Confine deck-controlled file reads (snippets + `src:`) | P1 | M | MED | — | DONE (plan file removed) | |
| 37 | | 015 | Validate paths in dev-server write sinks | P1 | S-M | MED | — | DONE (plan file removed) | |
| 38 | | 016 | Confine export output path from deck `exportFilename` | P2 | S | LOW | — | TODO | |
| 39 | | 017 | Validate WebSocket Origin on privileged ws handlers | P2 | M | MED | — | TODO | |
| 40 | | 018 | Enforce `--remote` auth server-side (not just client) | P3 | M-L | MED | — | TODO | |
| 41 | | 019 | Key `getRoots()` cache by entry (multi-entry build/export) | P2 | M | MED | — | TODO | |
| 42 | | 020 | O(1) slide lookup Map (fix O(n²) TOC) | P2 | S | LOW | — | TODO | |
| 43 | | 021 | Consolidate divergent `addToTree` TOC builders | P2 | M | MED | — | TODO | |
| 44 | | 022 | Test the export pipeline (characterization tests) | P1 | M | LOW | — | TODO | |
| 45 | | 023 | Decompose god functions in `export.ts`/`cli.ts` | P3 | L | MED | 022 | TODO | |
| 46 | | 024 | Incremental HMR parse cache | P3 | L | MED | — | TODO | |
| 47 | | 025 | Guard `skills/`-vs-`docs/` drift in CI | P3 | S-M | LOW | — | TODO | |
| 48 | |
| 49 | Status values: `TODO` | `IN PROGRESS` | `DONE` | `BLOCKED` (one-line reason) | |
| 50 | `REJECTED` (one-line rationale). |
| 51 | |
| 52 | ## Recommended first wave |
| 53 | |
| 54 | If you don't run them all, start with the low-risk, high-confidence set (all clean |
| 55 | verification stories): **001, 002, 003, 004, 006, 007, 008, 014, 015** — plus |
| 56 | **022** early, because it unblocks the export refactor and de-risks 007/016/023. |
| 57 | |
| 58 | ## Dependency notes |
| 59 | |
| 60 | - **023 requires 022**: do not refactor `exportSlides`/serve handler until the |
| 61 | export characterization tests exist (023's only safety net; there are no serve |
| 62 | tests, so its Step 3 is manual + STOP-gated). |
| 63 | - **021 ↔ 022**: 021 moves `addToTree` into `@slidev/parser` as `buildTocTree`; |
| 64 | 022 tests the export TOC path. If 021 lands first, 022 tests the shared builder; |
| 65 | if 022 lands first, 021 keeps its test green. Land either order, but re-run the |
| 66 | other's tests. |
| 67 | - **018 subsumes the network-exposure parts of 015 and 017**; still land 015 |
| 68 | (path validation) and 017 (origin) as defense-in-depth regardless. |
| 69 | - **012 ↔ 024**: 012 makes the per-HMR `utils` refresh actually run (awaited); |
| 70 | 024 reduces the parse cost that makes such per-HMR work expensive. If 012's |
| 71 | cost proves high before 024 lands, see 012's STOP condition. |
| 72 | - **010 ↔ 020**: both touch `packages/client/logic/slides.ts` (`getSlide`/ |
| 73 | `getSlidePath`). Whichever lands second must preserve the other's change |
| 74 | (010 = undefined guard, 020 = lookup Map). Small merge; not a hard edge. |
| 75 | - **001, 002, 003** are the CI/DX cluster — independent but naturally reviewed together. |
| 76 | |
| 77 | ## Security cluster (findings share one theme) |
| 78 | |
| 79 | 014, 015, 016, 017, 018 all harden the **dev-server + parser trust boundary** that |
| 80 | Slidev's existing `vite/importGuard.ts` began. 014 (arbitrary file *read* via |
| 81 | snippets/`src:`) and 015 (traversal *writes*) are the highest-confidence, |
| 82 | most-actionable; 018 is the larger architectural lift (real server-side auth). |
| 83 | Frame all of them as defensive maintenance for the "untrusted deck" and |
| 84 | "exposed dev server" threat models — consistent with the maintainers' own |
| 85 | hardening intent. |
| 86 | |
| 87 | ## Findings considered and rejected (so they aren't re-audited) |
| 88 | |
| 89 | - **`export.ts:630-655` "empty catches"** — NOT a bug: a deliberate 4-location |
| 90 | Playwright-resolution fallback chain ending in a helpful `throw`. |
| 91 | - **`importGuard.ts` skipping dynamic imports with `${` template specifiers** — |
| 92 | accepted limitation; defense-in-depth atop Vite's `server.fs.strict`, does not |
| 93 | itself widen `fs.allow`. |
| 94 | - **`resolver.ts` `catch {}` blocks (`:116/148/157/162`)** — deliberate best-effort |
| 95 | resolution fallbacks; not load-bearing error swallowing. |
| 96 | - **prettier v2 (vscode) vs v3 (root)** — documented decision (`taze.config.ts:10`). |
| 97 | - **PlantUML default egress to `plantuml.com`** (`parser/src/config.ts:37`) — |
| 98 | by-design/documented; a docs note at most, not a code change. |
| 99 | - **`DEPS-02` minor version drift** (`@types/katex` 0.16 vs katex 0.17; the |
| 100 | `@hedgedoc` patch; `@lillallol/outline-pdf` bus-factor) — low leverage; monitor, |
| 101 | no dedicated plan. |
| 102 | - **Global-install E2E disabled in `smoke.yml`** — a documented pnpm-v11 |
| 103 | limitation, not a defect. |
| 104 | |
| 105 | ## Direction findings (not planned here — options for the maintainer) |
| 106 | |
| 107 | Surfaced during the audit but out of the "fix" set; each is grounded in repo |
| 108 | evidence and would be a design/spike plan if pursued: |
| 109 | |
| 110 | - **D1** Agent deck-*authoring* LM tools (write side of the read-only |
| 111 | `packages/vscode/src/lmTools.ts`, over `@slidev/parser`'s existing `stringify`/ |
| 112 | `parseSlide`). |
| 113 | - **D2** A `skills/` **generator** (`scripts/gen-skills.ts`) — the automation |
| 114 | behind plan 025's drift guard. |
| 115 | - **D3** Backend-agnostic deck introspection (extract `lmTools.ts` into CLI |
| 116 | subcommands / an MCP server). |
| 117 | - **D4** Finish per-image snapshot export (`integrations/snapshots.ts:25` TODO). |
| 118 |