# Roadmap

> Update / create / reprioritise the product roadmap — add, remove, reorder milestones; renders a markdown table per milestone.

- Skill: `me2resh/roadmap` (Agent Skill)
- Install (CLI): `npx skillmds@latest add me2resh/roadmap`
- Raw SKILL.md: https://api.skillmd.com/api/skills/me2resh/roadmap/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: Product & Planning
- Author: me2resh (https://skillmd.com/u/me2resh)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/me2resh/roadmap

---


# /roadmap — Product Roadmap

Read `.claude/rules/writing-standard.md`. Use the **controlled technical writing profile** for roadmap
artefacts: keep decisions, uncertainty, and next actions clear and concise.

Manage the product roadmap as a single durable file. The skill is intentionally low-ceremony: a roadmap is a markdown file with milestones, each containing a table of items. `/roadmap` reads it, edits it, and renders it.

## Path resolution

Read the registry path via `portfolio_registry`, the per-project docs dir via `portfolio_projects_dir`, and the ideas backlog via `portfolio_ideas_backlog` — all from `.claude/hooks/_lib-portfolio-paths.sh`. Source the helper at the top of any bash block that touches those paths:

```bash
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-read-config.sh"
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-portfolio-paths.sh"
projects_dir=$(portfolio_projects_dir)
registry=$(portfolio_registry)
```

Defaults match today's single-fork layout (`./apexyard.projects.yaml`, `./projects`, `./projects/ideas-backlog.md`). Adopters in split-portfolio mode override the `portfolio.{registry, projects_dir, ideas_backlog}` keys in `.claude/project-config.json`. Don't hardcode literal `apexyard.projects.yaml` or `projects/` paths in bash blocks — the helper resolves whichever mode the adopter is in. See `docs/multi-project.md`.

**Write targets** (see me2resh/apexyard#373 + #443): paths documented as `projects/<name>/X` in this skill are canonical adopter-facing forms — implement them in bash as `"${projects_dir}/<name>/X"`. Never construct from `"${PWD}/projects/..."`, `"$(git rev-parse --show-toplevel)/projects/..."`, or a literal `./projects/...` — those break in split-portfolio v2 mode where `projects_dir` resolves to a sibling repo.

**REQUIRED per-block preamble** (see #443): Claude executes each ```bash``` block as a separate shell invocation. The `projects_dir` assignment from the Path resolution section above does NOT carry into later blocks. Every bash block that writes to a `projects/<name>/X` path MUST start with this three-line preamble so it's self-contained:

```bash
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-read-config.sh"
source "$(git rev-parse --show-toplevel)/.claude/hooks/_lib-portfolio-paths.sh"
projects_dir=$(portfolio_projects_dir)
# ... now write to "${projects_dir}/<name>/X"
```

The Path resolution section's example sources the helper *once* for documentation purposes; it does not absolve later blocks from sourcing it themselves. Treat each ```bash``` fence as a fresh process.

## Activated role

When `/roadmap` runs, activate the **[Head of Product](../../../roles/product/head-of-product.md)** role — they own roadmap prioritisation, strategic sequencing, and milestone decisions. For adding a specific item with a PRD, chain to `/write-spec` which activates the [Product Manager](../../../roles/product/product-manager.md) instead. For items that require data-driven reprioritisation, involve the [Product Analyst](../../../roles/product/product-analyst.md).

See [`.claude/rules/role-triggers.md`](../../rules/role-triggers.md) for the full activation protocol.

## Usage

```
/roadmap                          # show current roadmap
/roadmap add "OAuth login" Q3 P0  # add an item to a milestone
/roadmap remove "OAuth login"     # remove an item
/roadmap reorder                  # interactive reordering
/roadmap show Q3                  # show a single milestone
```

## File location

Every roadmap lives at `projects/<name>/roadmap.md` inside your ops repo, where `<name>` matches an entry in `apexyard.projects.yaml`. Without `--project`, the skill asks which project's roadmap to operate on, listing the registry.

## File format

```markdown
# Product Roadmap

> Last updated: YYYY-MM-DD
> Owner: @octocat

## Now (current cycle)

| ID | Item | Priority | Status | Owner | Notes |
|----|------|----------|--------|-------|-------|
| RM-001 | CSV export | P0 | in-progress | @alice | GH#42 |
| RM-002 | OAuth login | P0 | not-started | @bob | depends on AgDR-0007 |

## Next (1-3 cycles out)

| ID | Item | Priority | Status | Owner | Notes |
|----|------|----------|--------|-------|-------|
| RM-003 | Multi-currency | P1 | not-started | – | |

## Later (3+ cycles out)

| ID | Item | Priority | Status | Owner | Notes |
|----|------|----------|--------|-------|-------|

## Done

| ID | Item | Shipped | PR |
|----|------|---------|-----|
| RM-000 | Health endpoint | 2026-04-05 | #41 |
```

The default milestones are **Now / Next / Later / Done** (Now-Next-Later format). Custom milestones (Q1/Q2/Q3 or v1.0/v1.1/v2.0) are also accepted.

## Process

### show (default)

1. Read the roadmap file
2. Render each non-empty milestone as a table
3. Print a footer summary:

   ```
   12 items · 5 in Now · 4 in Next · 3 in Later · 8 Done
   ```

### add `<item>` `<milestone>` `<priority>`

1. Read the roadmap file
2. Find the next available `RM-NNN` ID
3. Append a row to the right milestone's table
4. Default `Status: not-started`, `Owner: –`
5. Update the `Last updated` date
6. Write the file back

### remove `<item-or-id>`

1. Locate the row by ID or fuzzy title match
2. Confirm before deleting
3. Remove the row, update the date

### reorder

1. List items in the chosen milestone with their position
2. Ask for the new ordering (e.g. `2,1,3,4`)
3. Rewrite the table in the new order

### move `<id>` `<new-milestone>`

1. Find the row by ID
2. Remove it from the current milestone
3. Append it to the target milestone
4. Update the date

### close `<id>`

1. Find the row by ID across Now / Next / Later
2. Move it to Done
3. Add `Shipped` date and `PR` column reference (ask user for PR # if it can't be inferred)

## Linking to GitHub Issues

If the user passes `--with-issues`, also create or update tracker issues to
mirror the roadmap. Dispatch through `tracker_create` (#670 / AgDR-0072,
extended by the #709 creator sweep) so the issues land in **this project's**
tracker — GitHub, GitLab, or a `custom` CLI. For a GitHub adopter this runs
`gh issue create` exactly as before.

### Resolve the target repo

`tracker_create`'s first argument is a concrete `owner/repo` slug — it is **not**
resolved for you. Determine it **once**, before the loop, the same way the other
creator skills (`/feature`, `/bug`, `/task`) do:

Read `.claude/session/current-ticket` to determine which repo we're working in.
If no active ticket, check `apexyard.projects.yaml` for managed projects. If only
one project, use it. If multiple, ask:

```
Which project should the roadmap issues be filed in?
```

If no projects are registered, ask for the repo in `owner/repo` format. Pass the
resolved value (e.g. `me2resh/apexyard`) as the first `tracker_create` argument —
**never** the literal `{owner/repo}` placeholder.

```bash
# Resolve + source the tracker lib once (before the loop) by walking up from cwd.
tracker_lib="$(r="$PWD"; while [ -n "$r" ] && [ "$r" != / ]; do \
  [ -f "$r/.claude/hooks/_lib-tracker.sh" ] && { echo "$r/.claude/hooks/_lib-tracker.sh"; break; }; \
  r="${r%/*}"; done)"
# shellcheck source=/dev/null
. "$tracker_lib"

# owner_repo is the value resolved above (e.g. me2resh/apexyard), NOT a literal.
owner_repo="{resolved owner/repo}"

# For each item in Now and Next without a GH link in Notes:
body_file="$(mktemp)"
printf 'Tracking issue for roadmap item %s\n' "{RM-NNN}" > "$body_file"
result="$(tracker_create "$owner_repo" "[Roadmap] {item}" "$body_file" "roadmap,{priority}")"
rc=$?
rm -f "$body_file"
if [ "$rc" -eq 3 ]; then
  # tracker.kind=none (shape-only): no tracker to create in. tracker_create
  # printed the rendered ticket body to stdout — surface it for manual /
  # external filing and skip the Notes back-write.
  echo "Tracker is 'none' (shape-only) — nothing was created in a tracker." >&2
  printf '%s\n' "$result"
elif [ "$rc" -eq 0 ] && [ -n "$result" ]; then
  ref="$(printf '%s' "$result" | jq -r '.ref')"
  # write ${ref} back into the item's Notes column
else
  echo "Issue creation failed for '{item}' — check the tracker CLI / auth." >&2
fi
```

Then write the issue reference (`${ref}`) back into the Notes column (only on the
`rc -eq 0` path above — the `kind=none` and failure paths leave Notes untouched).

## Output format (show)

```
ROADMAP — example-app — last updated 2026-04-06
================================================

NOW (current cycle)
| ID     | Item            | Priority | Status      | Owner  | Notes                |
|--------|-----------------|----------|-------------|--------|----------------------|
| RM-001 | CSV export      | P0       | in-progress | @alice | GH#42                |
| RM-002 | OAuth login     | P0       | not-started | @bob   | depends on AgDR-0007 |

NEXT (1–3 cycles out)
| ID     | Item            | Priority | Status      | Owner | Notes |
|--------|-----------------|----------|-------------|-------|-------|
| RM-003 | Multi-currency  | P1       | not-started | –     |       |

LATER
(empty)

DONE (last 5)
| ID     | Item            | Shipped    | PR  |
|--------|-----------------|------------|-----|
| RM-000 | Health endpoint | 2026-04-05 | #41 |

Summary: 3 active · 0 later · 1 done
Owner: @octocat
```

## Priorities

| Priority | Meaning |
|----------|---------|
| P0 | Must ship in current cycle |
| P1 | Should ship soon |
| P2 | Nice to have |
| P3 | Speculative |

## Status values

| Status | Meaning |
|--------|---------|
| not-started | No work yet |
| in-progress | Active work, branch/PR exists |
| blocked | Waiting on something — note in Notes column |
| in-review | PR open, awaiting review |
| done | Shipped (auto-moves to Done milestone) |

## Rules

1. **One file = one project's roadmap** — no shared roadmaps across projects
2. **IDs are stable** — `RM-001` never changes once assigned
3. **Done is append-only** — items move to Done, never out of it
4. **Update `Last updated`** on every write
5. **Confirm destructive ops** — `remove`, `reorder` ask first
6. **One roadmap per project** — always write to `projects/<name>/roadmap.md` in the ops repo
7. **Don't auto-create issues unless asked** — `--with-issues` is opt-in
8. **Preserve markdown formatting** — don't reflow rows or change column widths unnecessarily

## Related skills

- `/write-spec` — once a roadmap item is approved, write its PRD
- `/stakeholder-update` — pulls "Now" and "Done" sections to summarise progress
- `/idea` — for ideas not yet on the roadmap

---

*Part of [ApexYard](https://github.com/me2resh/apexyard) — multi-project SDLC framework for Claude Code · MIT.*

