---
name: apt:roadmap
description: "Manage per-scope project roadmaps — milestones, phases, owners, ROADMAP.md rendering"
apt-skill-version: {{APT_VERSION}}
stage: ops
intent: configure
when_to_use: "The user wants to view, add, or update per-scope milestones/phases in the project roadmap and render ROADMAP.md."
user_invocable: true
internal: false
spawns_agent: false
agent_name: null
task_context: none
default_execution_mode: step
execution_modes:
  - auto
  - step
allowed-tools: "Bash, Read, Write, Edit, Grep, Glob"
argument-hint: "apt:roadmap [list|add-milestone|update-phase|render] [args...]"
gates: []
---

<objective>
Conversational interface for project roadmaps. Roadmaps are partitioned by
scope — each scope has its own directory with two files:

- `.aperant/roadmap/{scope}/milestones.json` — structured source of truth (AI updates this)
- `.aperant/roadmap/{scope}/ROADMAP.md` — human-rendered view (auto-generated)

The default scope is `project` (product work). Teams can add more scopes
(`framework`, `docs`, `infra`, …) to keep parallel roadmaps in the same repo
without cross-contamination. Milestone IDs must be unique across all scopes.

All files are committed to git as part of team collaboration mode (`apt:setup` →
Team Collaboration → Yes). Other team members see roadmap changes when they pull.

This skill never edits source code or touches non-roadmap state. It is purely
a wrapper around `apt-tools roadmap` commands with a friendly conversation flow.
</objective>

<process>

## 1. Detect Team Mode

```bash
node packages/framework/bin/apt-tools.mjs team status .
```

If `enabled: false`, tell the user:
```
Team mode is not enabled. Roadmap is a shared team feature. Run /apt:setup
and choose "Team Collaboration → Yes" to enable it.
```
Then exit.

## 2. Detect Roadmap State

```bash
ls -d .aperant/roadmap/*/ 2>/dev/null || echo "MISSING"
```
If any scope directory exists with a `milestones.json`, the roadmap is initialized.

**If MISSING** → offer to initialize:
```
AskUserQuestion([
  {
    question: "No roadmap found. Initialize one for this project?",
    header: "Init Roadmap",
    multiSelect: false,
    options: [
      { label: "Yes — empty roadmap", description: "Creates an empty milestones.json + ROADMAP.md you can populate" },
      { label: "Yes — bootstrap from conversation", description: "I'll ask you about goals and milestones, then create them" },
      { label: "Cancel", description: "Exit without changes" }
    ]
  }
])
```

For "Yes — empty": `node packages/framework/bin/apt-tools.mjs roadmap init .`

For "Yes — bootstrap from conversation": run **Bootstrap Flow** below.

**If roadmap exists** → show current state and offer actions.

## 3. Show Current State

Read every `.aperant/roadmap/{scope}/milestones.json` and display, grouped by scope:
```
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 ROADMAP — {project name}
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

{For each milestone:}
■ {milestone.title}  ({completed}/{total} phases)
   Goal: {milestone.goal}

   ✓ {phase.id}  {phase.title}                     {owner}
   ▶ {phase.id}  {phase.title}  ← in progress      {owner}
   ○ {phase.id}  {phase.title}                     {owner}
   ✖ {phase.id}  {phase.title}  ← blocked          {owner}
```

Use these icons: ✓ completed, ▶ in-progress, ○ planned, ✖ blocked, — skipped.

## 4. Action Menu

```
AskUserQuestion([
  {
    question: "What would you like to do?",
    header: "Roadmap",
    multiSelect: false,
    options: [
      { label: "Add milestone", description: "Create a new top-level milestone" },
      { label: "Add phase", description: "Add a phase to an existing milestone" },
      { label: "Update phase status", description: "Mark a phase as in-progress, completed, blocked, etc." },
      { label: "Reassign owner", description: "Change who owns a phase" },
      { label: "View full markdown", description: "Show .aperant/roadmap/{scope}/ROADMAP.md for a chosen scope" },
      { label: "Re-render", description: "Regenerate ROADMAP.md from milestones.json" },
      { label: "Done", description: "Exit" }
    ]
  }
])
```

### Add milestone

Ask for: id, title, goal. Then:
```bash
node packages/framework/bin/apt-tools.mjs roadmap add-milestone . \
  --id "{id}" --title "{title}" --goal "{goal}"
```

### Add phase

Show milestones, ask which one. Then ask: id, title, description, **owner** (see
Owner resolution below), blocked_by (optional, comma-separated phase IDs),
deliverables (optional, pipe-separated list). Then:
```bash
node packages/framework/bin/apt-tools.mjs roadmap add-phase . \
  --milestone "{milestoneId}" --id "{phaseId}" --title "{title}" \
  --description "{description}" --owner "{owner}" \
  --blocked-by "{blockedBy}" --deliverables "{deliverables}"
```

**Owner resolution** — before running add-phase, decide what to pass for `--owner`:

1. Check `.aperant/team.json`:
   ```bash
   test -f .aperant/team.json && cat .aperant/team.json || echo "SOLO"
   ```
2. **Team mode off or missing (`SOLO`, or `enabled: false`)** — omit `--owner`. The
   CLI defaults to the current git identity name, which is the right attribution
   for a solo developer. No question needed.
3. **Team mode on, exactly one member** — same as solo: omit `--owner` and let the
   CLI pick it up from git identity (which will match the single member).
4. **Team mode on, two or more members** — ask who owns this phase using the
   member list from `team.json`:
   ```
   AskUserQuestion([
     {
       question: "Who owns this phase?",
       header: "Phase owner",
       multiSelect: false,
       options: [
         ...teamMembers.map(m => ({ label: m.name, description: m.email })),
         { label: "Both / shared", description: "More than one owner" },
         { label: "TBD", description: "Decide later" }
       ]
     }
   ])
   ```
   Pass the chosen label as `--owner "{label}"`. Never skip the question when the
   team has multiple members — the roadmap is a shared artifact and attribution
   matters.

The same resolution applies to **Reassign owner** below.

### Update phase status

Show all phases with their current status. Ask which to update. Ask new status from: planned, in-progress, completed, blocked, skipped, deleted. Then:
```bash
node packages/framework/bin/apt-tools.mjs roadmap set-phase-status . \
  --milestone "{milestoneId}" --phase "{phaseId}" --status "{status}"
```

If marking completed, the CLI auto-stamps `completed_at`. `--owner` is also
accepted — passing it alongside preserves the status but updates the owner, which
is how **Reassign owner** works:
```bash
node packages/framework/bin/apt-tools.mjs roadmap set-phase-status . \
  --milestone "{mId}" --phase "{pId}" --status "{currentStatus}" --owner "{newOwner}"
```
Use the **Owner resolution** flow described under Add phase to pick the new owner.

Status `deleted` is destructive — it removes the phase and runs orphan-block semantics (C28 D-07). Any active or completed task pointing at this `{milestone_id, phase_id}` blocks the delete; pass `--force --reason "<text>"` to cascade-null those tasks' linkage and record a history entry. `--force` without `--reason` fails.

### Delete phase / milestone

Non-status destructive paths use dedicated subcommands:
```bash
node packages/framework/bin/apt-tools.mjs roadmap delete-phase . --milestone "{mId}" --phase "{pId}" [--force --reason "<text>"]
node packages/framework/bin/apt-tools.mjs roadmap delete-milestone . --id "{mId}" [--force --reason "<text>"]
```
Same orphan-block + cascade-null semantics as `set-phase-status --status deleted`.

### Validate roadmap references

Every task with `roadmap_ref_url` (an optional pointer into an external document — a spec, design doc, issue, or markdown anchor) can be swept for rot:
```bash
node packages/framework/bin/apt-tools.mjs roadmap validate-refs . [--offline]
```
Path refs (e.g. `docs/architecture.md#auth-flow`) resolve relative to the project root and check for an `<a id="anchor">` tag in the target file. URL refs (http/https) HEAD via curl (skip under `--offline`).

### Re-render

```bash
node packages/framework/bin/apt-tools.mjs roadmap render .
```

## 5. Bootstrap Flow (for empty projects)

If user chose "bootstrap from conversation":

1. Ask: project name, big-picture goal (one sentence).
2. Run `roadmap init` with the project name.
3. Ask: how many milestones? (1-5 typical)
4. For each milestone, ask: title, goal.
5. For each milestone, ask: how many phases? Then for each phase, ask: title, owner, deliverables.
6. After all are added, run `roadmap render` and show the result.
7. Tell the user: "Roadmap created. Other team members will see it after you commit `.aperant/roadmap/`."

## 6. Queue Docs Effects (deterministic drain)

After any write operation, queue a `roadmap.render` effect for the audit trail and drain. The drain treats `roadmap.render` as ack-only (the CLI subcommand already re-rendered the ROADMAP.md synchronously). The record call is kept for the audit trail only.

Skip this entire section if no `.aperant/tasks/INDEX.md` exists yet (nothing to enrich) or if team mode is disabled.

```bash
node packages/framework/bin/apt-tools.mjs effects record . \
  --op roadmap.render \
  --agent apt-team-docs-drain \
  --mode roadmap-render \
  --affects ".aperant/roadmap/{scope}/ROADMAP.md" \
  --context '{"scope":"{scope}","trigger":"{phase-added|milestone-added|render}"}'

node packages/framework/bin/apt-tools.mjs effects drain .
```

## 7. Commit Reminder

Roadmap files are committed to git. After significant changes, remind the user:
```
Roadmap updated. Don't forget to commit:
  git add .aperant/roadmap/
  git commit -m "roadmap: {summary of change}"
```

</process>
