# Route Ingredient Planning

> Multi-ingredient planning for routes that must combine named roads, desired road classes, repeated water/fuel cadence, multiple POIs, exclusions, or several mandatory anchors. Use for "must ride", "avoid", repeated stops, fuel-stop tours, or complex ingredient ordering before generation; use route-poi-stops for a single parking/start-anchor search.

- Skill: `esemianczuk/route-ingredient-planning` (Agent Skill, multi-file: 3 files)
- Install (CLI): `npx skillmds@latest add esemianczuk/route-ingredient-planning`
- Raw SKILL.md: https://api.skillmd.com/api/skills/esemianczuk/route-ingredient-planning/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Coding & Dev Tools
- Author: Esemianczuk (https://skillmd.com/u/esemianczuk)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/esemianczuk/route-ingredient-planning

---


# Route Ingredient Planning

## Use when

Use before generation when the request combines named roads, mandatory POIs, stop cadence, multiple anchors, exclusions, or segment-specific qualities.

## Do not use when

Do not use for a simple single-anchor loop, a single parking/start-anchor search, or an ordinary point-to-point request with no mandatory ingredients.

Do not use this as the primary skill for regional climb counts, climb extrema, selected-climb detail, or a route defined by catalog climbs. Use climb-route-intelligence, which passes its selected climb ids into this shared planner contract.

Prefer an explicit ingredient plan before trying a large generation.

## Workflow

1. Resolve every explicit named start, end, and intermediate anchor with `route.geocode_locations`. For an area-only loop such as "around Madison," pass the geocoded area and use its center as the planning start; the backend also infers this anchor when only `area.center` is supplied.
2. Plain explicit/geocoded anchors do not need POI or road discovery. Put them in user-requested order and call `route.generate_multi_point_route` exactly once with `count: 1` unless alternatives were requested. `mode` and `generation_mode` are compatibility aliases; the ordered waypoints determine the geometry. Obey the returned `completion.follow_up_policy`: a successful route is terminal for this response, so report its actual distance instead of tuning it with a second generation call.
3. For a brand-new route with any mandatory support stop, cadence, named road, or mixed discoverable ingredient constraint, call `route.plan_ingredient_options` before generation.
   For an area route with a stop count but no user-specified cadence, use `route_mode: "loop"`, the target distance, and a count-only need such as `{"class":"water","target_count":3}`. Do not invent `spacing_m` or `min_spacing_m`; the planner distributes repeated stops across even interior route slots. Supply spacing only when the user actually requested an interval or minimum gap.
4. Use the planner's recommended pack when the user delegated the choice. The planner may rerank its sidecar packs with cached shortest-path feasibility checks before choosing it. An unresolved mandatory ingredient always makes the pack non-executable: a partial pack with one water stop is never a valid recommendation for a three-water-stop request, even when the user delegated stop choice. If the planner reports a meaningful compromise, network-feasibility risk, or no executable pack, explain it before generating.
5. Inspect `generation_contract` before generation. Continue only when `recommended_next_call` is present and `external_generation_call_budget` is `1`; copy its compact `{ingredient_plan_ref: ...}` arguments verbatim into exactly one `route.generate_multi_point_route` call. Do not expand the reference or add waypoints, distance, `generation_mode`, `variants`, or other fields. The server owns the canonical plan. If the call is null or the budget is `0`, do not copy candidates or option-pack waypoints by hand and do not call any generation tool. Correct a missing area/start input once or explain the reported shortfall.
6. Treat bounded fallback and distance calibration behind an ordinary plan reference as transparent server-side recovery; they must not be called manually by the model. The server may use up to two internal same-pack calibration passes when all ingredients were reached but distance alone missed tolerance. A climb-only `distance_fill` plan is different: preserve the single ranked climb need and its min/max/tolerance fields, then let the server choose a target-fitting CH chain with zero expensive generation jobs. Never synthesize a second waypoint plan, generate a baseline route first, expand one ranked need into several climb ids, or add planned ingredients one at a time.
7. Use the returned default 3D profile artifact as the route visual. It should contain normal climb callouts and the verified support-stop POI markers; do not make an extra render call unless the user requests another view or the default artifact is missing.
8. Report the selected ingredients and the returned `ingredient_verification`, including partial, missed, substituted, and co-satisfied roles. If verification is `verified`, report the route as complete and do not offer a distance retry merely because the actual distance differs within the accepted tolerance. Keep route/workspace IDs as plain identifiers, never fabricated `sandbox:` or file links.

Use these exact tool paths; do not invent intermediate planner names:

- New loop or point-to-point route with water, fuel, cafes, restrooms, parks, parking, named roads, or mixed mandatory ingredients: `route.geocode_locations` when needed -> `route.plan_ingredient_options` -> exactly one `route.generate_multi_point_route`.
- Three or more explicit named/geocoded anchors with no discoverable road or POI requirement: `route.geocode_locations` for each unresolved place -> exactly one `route.generate_multi_point_route`; do not call `route.plan_ingredient_options`.
- Existing stored route that needs support stops planned along its current corridor: `route.plan_water_stops` or `route.plan_poi_stops`.
- Existing stored route that needs one confirmed stop inserted: search the local cache, then use `route.add_poi_stop`.
- Required named roads without other mandatory POIs may use `route.plan_ingredient_options` followed by the returned generation call. Use `route.generate_named_road_route` only when the planner explicitly recommends that specialized path.

`route.plan_water_stops` and `route.plan_poi_stops` require an existing stored route. They are follow-up tools, not pre-generation planners.

For requests such as "three water stops, one of them a park", encode the requirements together in `poi_needs`. One selected stop may co-satisfy multiple roles. Do not search water and parks independently and hope the results line up.

Do not use live Overpass before the ingredient planner. The planner owns cache-first candidate selection and reports whether a fallback is needed.

Use [references/agent-recipes.md](references/agent-recipes.md) for multi-stop and desired-road flows.

## Postconditions

- The plan reports resolved, partial, substituted, and missing ingredients.
- Mandatory anchors are ordered before route generation and represented by locked waypoints or a stable ingredient pack id.
- Generation starts only when the returned plan is feasible or the user accepts a stated compromise.
- A new-route request performs at most one expensive route-generation job after planning; a climb `distance_fill` plan instead performs zero expensive jobs and reports its bounded cheap CH legs separately.
- The MCP tool may make one bounded internal fallback attempt for a clearly infeasible primary pack, but the model still makes exactly one external generation call.
- The final response identifies any co-satisfied role, such as which water stop is also the park.
- Treat a verified stop within the returned waypoint tolerance (normally at most about 100 m) as part of the route. Describe that small access offset as on-route access, not as a reason to offer another full reroute. Offer a corrective edit only when verification marks the stop missed/partial or the offset is materially larger.

