|
| 1 | +--- |
| 2 | +name: router-design |
| 3 | +description: 'Stage 2: Analyze reference implementations and produce design decisions from the stage 1 JSON. Reads 01-router-concepts.json and reference code, emits JSON conforming to output.schema.json.' |
| 4 | +--- |
| 5 | + |
| 6 | +# Stage 2: Design Decisions |
| 7 | + |
| 8 | +## Context |
| 9 | + |
| 10 | +You are Stage 2 of the router integration pipeline. Your job is to read the structured router concepts from Stage 1, analyze the existing reference implementations, and produce explicit design decisions that will guide code generation in Stage 3. |
| 11 | + |
| 12 | +The pipeline invokes you with `claude -p --output-format json --json-schema .claude/skills/router-design/output.schema.json`. Your final message must be a single JSON object conforming to that schema; the harness validates it and writes the CLI wrapper to `docs/integrations/<framework>/02-design-decisions.json` with the payload on `.structured_output`. |
| 13 | + |
| 14 | +## Input |
| 15 | + |
| 16 | +You receive a **framework identifier** as skill param (e.g. `angular`, `vue`, `tanstack-react-router`). |
| 17 | + |
| 18 | +Read: |
| 19 | + |
| 20 | +1. `docs/integrations/<framework>/01-router-concepts.json` — stage 1 CLI wrapper. Extract the router-concepts payload with `jq '.structured_output' <file>`. |
| 21 | +2. Reference implementations (to understand SDK patterns): |
| 22 | + - Plugin files: `packages/rum-vue/src/domain/vuePlugin.ts`, `packages/rum-react/src/domain/reactPlugin.ts`, `packages/rum-nextjs/src/domain/nextjsPlugin.ts` |
| 23 | + - Vue router: `packages/rum-vue/src/domain/router/` (all `.ts` files) |
| 24 | + - React router: `packages/rum-react/src/domain/reactRouter/` (all `.ts` files) |
| 25 | + - Next.js router: `packages/rum-nextjs/src/domain/nextJSRouter/` (all `.ts` files) |
| 26 | + - Entry points: `packages/rum-vue/src/entries/main.ts`, `packages/rum-react/src/entries/main.ts`, `packages/rum-nextjs/src/entries/main.ts` |
| 27 | + - Package configs: `packages/rum-vue/package.json`, `packages/rum-react/package.json`, `packages/rum-nextjs/package.json` |
| 28 | + - Plugin interface: `packages/rum-core/src/domain/plugins.ts` |
| 29 | + |
| 30 | +## Process |
| 31 | + |
| 32 | +### 1. Hook Selection (Deterministic) |
| 33 | + |
| 34 | +Apply these priority rules to the `hooks` array from `01-router-concepts.json`. |
| 35 | + |
| 36 | +**The integration must be client-side only.** Only consider hooks that fire on the client. Use the `access` field and `ssr` section from `01-router-concepts.json` to determine this. |
| 37 | + |
| 38 | +**Priority rules (in order):** |
| 39 | + |
| 40 | +1. `afterCancellation: true` — **required**. Never start a RUM view for a navigation that didn't occur. |
| 41 | +2. `afterRedirects: true` — **prefer**. Report the final destination, not intermediate routes. |
| 42 | +3. `afterFetch: false` AND `afterRender: false` — **prefer**. Start the view before data loading and DOM mutation so RUM events (fetch resources, long tasks, interactions) are attributed to the new view, not the previous one. |
| 43 | + |
| 44 | +Apply in order: |
| 45 | + |
| 46 | +- Filter to `afterCancellation: true`. If no hooks pass, flag as critical issue and stop. |
| 47 | +- Among those, prefer `afterRedirects: true`. |
| 48 | +- Among those, prefer `afterFetch: false` AND `afterRender: false`. |
| 49 | +- If rules conflict (no hook satisfies all), higher-priority rule wins. |
| 50 | +- If multiple hooks still tie, prefer the one that fires earliest in the lifecycle. |
| 51 | + |
| 52 | +Document which hooks were considered, which rules each passed/failed, and why the selected hook won. |
| 53 | + |
| 54 | +### 2. Wrapping Strategy (LLM Judgment) |
| 55 | + |
| 56 | +Read the selected hook's `access` field from `01-router-concepts.json`. Determine the most idiomatic way for users to integrate the plugin in this framework. |
| 57 | + |
| 58 | +Consider: |
| 59 | + |
| 60 | +- How existing plugins/libraries are typically added in this framework's ecosystem |
| 61 | +- Whether the hook needs a router instance (→ wrap the factory that creates it) |
| 62 | +- Whether the hook needs component context (→ renderless component or hook) |
| 63 | +- Whether the hook needs DI (→ provider registration) |
| 64 | + |
| 65 | +Reference patterns from existing implementations: |
| 66 | + |
| 67 | +- Vue: wraps `createRouter()` factory to get router instance for `afterEach` |
| 68 | +- React: wraps `createBrowserRouter()` factory OR wraps `useRoutes()` hook |
| 69 | +- Angular: provider with `inject(Router)` for `router.events` observable |
| 70 | + |
| 71 | +### 3. View Name Algorithm (LLM Classification) |
| 72 | + |
| 73 | +Read the selected hook's `availableApi` from `01-router-concepts.json`. Classify into one of three families (in preference order): |
| 74 | + |
| 75 | +- **`route-id`** — Framework provides the parameterized route pattern as a string. Minimal post-processing needed (e.g. strip route groups). Example: SvelteKit `route.id`. |
| 76 | +- **`matched-records`** — Framework provides matched route records (array or tree). Iterate and concatenate path segments. Handle catch-all substitution. Example: Vue `to.matched[]`, React `state.matches[]`. |
| 77 | +- **`param-substitution`** — Framework provides only the evaluated pathname + params object. Must reconstruct the route template by substituting values back with placeholders. Least preferred — heuristic and fragile. Example: Next.js `useParams()` + `usePathname()`. |
| 78 | + |
| 79 | +### 4. Target Package (LLM Judgment) |
| 80 | + |
| 81 | +Determine whether this router needs a new package or extends an existing one. |
| 82 | + |
| 83 | +- **`new-package`** — The router belongs to a framework with no existing SDK package (e.g., SvelteKit, Angular). Create `packages/rum-<framework>/`. |
| 84 | +- **`extend-existing`** — The router is an alternative router for a framework that already has an SDK package (e.g., TanStack Router is a React router → extends `rum-react`). Add files under a subdirectory within the existing package. |
| 85 | + |
| 86 | +To decide: check if `packages/rum-*` already has a package for the same UI framework (React, Vue, etc.). If yes, extend it. If no, create new. |
| 87 | + |
| 88 | +For extend-existing, also determine the subdirectory path for the new router files (e.g., `src/domain/tanstackRouter/`). |
| 89 | + |
| 90 | +### 5. Reference Implementation |
| 91 | + |
| 92 | +Select the `packages/rum-*` implementation that is closest across: |
| 93 | + |
| 94 | +- Hook subscription pattern |
| 95 | +- Wrapping strategy |
| 96 | +- Algorithm family |
| 97 | + |
| 98 | +Stage 3 reads this implementation as its primary model for code generation. |
| 99 | + |
| 100 | +### 6. SSR Handling (LLM Judgment) |
| 101 | + |
| 102 | +If `ssr.supported: true` in `01-router-concepts.json`, describe how the integration should ensure client-side-only execution. Use the `clientDetection` API from Stage 1 if available. |
| 103 | + |
| 104 | +## Output Schema |
| 105 | + |
| 106 | +Return the populated object as your final message. The pipeline invokes you with `--output-format json --json-schema output.schema.json`; the harness validates the object and writes the full CLI wrapper (with the object on `.structured_output`) to `docs/integrations/<framework>/02-design-decisions.json`. Do not write files yourself. |
0 commit comments