Role
You publish approved feature specs into a Linear project. You do not decide what to build — that was settled by feature-spec-author in Phase 1. You translate the approved tasks.md into a tree of Linear issues and maintain the issue map that Phase 3 and 4 will use.
You are the Linear-tracker parallel of backlog-manager (which targets JIRA). Same role, same inputs, same outputs — only the tracker substrate differs. You are invoked by the ol-sdd-workflow orchestrator at Phase 2 when the project's tracker is linear, or directly when backlog changes are needed.
The Linear model (read this first — it is the whole skill)
Linear has no Epic/Story/Subtask issue types and no transition workflow. The hierarchy is carried by labels and issue nesting, and state is set directly. The convention this skill enforces:
| Concept | Linear realisation |
|---|---|
| Shared team | one Linear Team for all apps (e.g. Ontoledgy, global prefix ONT-) — owns states, labels, cycles |
| App / solution | one Linear Project under the shared team (pre-existing — never created per feature) |
| Feature (one per feature; Jira "epic") | an Issue labelled type:feature, in the app project |
| Story (one per requirement) | an Issue labelled type:story, parentId = the feature-issue |
| Task (one per atomic task) | an Issue labelled type:task, parentId = the story-issue |
| Estimate | numeric estimate (points, not hours) |
| Spec back-link | links: [{url, title}] on the issue (shows as an attachment) |
| Initial state | set state directly to Backlog — no transitions |
| Sprint | a Cycle (assigned later by the sprint planner, not here) |
| Release grouping | an optional project Milestone (milestone on the issue) |
Nesting is reliable for the 3 levels above (feature→story→task), proven live. Because some Linear views flatten deep trees, the type:* labels are the primary way to read the hierarchy and nesting is secondary structure.
Inputs
documentation/specs/{feature}/tasks.md— approved (Phase 1 gate 1c passed)documentation/specs/{feature}/requirements.md— for story groupingdocumentation/specs/{feature}/design.md— for the feature-issue description- Docs URL for the spec page (Notion or Confluence) — for back-links
- Linear team name or ID (e.g.
Ontoledgy) — required to create issues - Linear project name or ID — the app's project (e.g.
OL AI Context Library) - Default assignee (optional)
- Optional: existing release milestone (from a release planner) — if present, task-issues are tagged with it
Project & feature handling: existing app project vs release milestone
Before creating anything, settle the placement:
-
Confirm the app's Linear Project exists under the shared team. Use
list_projects(filter by name). If it does not exist, ask the user to confirm the project name and create it once withsave_projecton the shared team (it is the app's home, not a per-feature container). Do not create a project per feature, and do not create a team per app — all apps share one team. -
Feature-issue strategy:
- Release-skeleton feature-issue exists (created by a release planner): fetch it (
get_issue), update its description with the full design overview + spec URL + repo spec-folder link, ensure it carriestype:feature, and create stories/tasks beneath it. Do not create a second feature-issue. - No release plan, or feature not yet represented: create a new feature-issue labelled
type:featureunder the app project.
- Release-skeleton feature-issue exists (created by a release planner): fetch it (
-
Release milestone (optional): if the release planner created a project milestone for this release, pass
milestoneon the task-issues so the roadmap reflects them. Otherwise omit.
Outputs
| Output | Where |
|---|---|
| Feature-issue (one) | ONT-NNN (global prefix), label type:feature, in the app project |
| Story-issues (several) | ONT-NNN, type:story, parentId = feature-issue |
| Task-issues (many) | ONT-NNN, type:task, parentId = story-issue |
| Ticket map | documentation/specs/{feature}/ticket-map.md |
Issue structure
Feature-issue (one per feature)
- Title:
{Feature Name} — {one-line description} - Description: overview + link to spec page (Notion/Confluence) + link to repo spec folder
- Project: the app project · Team: the shared team (
Ontoledgy/ONT-) - Labels:
type:feature,feature:{feature-name} - State:
Backlog
Story-issue (one per requirement group)
- Title: the requirement user story ("As a {role}, I want {feature}, so that {benefit}")
- Description: acceptance criteria from requirements.md (EARS format preserved)
- parentId: the feature-issue · Project/Team: same as the feature-issue
- Labels:
type:story,feature:{feature-name},req:{requirement-number} - State:
Backlog
Task-issue (one per atomic task in tasks.md)
- Title: task title from tasks.md
- Description: task purpose · exact files to create/modify ·
_Leverage:references ·_Requirements:back-link · spec section link · impl hints from design.md - parentId: the story-issue covering the referenced requirement
- Labels:
type:task,feature:{feature-name},skill:{skill-name}(from the_Skill:annotation),agent:{engine}(codexorclaude— see Execution-engine assignment),req:{requirement-number} - Estimate: points (see Estimate convention below)
- Links:
[{url: spec-section URL, title: "Spec — {section}"}] - Milestone: release milestone if one exists
- Assignee: default assignee if configured
- State:
Backlog
Estimate convention
tasks.md carries _Estimate: in hours; Linear uses numeric points. Convert with the project's scale. Default heuristic when the team uses a Fibonacci scale: ≤2h→2, ≤4h→3, ≤1d→5, ≤2d→8, more→13. If the team's scale is configured as hours/linear, carry the hour value as the number. Record which convention was used in the issue map header so estimates stay comparable across features.
Execution-engine assignment
Each task-issue is labelled upfront with the engine that will implement it, so Phase 4's executor never decides at runtime. The engine is one of:
agent:claude— implemented by the routed Claude-native engineer skill (skill:{name}), with the fullclean-code-reviewerloop.agent:codex— implemented by delegated Codex via the Codex MCP.
Confirm the engine-routing label name against the live workspace first. This skill uses
agent:*as the engine-routing convention (the pair that shipped and got adopted workspace-wide:agent:codex/agent:claude). Before assuming either name, runlist_issue_labels(Step 3) and check which engine-routing pair already exists on the team. If the workspace already carries a different pair (e.g. a legacyexec:*), follow the live precedent rather than introducing a second, competing scheme — and flag the divergence to the user. Never create a new engine-routing pair when one already exists under a different name.
Default rule: classify each task simple/complex using task-executor's complexity-triage signals (estimate ≥ 5 pts / > 4h · more than 3 files · introduces/changes a public API, schema, or migration · domain is auth/payments/data-identity(BIE)/security · touches async/locking/transactions/ordering · requires new test files), then complex → agent:claude, simple → agent:codex. All derivable from tasks.md at backlog time. Set it as a default the user reviews — show it per task in the Step 4 preview and the issue map, and let the user override any task's engine before publishing. The agent:{engine} label is the single source of truth sprint-planner carries into the kickoff and sprint-executor reads. Like the other type:* / skill:* labels, the two agent:* labels must exist on the team before issues reference them (Step 3).
Workflow
Step 1 — Validate inputs
- Confirm tasks.md exists and is marked approved; confirm the three spec files exist.
- Confirm the shared Linear team and the app project are known (from
documentation/workflow-config.mdor user input). Resolve the project withlist_projects. - Confirm the feature does not already have a feature-issue (search
list_issueswithlabel:type:feature+feature:{name}in the project). If one exists, ask: update existing or abort?
Step 2 — Parse tasks.md
Extract top-level groupings (→ candidate stories), individual tasks (- [ ] N.) with their _Requirements: / _Leverage: / _Skill: / _Estimate: fields, and group tasks by requirement to form stories.
Step 3 — Ensure labels exist
The hierarchy and routing live in labels, so they must exist before issues reference them.
list_issue_labelsfor the team first, and read the result before creating anything. Create any missing ones withcreate_issue_label(scope to the team viateamId):- the three hierarchy labels
type:feature/type:story/type:task(one-time, on the shared team) - the engine-routing pair, but only after resolving which pair the workspace uses (see the guard below) — create
agent:codex/agent:claudeonly if no engine-routing pair already exists; if a pair is already present under any name, reuse it and create nothing feature:{feature-name}(once per feature)req:{n}andskill:{name}as referenced by the tasks
- the three hierarchy labels
- Resolve the engine-routing pair before creating any engine label. From the
list_issue_labelsresult, check whether the team already carries an engine-routing pair (theagent:*default, or a legacy pair under a different name such asexec:codex/exec:claude). If one exists, use that existing pair on all task-issues and tell the user the workspace diverges from theagent:*default when it does. Only createagent:codex/agent:claudewhen no engine-routing pair exists yet. Never create a second, competing pair. - Do not assume
save_issueauto-creates labels — create them explicitly.
Step 4 — Preview before publishing
Produce the tree and the totals; ask the user to approve before any issue is created:
Project: {app project} (Team {team})
Feature-issue: {feature} — {description} [type:feature]
├── Story: As a {role}... (sum: 13 pts) [type:story, req:1]
│ ├── Task: Add columns to model [type:task, skill:python-data-engineer, agent:codex, 5pt]
│ ├── Task: Write Alembic migration [type:task, skill:python-data-engineer, agent:claude, 5pt]
│ └── Task: Add unit tests [type:task, skill:clean-code-tests, agent:codex, 3pt]
└── Story: As a {role}... (sum: 8 pts) [type:story, req:2]
└── ...
Total: {N} issues, {P} points · engines: {n} claude / {m} codex
Ask the user to approve before creating issues — and to override any task's agent: engine if they disagree with the default classification.
Step 5 — Create issues (capture IDs for nesting)
Use the Linear MCP in this order — each call returns an identifier (e.g. ONT-42) you pass as the next level's parentId:
- Feature-issue —
save_issue(orsave_issuewithidto flesh out a release skeleton). Capture its identifier. - Story-issues — one
save_issueeach withparentId= the feature-issue identifier. Capture each. - Task-issues — one
save_issueeach withparentId= the parent story identifier,estimate,labels,links, andmilestoneif applicable.
Create in small batches and surface any API error immediately — do not continue on failure. The proven call shape (from the ONT-27 dogfood):
save_issue({
title: "Add licence columns to LegalEntities model",
team: "Ontoledgy",
project: "OL AI Context Library",
parentId: "ONT-41", // the story-issue
labels: ["type:task", "feature:licence", "skill:python-data-engineer", "agent:codex", "req:1"],
estimate: 5,
state: "Backlog",
links: [{ url: "<spec section URL>", title: "Spec — Data model" }],
description: "<purpose · files · _Leverage · _Requirements · hints>"
})
If operating on a release-skeleton feature-issue, also update the release roadmap (milestone / release page) to reflect that this feature moved from "specced" to "in backlog".
Step 6 — Write issue map
Create documentation/specs/{feature}/ticket-map.md — the canonical mapping Phase 3/4 use. Commit it.
# Issue Map — {feature-name} (estimate convention: points, Fibonacci)
Feature-issue: [ONT-40](https://linear.app/ontoledgy/issue/ONT-40)
| Task (from tasks.md) | Linear ID | Type | Skill | Engine | Estimate | Status |
|----------------------|-----------|------|-------|--------|----------|--------|
| 1. Add columns to model | [ONT-42](...) | task | python-data-engineer | codex | 5 | Backlog |
| 2. Write Alembic migration | [ONT-43](...) | task | python-data-engineer | claude | 5 | Backlog |
...
Step 7 — Update the docs surface & return
Append a "Linear Issues" section to the spec page (Notion or Confluence, via the docs adapter) with a table matching the issue map, so reviewers can trace spec → issues. Then return to caller with: feature-issue identifier + URL, count of stories and tasks, total points, ticket-map path, and the project board URL.
Mapping from backlog-manager (JIRA → Linear)
| backlog-manager (JIRA) | linear-backlog-manager |
|---|---|
createJiraIssue epic |
save_issue labelled type:feature (Jira epics are features; no separate epic level) |
createJiraIssue story parent:{epic} |
save_issue parentId:{feature} labelled type:story |
createJiraIssue subtask parent:{story} |
save_issue parentId:{story} labelled type:task |
addCommentToJiraIssue for back-link |
links:[{url,title}] on the issue (or save_comment) |
| estimate in hours | estimate in points (convert; record convention) |
| project key (one per app) | one shared team (global ONT- prefix) + one project per app |
| release-skeleton epic | release-skeleton feature-issue, or project milestone |
editJiraIssue to amend |
save_issue with id to amend |
Updates and amendments
When a spec changes after issues are published:
- New task added:
save_issuea new task-issue under the right story; append to the issue map. - Task removed: set state to
Canceledwith a reason comment; strike it through in the issue map. Never delete issues — the audit trail matters. - Task scope changed:
save_issuewithidto edit description; update the issue map. - Estimate changed:
save_issuewithidto changeestimate.
What this skill does NOT do
- Does not author specs (Phase 1 /
feature-spec-author) - Does not plan sprints or assign cycles (Phase 3 / sprint planner)
- Does not execute tasks (Phase 4 / executor)
- Does not log implementation details (Phase 5 /
impl-logger) - Does not create the app's Linear Project per feature — the project is the app's pre-existing home
References
skills/backlog-manager/SKILL.md— the JIRA original; identical role and contract- Linear MCP:
save_issue,save_project,list_projects,list_issues,get_issue,list_issue_labels,create_issue_label,list_issue_statuses,save_milestone,save_comment - Proven call sequence: feature-issue
ONT-27→ storiesONT-28/29→ tasksONT-30..34in project "OL AI Context Library" (the trial that produced this skill; originally created astype:epic, since relabelled totype:featureper the decided standard) - Companion:
impl-logger(Phase 5,tracker: linear), the tracker-adapter generalisation of the executors
Feedback
If the user corrects this skill's output due to a misinterpretation or missing rule in the skill itself (not a one-off preference), invoke skill-feedback to capture structured feedback and optionally post a GitHub issue.
If skill-feedback is not installed, ask the user: "This looks like a skill defect. Would you like to install the skill-feedback skill to report it?" If the user declines, continue without feedback capture.