Triggers: /track-init, /plan-epic <description>, /plan-migrate, /track <description>, /backlog, "qué tenemos pendiente?", "what's pending?", "acordate que hay que...", "remember to...", "vamos a planear", "armemos la épica", bug found during implementation, SDD tasks generated by sdd-tasks.
Hierarchy model — Epic / Story / Task / Bug
This skill enforces a three-level hierarchy (plus Bug as an orthogonal type) modeled with GitHub Issue Types (native) and sub-issues (native). Labels are kept for backwards compatibility but Issue Types are authoritative.
| Level | GitHub Issue Type | Lifetime | Sub-issues of | Has sub-issues |
|---|---|---|---|---|
| Epic | Epic |
Weeks | none | Stories |
| Story | Feature |
Days | Epic | Tasks |
| Task | Task |
Hours | Story (or Epic for hotfixes) | none |
| Bug | Bug |
Hours–Days | Epic (Stability) or Story it breaks | none |
Rules of thumb:
- One Task = one Claude session. If it does not fit, it is a Story disguised as a Task. Split it.
- A Story without sub-issues is allowed only when its acceptance criteria already serve as the task list (truly small).
- An Epic without sub-issues is a smell — likely it is actually a Story.
- A Bug can be standalone (no parent) when it does not belong to a planned epic.
Issue Types vs labels
The skill writes BOTH the Issue Type (when the org has Issue Types enabled)
AND the equivalent legacy label (type/epic, type/feature, type/task,
type/bug). This lets old views, filters, and the existing /track flow
keep working while the new hierarchy is adopted incrementally.
Detection of Issue Type availability is done once per session via the
Issue-Type detection block in COMMANDS.md. If the response is empty or the field
is unsupported, fall back to label-only mode and warn the user once per session:
"Issue Types are not enabled on this org. Falling back to labels —
type/epic,type/feature,type/task,type/bug. To enable native types, go to Org settings → Issue Types."
All literal
gh/ GraphQL command blocks live inCOMMANDS.md, referenced below by step ID (e.g.[track-init §1]). This file holds the decision logic;COMMANDS.mdholds the exact invocations.
Process
Pre-flight: Detect Repo and Config
Before any flow, resolve owner/repo (run Pre-flight — detect repo in COMMANDS.md).
Then search engram for the project config:
mem_searchwith querytracking/{repo-name}/config- If found, call
mem_get_observationto get project number and field IDs - If NOT found and the flow requires project operations → warn user to run
/track-initfirst
Flow 1: Bootstrap — /track-init
Run once per repo. Idempotent — safe to run again.
Step 0 — Detect token capabilities
Run [track-init §0]. On failure, classify the cause using the table there —
never assume every failure is the token. Distinct causes route to distinct actions:
gh-not-authenticated (§0-auth), fine-grained PAT (§0-manual), missing project
scope (gh auth refresh -s project), a transient GitHub/network error (stop and
retry later, do NOT touch the token), or — when REST works but Projects/GraphQL
returns FORBIDDEN — a misconfigured token env var (§0-token-env: wrong token
type, or the right token under a name gh never reads).
Most common (your case): fine-grained PATs do NOT support Projects V2 on personal
accounts (only orgs). [track-init §0-manual] offers two exits: fix the token
(Classic PAT + gh auth, recommended — unblocks automatic creation for every repo)
or create the board manually (fast fallback). Recommend the token fix. If the user
fixes the token, re-run Step 0 then go to Step 1. If they create it manually, capture
the project number and skip to Step 2 (labels).
Step 1 — Create GitHub Project board (automatic path)
Only if Step 0 confirms Projects V2 support: run [track-init §1] (creates the
project + the Status / Priority / Type single-select fields).
Step 2 — Create labels
Run [track-init §2] (type / priority / status labels). Idempotent — a label that
already exists makes gh label create fail; catch and continue.
Step 3 — Capture field and option IDs
Whether the board was created automatically (Step 1) or manually (Step 0), run
[track-init §3] to capture status_field_id, priority_field_id, type_field_id
and the option IDs for each value. Required by gh project item-edit.
Step 4 — Save config to engram
Call mem_save with:
- title: "Bootstrap project-tracking config for {repo-name}"
- type: config
- topic_key:
tracking/{repo-name}/config - content: project_number, owner, repo, all field IDs and option IDs as a structured map
Confirm to user: "Board linked. Project #{project_number} for {owner}/{repo}. Config saved to engram."
Flow 2: Plan Epic — /plan-epic <description>
Used to plan a large feature or initiative end-to-end: one Epic, its Stories, and the Tasks of each Story. Nothing is written to GitHub until the user confirms explicitly.
Run /track-init first if no engram config exists for this repo.
Step 1 — Read templates
Load the body templates from this skill directory:
templates/epic.mdtemplates/story.mdtemplates/task.md
These are the contract — every issue created in this flow follows them.
Step 2 — Detect Issue Type support
Run Issue-Type detection (COMMANDS.md). Cache the result for the session.
Decide: native Issue Types mode OR label-fallback mode.
Step 3 — Propose the hierarchy as Markdown in chat
Produce a single chat message structured as:
# Epic: {Title}
Type: Epic | Priority: {P0..P3}
[Epic body following templates/epic.md]
---
## Story 1: {Title}
Type: Feature | Priority: {P0..P3} | Parent: Epic above
[Story body following templates/story.md]
### Task 1.1: {Title}
Type: Task | Parent: Story 1
[Task body following templates/task.md]
### Task 1.2: {Title}
...
---
## Story 2: {Title}
...
Show the full proposal in ONE message. Do not call any GitHub API yet.
Step 4 — Wait for confirmation
End the proposal message with EXACTLY this prompt:
Review the plan above. Reply:
- "dale" / "go" / "create" → I create all N issues + sub-issue links
- "edit <X>" / "no <X>" → tell me what to change, I revise the plan
- "abort" → drop the plan, nothing is created
If the user replies anything other than a confirmation keyword, treat it as edit instructions and produce a revised proposal. Re-prompt. Never create issues without an explicit confirmation keyword from this list.
Step 5 — Execute creation in order
Create issues in the order: Epic → Story 1 → Task 1.1 → Task 1.2 → Story 2 → ...
For each issue:
- Call
mcp__github__create_issuewith title, body, labels, and Issue Type (when supported). - Capture the returned issue number.
- If the issue has a parent in the proposal, link as sub-issue with
[sub-issue link]. - Add the issue to the Project board (
[track §3]). - Set Project fields (
[track §4]): Status=Backlog, Priority=as proposed, Type=as proposed.
Step 6 — Report back
Print a single summary block:
## Created
Epic #N: {title}
├── Story #N+1: {title}
│ ├── Task #N+2: {title}
│ ├── Task #N+3: {title}
│ └── Task #N+4: {title}
└── Story #N+5: {title}
├── Task #N+6: {title}
└── Task #N+7: {title}
Total: 1 epic, 2 stories, 5 tasks. All added to project board as Backlog.
Save an engram observation:
mem_savewith type: architecture, topic_key:tracking/{repo}/epic-{N}, content: epic title, child story+task numbers, source of the plan (user request, SDD spec ref, etc.).
Flow 3: Plan Migrate — /plan-migrate
Used when an existing repo has a flat board (issues without parent/child relationships) and needs to be reorganized into the Epic/Story/Task hierarchy.
Step 1 — Fetch the current state
Run [migrate-fetch]. Group issues by likely level using these heuristics:
| Signal | Likely level |
|---|---|
Title starts with epic: or label type/epic or Type=Epic |
Epic |
Title starts with feat: or label type/feature or Type=Feature |
Story |
Title starts with bug: or label type/bug or Type=Bug |
Bug |
Title starts with chore:/task: or no prefix |
Task |
Step 2 — Propose the hierarchy in chat
Produce a single message:
# Migration proposal — {N} open issues
## Epics
- #10 epic: Payments & Monetization
Will own:
- #28 feat: Stripe integration for guide bookings
- {other feats that thematically belong}
- #14 epic: Push Notifications & Engagement
Will own:
- #30 feat: push notifications (Expo / Firebase)
## Bugs (orphan — no epic owner)
- #18, #19, #20, ...
Recommended: create epic "Stability & Bugfixes" to own all of these.
OR leave as standalone (no parent). Pick one.
## Unclassified
- #X, #Y — could not infer parent confidently. Please tell me.
End with the same confirmation prompt as /plan-epic Step 4.
Step 3 — Apply on confirmation
For each parent → child pair, run [sub-issue link]. For issues that need an Issue
Type set retroactively, run [set-issue-type]. If Issue Types are not supported,
ensure the legacy type/* label is present instead.
Step 4 — Report
Print the same tree summary as /plan-epic Step 6. Save engram observation
type: architecture, topic_key: tracking/{repo}/migration-{date}.
Flow 4: Track — Create Issues and Add to Board
Trigger model (hybrid)
| Situation | Behavior |
|---|---|
/track <description> |
Create issue immediately, no confirmation |
/track with multiple items or backlog session |
Create all, show list, ask confirmation before executing |
sdd-tasks generates tasks |
Propose creating one issue per task, ask confirmation |
| User mentions future work ("habría que...", "falta...", "después hay que...") | Collect internally. At session end propose: "Detected N trackable items: [list]. Create as issues?" |
| "acordate que hay que..." / "remember to..." | Propose creating an issue immediately |
| Bug found during implementation | Propose creating issue with type/bug label immediately |
Issue creation steps
Step 1 — Deduplicate
Run [track §1]. If a similar issue exists, show it and ask: "Issue #N already
exists: '{title}'. Create a new one anyway?"
Step 2 — Create issue via MCP
Run [track §2] — mcp__github__create_issue with owner, repo, conventional
title (type: description), the body template, and applicable labels. The body
template lives in [track §2].
Step 3 — Add to project board
Run [track §3] (captures item_id).
Step 4 — Set project fields
Run [track §4] — load field/option IDs from engram config
(tracking/{repo-name}/config), then set Status / Priority / Type.
Step 5 — Confirm to user
Created #14: feat: add OAuth with Google — added to board as Backlog/P1-High [Feature]
Flow 5: Query — /backlog and status questions
Default: full board summary
Run [backlog §default], then group items by Status field value. Output:
## Project Board: {project-name}
### In Progress (2)
- #14 feat: OAuth with Google [P1-High]
- #12 fix: Email validation [P0-Critical]
### Review (1)
- #13 feat: User profile page [P1-High]
### Backlog (3)
- #15 feat: Dashboard redesign [P2-Medium]
- #16 chore: Update dependencies [P3-Low]
- #17 feat: Export to PDF [P2-Medium]
### Done (last 5)
- #11 fix: Login redirect loop [P1-High]
Show statuses in order: In Progress → Review → Todo → Backlog → Done (last 5 only to avoid noise).
Filtered queries
| Command | Filter applied |
|---|---|
/backlog bugs |
Only items with Type = Bug |
/backlog high |
Only items with Priority = P0-Critical or P1-High |
/backlog in-progress |
Only items with Status = In Progress |
/backlog todo |
Only items with Status = Todo |
/backlog review |
Only items with Status = Review |
Status Transitions
Manual triggers: /track move #14 in-progress, /track move #14 review,
/track move #14 done. Underlying command: [status-move].
Automatic integration points:
- Claude starts working on issue → move to "In Progress"
- Claude opens PR for issue → move to "Review" (integrates with
branch-prskill) - PR merged / issue closed → move to "Done"
Rules
- GitHub Issues + Projects is the source of truth for WHAT needs to be done — backlog, tasks, stories, bugs
- Engram remains the source of truth for decisions, discoveries, and technical context — these are complementary, not competing
- Never create duplicate issues — always run
gh issue list --searchbefore creating - Issue titles follow conventional format:
type: description(e.g.,feat: add OAuth,fix: email validation) - Labels use lowercase with
/separator:type/feature,priority/high,status/backlog - Every issue MUST have at least the Type field set on the project board
/track-initis idempotent — running it twice must not create duplicate labels or projects; handle errors fromgh label creategracefully- Project config (IDs) MUST be saved to engram after bootstrap with
topic_key: tracking/{repo-name}/config - Before any
gh project item-editcall, load field IDs from engram; if not found re-derive withgh project field-list - Batch creation requires confirmation — when proposing multiple issues at once, show the full list and wait for user approval before creating any
- Propose, don't assume type/priority — when auto-detecting trackable items from conversation, infer reasonable defaults but show them to the user before creating
- Prefer MCP for issue creation (
mcp__github__create_issue) andghCLI for all project board operations - When
/track-inithas not been run (no engram config, no linked project), detect this and suggest running it before any board operation - Fine-grained PATs do NOT support Projects V2 on personal accounts — only Classic PATs or org-scoped tokens work. When detected, offer both exits (
§0-manual): recommend fixing the token (Classic PAT +gh auth, unblocks every repo) over the manual-board fallback. Never present manual creation as the only option ghreads its token only fromGH_TOKEN>GITHUB_TOKEN(in that order), and an env var token overridesgh auth login. A correct token under any other name (e.g.GITHUB_CLASSIC_TOKEN) is silently ignored. When REST works but Projects/GraphQL returnsFORBIDDEN, suspect a misconfigured env var (§0-token-env): put the Classic PAT inGH_TOKEN, verify with the one-line GraphQLviewer.projectsV2query. Document the rule — token type, variable name, verification — never the secret value- Both automatic and manual board creation paths converge at Step 3 (capture IDs) — the rest of the workflow is identical regardless of how the board was created
/plan-epicis confirm-first: the full hierarchy (Epic + Stories + Tasks) is proposed as Markdown in chat; NOcreate_issuecall fires until the user replies with an explicit confirmation keyword (dale,go,create). Anything else is treated as edit instructions, not approval.- Issue Types are authoritative when available; legacy
type/*labels are written alongside for backwards compatibility. Detect support via GraphQLissueTypesquery, cache the result for the session, and fall back to label-only mode with a one-time warning when types are unsupported. - Sub-issue links use the
addSubIssueGraphQL mutation — never simulate hierarchy with text in the body ("parent: #N" alone does not create the link). - One Task = one Claude session. If a Task does not fit in one session, it is a Story disguised as a Task — propose splitting it before creating.
/plan-migratenever modifies issues without confirmation — same confirm-first contract as/plan-epic. Unclassified issues are flagged for the user, not auto-assigned.- Bug issues can be standalone (no parent epic) or owned by a "Stability & Bugfixes" epic. Pick one approach per repo and keep it consistent — the choice goes into the engram config under
tracking/{repo}/config. - Body templates live in
skills/project-tracking/templates/(epic.md,story.md,task.md,bug.md) and are the ONLY source of truth for issue body structure. Do not improvise alternative formats.