|
| 1 | +# Router Integration Pipeline β Design Spec |
| 2 | + |
| 3 | +## Overview |
| 4 | + |
| 5 | +A fully automated Claude Code skill pipeline that generates draft Browser SDK router integration PRs from framework public documentation. The pipeline fetches framework router docs, analyzes concepts, maps them to existing SDK patterns, generates a complete package with tests, and opens a draft PR β with no human intervention until the PR is ready for review. |
| 6 | + |
| 7 | +## Invocation |
| 8 | + |
| 9 | +``` |
| 10 | +/router:pipeline <framework> <doc-url> [<doc-url>...] |
| 11 | +``` |
| 12 | + |
| 13 | +Example: |
| 14 | +``` |
| 15 | +/router:pipeline svelte https://svelte.dev/docs/kit/routing |
| 16 | +``` |
| 17 | + |
| 18 | +## Architecture |
| 19 | + |
| 20 | +### Approach: Hybrid β Orchestrator + Stage Skills |
| 21 | + |
| 22 | +Six skills total: one orchestrator that chains five stage skills sequentially. Each stage produces a durable markdown artifact in `docs/integrations/<framework>/`. Individual stage skills can be re-run independently. |
| 23 | + |
| 24 | +### Pipeline Flow |
| 25 | + |
| 26 | +``` |
| 27 | +/router:pipeline |
| 28 | + β |
| 29 | + ββ /router:fetch-docs β 01-router-concepts.md |
| 30 | + β ββ EXIT if incompatible framework |
| 31 | + β |
| 32 | + ββ /router:analyze β 02-sdk-mapping.md |
| 33 | + β ββ EXIT if critical concepts unmapped |
| 34 | + β |
| 35 | + ββ /router:design β 03-design-decisions.md |
| 36 | + β |
| 37 | + ββ /router:generate β 04-generation-manifest.md + packages/rum-<framework>/ |
| 38 | + β |
| 39 | + ββ /router:pr β Draft PR on GitHub |
| 40 | +``` |
| 41 | + |
| 42 | +No human gates. Early exits produce an `EXIT.md` alongside whatever artifacts were written. |
| 43 | + |
| 44 | +## Artifacts |
| 45 | + |
| 46 | +All artifacts live in `docs/integrations/<framework>/`. |
| 47 | + |
| 48 | +### Source Reference Convention |
| 49 | + |
| 50 | +Every factual claim in every artifact is inline-linked to its source β either an external doc URL or an internal file path with line range. Unsourced claims are explicitly marked as *inferred: \<reasoning\>*. |
| 51 | + |
| 52 | +Example: |
| 53 | +```markdown |
| 54 | +Routes use [`:param` syntax](https://svelte.dev/docs/kit/routing#dynamic-parameters) |
| 55 | +for dynamic segments, equivalent to Angular's |
| 56 | +[`:id` in route config path](packages/rum-angular/src/domain/angularRouter/startAngularView.ts#L15-L28). |
| 57 | +``` |
| 58 | + |
| 59 | +### `01-router-concepts.md` (Stage 1 output) |
| 60 | + |
| 61 | +Structured extraction from framework docs: |
| 62 | + |
| 63 | +- **Route definition format** β config object, file-based, decorators |
| 64 | +- **Dynamic segment syntax** β `:id`, `[id]`, `{id}`, etc. |
| 65 | +- **Catch-all/wildcard syntax** β `*`, `**`, `[...slug]`, etc. |
| 66 | +- **Navigation lifecycle hooks** β which events fire and when |
| 67 | +- **Navigation lifecycle timing** β specifically: where in the lifecycle do redirects resolve, and where do data fetches (loaders/resolvers) execute? The SDK wants to start a view as early as possible but after redirects and before both data fetches (loaders/resolvers) and component rendering, so that all resource loading and rendering work is attributed to the correct view. If no single hook satisfies all three constraints (after redirects, before data fetches, before render), document the trade-off and rank the options. Reference how existing integrations solve this: Angular uses [`GuardsCheckEnd`](packages/rum-angular/src/domain/angularRouter/provideDatadogRouter.ts) (after guards/redirects, before resolvers), Vue uses [`afterEach`](packages/rum-vue/src/domain/router/vueRouter.ts) (after everything), React uses [`subscribe`](packages/rum-react/src/domain/reactRouter/createRouter.ts) (after state change). |
| 68 | +- **Route matching model** β nested vs flat, outlets, layouts |
| 69 | +- **Programmatic navigation API** β how router exposes state |
| 70 | +- **`compatible`** β boolean flag. `false` if the framework lacks a client-side route tree, dynamic segments, or navigation lifecycle events |
| 71 | + |
| 72 | +### `02-sdk-mapping.md` (Stage 2 output) |
| 73 | + |
| 74 | +Maps each framework concept to its SDK equivalent by reading reference implementations ([rum-angular](packages/rum-angular/), [rum-react](packages/rum-react/), [rum-vue](packages/rum-vue/)): |
| 75 | + |
| 76 | +- Navigation event β equivalent of Angular's [`GuardsCheckEnd`](packages/rum-angular/src/domain/angularRouter/provideDatadogRouter.ts), Vue's [`afterEach`](packages/rum-vue/src/domain/router/vueRouter.ts), React's [`subscribe`](packages/rum-react/src/domain/reactRouter/createRouter.ts) |
| 77 | +- Dynamic segment syntax β `computeViewName` normalization strategy |
| 78 | +- Catch-all handling β substitution approach |
| 79 | +- Route tree shape β traversal algorithm |
| 80 | +- Framework DI/plugin model β wrapping strategy (provider, hook, wrapper, plugin install) |
| 81 | +- Peer dependencies needed |
| 82 | +- Concepts marked `unmapped` if no SDK equivalent exists (with severity: `critical` or `minor`) |
| 83 | + |
| 84 | +### `03-design-decisions.md` (Stage 3 output) |
| 85 | + |
| 86 | +Design document covering: |
| 87 | + |
| 88 | +- Architecture decisions with rationale |
| 89 | +- Public API surface (what the user imports and calls) |
| 90 | +- File structure plan |
| 91 | +- Test strategy and edge cases to cover |
| 92 | +- Trade-offs and alternatives considered |
| 93 | + |
| 94 | +### `04-generation-manifest.md` (Stage 4 output) |
| 95 | + |
| 96 | +Listing of every generated file: |
| 97 | + |
| 98 | +- File path |
| 99 | +- Purpose (one line) |
| 100 | +- Which reference file it was modeled after (linked) |
| 101 | +- Deviations from the reference pattern and why |
| 102 | + |
| 103 | +### `EXIT.md` (on early exit only) |
| 104 | + |
| 105 | +Written when the pipeline exits before completion: |
| 106 | + |
| 107 | +- Which stage failed or exited |
| 108 | +- Reason with source links supporting the exit decision |
| 109 | +- List of artifacts produced before exit |
| 110 | + |
| 111 | +## Stage Details |
| 112 | + |
| 113 | +### Stage 1: `/router:fetch-docs` |
| 114 | + |
| 115 | +**Input:** Framework name + doc URL(s) |
| 116 | +**Output:** `01-router-concepts.md` |
| 117 | + |
| 118 | +Fetches and parses the provided URLs. Extracts the structured summary described above. Performs compatibility check β flags frameworks that lack standard routing concepts (no client-side route tree, no dynamic segments, no navigation events). Examples of incompatible frameworks: Shopify Hydrogen (server-only loaders), Salesforce Lightning (proprietary component model). |
| 119 | + |
| 120 | +### Stage 2: `/router:analyze` |
| 121 | + |
| 122 | +**Input:** `01-router-concepts.md` + reference implementations in `packages/rum-angular/`, `packages/rum-react/`, `packages/rum-vue/` |
| 123 | +**Output:** `02-sdk-mapping.md` |
| 124 | + |
| 125 | +Reads reference implementations to understand the common contract: |
| 126 | +- [Plugin interface](packages/rum-core/src/domain/plugins.ts) β `RumPlugin` with `onInit`/`onRumStart` |
| 127 | +- [Public API](packages/rum-core/src/boot/rumPublicApi.ts) β `startView()` method |
| 128 | +- `computeViewName()` implementations across all three reference packages |
| 129 | +- Navigation event subscription patterns |
| 130 | + |
| 131 | +Maps each concept from Stage 1 to an SDK equivalent. Exits if critical concepts (navigation event, route tree access) have no mapping. |
| 132 | + |
| 133 | +### Stage 3: `/router:design` |
| 134 | + |
| 135 | +**Input:** `01-router-concepts.md` + `02-sdk-mapping.md` |
| 136 | +**Output:** `03-design-decisions.md` |
| 137 | + |
| 138 | +Produces the design document. Synthesizes the mapping into concrete decisions: which files to create, what the public API looks like, how tests are structured. References both the framework docs and the SDK patterns that informed each decision. |
| 139 | + |
| 140 | +### Stage 4: `/router:generate` |
| 141 | + |
| 142 | +**Input:** All previous artifacts + reference implementations |
| 143 | +**Output:** `packages/rum-<framework>/` + `04-generation-manifest.md` |
| 144 | + |
| 145 | +Generates the package structure: |
| 146 | + |
| 147 | +``` |
| 148 | +packages/rum-<framework>/ |
| 149 | +βββ src/ |
| 150 | +β βββ entries/ |
| 151 | +β β βββ main.ts |
| 152 | +β βββ domain/ |
| 153 | +β β βββ <framework>Plugin.ts |
| 154 | +β β βββ <framework>Plugin.spec.ts |
| 155 | +β β βββ <framework>Router/ |
| 156 | +β β β βββ start<Framework>View.ts |
| 157 | +β β β βββ start<Framework>View.spec.ts |
| 158 | +β β β βββ types.ts |
| 159 | +β β β βββ <integration-point>.ts |
| 160 | +β β βββ error/ |
| 161 | +β β βββ add<Framework>Error.ts |
| 162 | +β β βββ add<Framework>Error.spec.ts |
| 163 | +β βββ test/ |
| 164 | +β βββ initialize<Framework>Plugin.ts |
| 165 | +βββ package.json |
| 166 | +βββ tsconfig.json |
| 167 | +βββ README.md |
| 168 | +``` |
| 169 | + |
| 170 | +**Code generation approach:** The agent reads reference implementations as examples (not templates). It uses `02-sdk-mapping.md` and `03-design-decisions.md` to determine the specific logic for: |
| 171 | +- `<integration-point>.ts` β shaped by which navigation hook to subscribe to |
| 172 | +- `computeViewName()` β shaped by dynamic segment and catch-all syntax |
| 173 | +- Plugin exposure β shaped by framework DI/plugin model |
| 174 | + |
| 175 | +**Test generation:** Derived from two sources: |
| 176 | +1. Common edge cases shared across all routers (from reference spec files): static paths, single/nested dynamic segments, empty/layout routes, catch-all/wildcard, duplicate navigation filtering, initial navigation |
| 177 | +2. Framework-specific cases from `01-router-concepts.md` |
| 178 | + |
| 179 | +### Stage 5: `/router:pr` |
| 180 | + |
| 181 | +**Input:** Generated package + all artifacts |
| 182 | +**Output:** Draft PR on GitHub |
| 183 | + |
| 184 | +- Creates branch: `<user>/<framework>-router-integration` |
| 185 | +- Two commits: |
| 186 | + 1. `π Add <framework> router integration design docs` |
| 187 | + 2. `β¨ Add <framework> router integration package` |
| 188 | +- Opens draft PR with body structured from `03-design-decisions.md`: |
| 189 | + - Summary of what was generated |
| 190 | + - Key design decisions |
| 191 | + - Link to `docs/integrations/<framework>/` for full artifact trail |
| 192 | + |
| 193 | +## Scope Boundaries |
| 194 | + |
| 195 | +### In scope |
| 196 | +- Conventional SPA/SSR frontend frameworks with standard routing: route tree, dynamic params, navigation lifecycle (e.g. Svelte, Remix, Solid, Ember) |
| 197 | +- Router integration (view tracking via `startView`) |
| 198 | +- Error handler integration (following reference pattern) |
| 199 | +- Unit tests for view name computation and navigation filtering |
| 200 | + |
| 201 | +### Out of scope |
| 202 | +- Non-conventional frameworks (Shopify Hydrogen, Salesforce Lightning, etc.) β pipeline exits early |
| 203 | +- E2E tests β not generated, left for human follow-up |
| 204 | +- Publishing/release automation |
| 205 | +- Modifications to existing packages or rum-core |
| 206 | + |
| 207 | +## Skill Location |
| 208 | + |
| 209 | +Skills live in `.claude/skills/router-integration/`: |
| 210 | + |
| 211 | +```text |
| 212 | +.claude/skills/router-integration/ |
| 213 | +βββ pipeline.md # /router:pipeline β orchestrator |
| 214 | +βββ fetch-docs.md # /router:fetch-docs |
| 215 | +βββ analyze.md # /router:analyze |
| 216 | +βββ design.md # /router:design |
| 217 | +βββ generate.md # /router:generate |
| 218 | +βββ pr.md # /router:pr |
| 219 | +``` |
| 220 | + |
| 221 | +## Stage Input Convention |
| 222 | + |
| 223 | +Each stage skill reads its inputs from the filesystem β no arguments are passed between skills except through the orchestrator's context: |
| 224 | + |
| 225 | +- **Orchestrator** receives `<framework>` and `<doc-url>` as arguments, writes them to `docs/integrations/<framework>/00-pipeline-input.md` |
| 226 | +- **Stage skills** read from `docs/integrations/<framework>/` for previous artifacts and from `packages/rum-angular/`, `packages/rum-react/`, `packages/rum-vue/` for reference implementations |
| 227 | +- **Framework name** is derived from the directory name |
0 commit comments