Skip to content

Commit 97f65f6

Browse files
committed
router integration pipeline
1 parent 017e398 commit 97f65f6

8 files changed

Lines changed: 949 additions & 0 deletions

File tree

Lines changed: 106 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,106 @@
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.
Lines changed: 118 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,118 @@
1+
{
2+
"title": "DesignDecisions",
3+
"description": "Explicit design decisions derived from Stage 1 router concepts and reference implementations, used by Stage 3 to generate code.",
4+
"type": "object",
5+
"additionalProperties": false,
6+
"required": [
7+
"selectedHook",
8+
"wrappingStrategy",
9+
"viewNameAlgorithm",
10+
"targetPackage",
11+
"referenceImplementation",
12+
"ssr"
13+
],
14+
"properties": {
15+
"selectedHook": {
16+
"type": "object",
17+
"additionalProperties": false,
18+
"required": ["name", "rationale"],
19+
"properties": {
20+
"name": {
21+
"type": "string",
22+
"description": "Hook name from 01-router-concepts.json, selected by the deterministic priority rules."
23+
},
24+
"rationale": {
25+
"type": "string",
26+
"description": "Which rules each candidate passed/failed and why the selected hook won."
27+
}
28+
}
29+
},
30+
"wrappingStrategy": {
31+
"type": "object",
32+
"additionalProperties": false,
33+
"required": ["pattern", "target", "rationale"],
34+
"properties": {
35+
"pattern": {
36+
"enum": ["wrap-factory", "renderless-component", "provider", "wrap-hook", "other"],
37+
"description": "wrap-factory: Wrap the router creation function, subscribe to hook inside. renderless-component: Component that calls the hook during lifecycle. provider: DI provider that injects the router and subscribes to events. wrap-hook: Wrap a user-facing hook to intercept route data. other: Escape hatch for unknown patterns."
38+
},
39+
"target": {
40+
"type": "string",
41+
"description": "What specifically to wrap/provide. E.g. 'createRouter from vue-router', 'ENVIRONMENT_INITIALIZER with inject(Router)'."
42+
},
43+
"rationale": {
44+
"type": "string",
45+
"description": "Why this is idiomatic for the framework."
46+
}
47+
}
48+
},
49+
"viewNameAlgorithm": {
50+
"type": "object",
51+
"additionalProperties": false,
52+
"required": ["family", "rationale"],
53+
"properties": {
54+
"family": {
55+
"enum": ["route-id", "matched-records", "param-substitution"],
56+
"description": "route-id: Framework provides parameterized route pattern as string. Minimal post-processing. matched-records: Framework provides matched route records. Iterate and concatenate path segments. param-substitution: Framework provides evaluated pathname + params. Reconstruct route template. Least preferred."
57+
},
58+
"rationale": {
59+
"type": "string",
60+
"description": "Why this family, based on the hook's availableApi."
61+
}
62+
}
63+
},
64+
"targetPackage": {
65+
"type": "object",
66+
"additionalProperties": false,
67+
"required": ["mode", "package"],
68+
"properties": {
69+
"mode": {
70+
"enum": ["new-package", "extend-existing"],
71+
"description": "new-package: Create a new packages/rum-<framework>/ from scratch. extend-existing: Add router support to an existing package (e.g. adding TanStack Router to rum-react)."
72+
},
73+
"package": {
74+
"type": "string",
75+
"description": "Target package directory name. For new-package: 'rum-<framework>'. For extend-existing: the existing package (e.g. 'rum-react')."
76+
},
77+
"subpath": {
78+
"type": "string",
79+
"description": "Only for extend-existing. The subdirectory for this router's files within the existing package. E.g. 'src/domain/tanstackRouter/' within packages/rum-react/."
80+
}
81+
}
82+
},
83+
"referenceImplementation": {
84+
"type": "object",
85+
"additionalProperties": false,
86+
"required": ["primary", "rationale"],
87+
"properties": {
88+
"primary": {
89+
"type": "string",
90+
"description": "Which packages/rum-* to model after (e.g. 'rum-vue'). Stage 3 reads this implementation as its primary source for code patterns."
91+
},
92+
"rationale": {
93+
"type": "string",
94+
"description": "Why this is the closest match."
95+
}
96+
}
97+
},
98+
"ssr": {
99+
"type": "object",
100+
"additionalProperties": false,
101+
"required": ["handling"],
102+
"properties": {
103+
"handling": {
104+
"type": "string",
105+
"description": "How to ensure client-side-only execution. 'N/A' if ssr.supported is false in Stage 1."
106+
}
107+
}
108+
},
109+
"notes": {
110+
"type": "string",
111+
"description": "Free text for additional design context, trade-offs, unmapped concepts, or anything Stage 3 needs to know."
112+
},
113+
"exitReason": {
114+
"type": "string",
115+
"description": "Only set if Stage 2 cannot proceed (e.g., no hook satisfies afterCancellation: true). When set, all other fields may be empty stubs and the pipeline will stop."
116+
}
117+
}
118+
}
Lines changed: 85 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,85 @@
1+
---
2+
name: router-fetch-docs
3+
description: 'Stage 1: Fetch framework router documentation and extract structured routing concepts as JSON conforming to output.schema.json from an npm package URL.'
4+
---
5+
6+
# Stage 1: Fetch Router Documentation
7+
8+
## Context
9+
10+
You are Stage 1 of the router integration pipeline. Your job is to resolve package metadata, fetch framework router documentation, and extract structured routing concepts as **factual data** for later stages.
11+
12+
The pipeline invokes you with `claude -p --output-format json --json-schema .claude/skills/router-fetch-docs/output.schema.json`. Your final message must be a single JSON object conforming to that schema; the harness validates it and exposes it on `.structured_output` of the CLI wrapper written to `docs/integrations/<framework>/01-router-concepts.json`.
13+
14+
Do NOT analyze which hooks the SDK should use, recommend approaches, or compare with existing SDK integrations. Only document what the framework provides.
15+
16+
## Input
17+
18+
You receive an **npm package URL** as skill param (e.g. `https://www.npmjs.com/package/vue-router`).
19+
20+
## Process
21+
22+
### 1. Resolve Package Metadata
23+
24+
Use WebFetch on the provided npm URL to extract:
25+
26+
- **Package name** (e.g. `vue-router`, `@angular/router`)
27+
- **Framework identifier** — derive a lowercase identifier from the package name (e.g. `vue-router``vue`, `@angular/router``angular`, `@tanstack/react-router``tanstack-react-router`)
28+
- Homepage / repository URL
29+
- Keywords and description
30+
31+
Then **find router documentation URLs** — from the npm page metadata (homepage, repository links), locate the framework's official routing documentation:
32+
33+
- Check the homepage URL for docs links
34+
- Check the GitHub repository README for documentation links
35+
- Look for `/docs/`, `/guide/`, `/routing` paths on the framework's site
36+
- Collect 1-3 relevant documentation URLs focused on routing
37+
38+
The pipeline creates the artifact directory for you — do not run `mkdir` yourself.
39+
40+
### 2. Fetch Documentation
41+
42+
Before fetching the provided URLs directly, try to find LLM-friendly versions of the docs:
43+
44+
1. **Check for `llms.txt`** — Fetch `<site-root>/llms.txt` (e.g. `https://svelte.dev/llms.txt`). This file indexes markdown documentation pages designed for LLM consumption. If it exists, use it to navigate to the relevant routing pages.
45+
2. **Try `.md` suffix** — For each doc URL, try appending `.md` to the path (e.g. `https://svelte.dev/docs/kit/routing.md`). Many doc sites serve a raw markdown version this way, which is much easier to parse accurately.
46+
3. **Fall back to HTML** — If neither LLM-friendly format is available, fetch the original URLs.
47+
48+
Use the WebFetch tool for all fetches. If a URL fails, note it and continue with remaining URLs. If all URLs fail, stop without emitting a JSON object — the harness will mark the run as an error.
49+
50+
**Prefer over-fetching to under-fetching.** Fetch every routing-related page you can find — API references, guides, tutorials. It is better to fetch a page and not need it than to miss information that a later stage requires. When in doubt, fetch it.
51+
52+
### 3. Extract Router Concepts into the JSON Schema
53+
54+
Analyze the fetched documentation and populate every field in the JSON schema. Use `null` for features the framework does not support.
55+
56+
#### Sourcing Rules
57+
58+
Every leaf field has a sibling `source` field. This is **mandatory** — the schema validation fails without it.
59+
60+
- If extracted from documentation: set `source` to the URL (with anchor if possible)
61+
- If inferred from multiple sources or reasoning: set `source` to `"inferred: <one-line reasoning>"`
62+
- API names, hook names, type names — everything factual must be traceable to a specific doc page
63+
64+
#### JSON Schema
65+
66+
Read `output.schema.json` (next to this SKILL.md) for the schema and field descriptions.
67+
68+
### 4. Compatibility Assessment
69+
70+
A framework is **incompatible** if it lacks:
71+
72+
- A client-side route tree or route matching mechanism
73+
- Dynamic segment parameters
74+
- Navigation lifecycle events that can be hooked into
75+
76+
If incompatible: stop without emitting a JSON object. The harness will mark the run as an error.
77+
78+
A framework is **compatible** even if:
79+
80+
- It uses file-based routing (as long as there's a runtime route representation)
81+
- Some hooks fire later than ideal (trade-off is documented, not a blocker)
82+
83+
## Output
84+
85+
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>/01-router-concepts.json`. Do not write files yourself.

0 commit comments

Comments
 (0)