Skip to content
Back to skills

Plans

ASecurity

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking. > Also consult **superpowers:writing-skills** for the repo's skill-authoring conventions (frontmatter, description triggers, references) while doing the doc-heavy tasks. **Goal:** Add a new dev-flow skill `linear-scrum` that pushes a project into Linear (via the connected L...

  • 6 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 19, 2026
developmentpythongobashgitapi

Works with

  • claude code
  • cli
  • api
  • mcp

Security analysis

A92/100
  • mediumInstalls packages at runtime which could introduce malicious dependencies

Pro scans all 13 files and shows the line behind each finding

Scanned October 19, 2026

npx -y skills add lukedj78/dev-flow --skill plans --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Plans?

Add the live security badge to your README. It updates with every re-scan.

Security grade badge for Plans
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/lukedj78-plans/badge)](https://www.skillsdirectory.com/skills/lukedj78-plans)

More formats (shields.io, HTML) on the badges page. Keep it an A: scan every change in CI with Pro.

Download with Pro
SKILL.md
# `linear-scrum` Skill Implementation Plan

> **For agentic workers:** REQUIRED SUB-SKILL: Use superpowers:subagent-driven-development (recommended) or superpowers:executing-plans to implement this plan task-by-task. Steps use checkbox (`- [ ]`) syntax for tracking.
> Also consult **superpowers:writing-skills** for the repo's skill-authoring conventions (frontmatter, description triggers, references) while doing the doc-heavy tasks.

**Goal:** Add a new dev-flow skill `linear-scrum` that pushes a project into Linear (via the connected Linear MCP) and runs every project with agile scrum (cycles, story-point estimates, sprint planning, velocity reports), with Linear as the source of truth after setup.

**Architecture:** A standalone skill folder `linear-scrum/` (family `core`, stack-agnostic) with a `SKILL.md`, three references (`scrum-model.md`, `linear-mcp.md`, a vendored `contracts.md`), and two small Python scripts (`task_key.py` for stable idempotent issue keys, `meta_linear.py` to upsert the `linear`/`scrum` blocks of `.workflow/meta.json`). Minimal edits to `dev-flow` add routing + a non-blocking policy and document the new `meta.json` blocks. The repo build scripts regenerate `skills.json` and `dist/linear-scrum.skill`.

**Tech Stack:** Markdown skills (dev-flow contract), Python 3 (stdlib only) for scripts + `pytest`-free unittest, the connected Linear MCP, the repo's `scripts/*.py` build/lint/registry tooling.

**Decisions locked (open questions from the spec, resolved here):**
- Reports: **chat-only** (no `.workflow/reports/` file — avoids a drift-prone artifact).
- Estimates without an `Estimated:` hint: **batch-prompt once** (list all unestimated tasks, ask for points in one message); default to unestimated if skipped.
- Skill name: **`linear-scrum`**.

---

## File Structure

**Create:**
- `linear-scrum/SKILL.md` — the skill: frontmatter + 3 modes (Setup / Adopt / Sync) + meta.json schema + `[VERIFY]` markers + DoD.
- `linear-scrum/references/scrum-model.md` — scrum conventions: cadence, Fibonacci estimates, state mapping, sprint-planning algorithm, velocity, `blocked` label.
- `linear-scrum/references/linear-mcp.md` — cookbook mapping each operation to Linear MCP tools; the headless API-key handoff to `docs/loop-engineering.md`.
- `linear-scrum/references/contracts.md` — vendored copy of `dev-flow/references/contracts.md` (house style: standalone skills carry a copy).
- `linear-scrum/scripts/task_key.py` — pure function: a `tasks.md` line → stable 12-char key for `issue_map`.
- `linear-scrum/scripts/meta_linear.py` — upsert `meta.json#linear` + `#scrum`, merge `issue_map` idempotently.
- `linear-scrum/scripts/test_scripts.py` — unittest for both scripts.

**Modify:**
- `dev-flow/SKILL.md` — routing at `prd_drafted`/`tasks_split`, horizontal-family mention, one policy paragraph.
- `dev-flow/references/contracts.md` — document the optional `linear`/`scrum` blocks.
- `install.sh` + `uninstall.sh` — add `linear-scrum`.
- `README.md` — add `linear-scrum` to the catalog (one row/paragraph).
- `skills.json` — regenerated by the build script (do not hand-edit).
- `dist/linear-scrum.skill` — regenerated by the build script.

---

### Task 1: Scaffold the skill folder + lint-valid frontmatter

**Files:**
- Create: `linear-scrum/SKILL.md`

- [ ] **Step 1: Create `linear-scrum/SKILL.md` with frontmatter + title only**

The `description` MUST contain the literal markers `Triggers` and `Not for:` (the repo linter enforces both). Write exactly:

```markdown
---
name: linear-scrum
description: 'Take a dev-flow project into Linear and run it with agile scrum. Creates/links a Linear Project under the workspace team, pushes `.workflow/tasks.md` into issues (story-point estimates, `area:web`/`area:agent` labels, milestones), sets up cycles/sprints (2-week cadence), and keeps Linear as the source of truth: sync pushes only new tasks, pulls status/velocity, plans the active sprint up to the velocity target, and reports. Triggers: "porta il progetto in Linear", "crea le issue in Linear", "setup scrum", "pianifica lo sprint", "report di velocity", "adotta questo progetto in Linear", or dev-flow routing from `prd_drafted`/`tasks_split`. Modes: Setup (new project), Adopt (existing Linear project), Sync (ongoing). Not for: writing or estimating the tasks themselves (use `prd-to-tasks`), building the app or agent (use design-md-to-app / eve-agent), or the headless server loop wiring (see docs/loop-engineering.md).'
---

# linear-scrum — Linear integration + agile scrum for a dev-flow project

(body written in Task 4)
```

- [ ] **Step 2: Run the linter and confirm the new skill passes frontmatter checks**

Run: `cd ~/my-skills && python3 scripts/lint_skills.py linear-scrum`
Expected: exit 0 (or 1 warnings-only), and NO error mentioning `linear-scrum` for `name`/`description`/`Triggers`/`Not for:`. If `pyyaml` is missing, run `pip install pyyaml` first.

- [ ] **Step 3: Commit**

```bash
git add linear-scrum/SKILL.md
git commit -m "feat(linear-scrum): scaffold skill folder + frontmatter"
```

---

### Task 2: `task_key.py` — stable idempotent issue keys (TDD)

**Files:**
- Create: `linear-scrum/scripts/task_key.py`
- Test: `linear-scrum/scripts/test_scripts.py`

- [ ] **Step 1: Write the failing test**

Create `linear-scrum/scripts/test_scripts.py`:

```python
import unittest
from task_key import task_key


class TestTaskKey(unittest.TestCase):
    def test_stable_across_checkbox_state_and_whitespace(self):
        a = task_key("- [ ] **Wire login flow** — connect the form to better-auth")
        b = task_key("- [x]   **Wire login flow**  — connect the form to better-auth")
        self.assertEqual(a, b)

    def test_title_only_ignores_body(self):
        a = task_key("- [ ] **Wire login flow** — body one")
        b = task_key("- [ ] **Wire login flow** — a totally different body")
        self.assertEqual(a, b)

    def test_different_titles_differ(self):
        a = task_key("- [ ] **Wire login flow**")
        b = task_key("- [ ] **Wire logout flow**")
        self.assertNotEqual(a, b)

    def test_is_12_hex_chars(self):
        k = task_key("- [ ] **Anything**")
        self.assertRegex(k, r"^[0-9a-f]{12}$")


if __name__ == "__main__":
    unittest.main()
```

- [ ] **Step 2: Run the test to verify it fails**

Run: `cd ~/my-skills/linear-scrum/scripts && python3 -m unittest test_scripts -v`
Expected: FAIL with `ModuleNotFoundError: No module named 'task_key'`.

- [ ] **Step 3: Write the minimal implementation**

Create `linear-scrum/scripts/task_key.py`:

```python
#!/usr/bin/env python3
"""task_key.py — derive a stable identity key for a tasks.md checkbox line.

The key goes into meta.json#linear.issue_map so re-running Setup/Sync never
creates a duplicate Linear issue for the same task. It is derived from the task
TITLE only (the text before the ` — ` body separator), normalized so that
checkbox state, bold markers, case, and whitespace do not change it.
"""
from __future__ import annotations

import hashlib
import re
import sys


def task_key(line: str) -> str:
    s = line.strip()
    s = re.sub(r"^-\s*\[[ xX]\]\s*", "", s)   # drop "- [ ] " / "- [x] "
    s = s.split(" — ")[0]                       # title before the em-dash body
    s = s.replace("**", "").strip()             # drop bold markers
    s = re.sub(r"\s+", " ", s).lower()          # collapse whitespace, case-fold
    return hashlib.sha1(s.encode("utf-8")).hexdigest()[:12]


if __name__ == "__main__":
    for arg in sys.argv[1:]:
        print(task_key(arg))
```

- [ ] **Step 4: Run the test to verify it passes**

Run: `cd ~/my-skills/linear-scrum/scripts && python3 -m unittest test_scripts -v`
Expected: 4 tests, all PASS.

- [ ] **Step 5: Commit**

```bash
git add linear-scrum/scripts/task_key.py linear-scrum/scripts/test_scripts.py
git commit -m "feat(linear-scrum): task_key.py for stable idempotent issue keys"
```

---

### Task 3: `meta_linear.py` — upsert the linear/scrum blocks (TDD)

**Files:**
- Create: `linear-scrum/scripts/meta_linear.py`
- Modify: `linear-scrum/scripts/test_scripts.py`

- [ ] **Step 1: Add failing tests to `test_scripts.py`**

Append to `linear-scrum/scripts/test_scripts.py` (above the `if __name__` block):

```python
from meta_linear import ensure_scrum, upsert_linear, record_issues


class TestMetaLinear(unittest.TestCase):
    def base(self):
        return {"project_slug": "demo", "phase": "tasks_split", "history": []}

    def test_upsert_linear_sets_block(self):
        m = upsert_linear(self.base(), team_id="T", team_name="Lucadigerlando",
                          project_id="P", url="http://x")
        self.assertEqual(m["linear"]["team_id"], "T")
        self.assertEqual(m["linear"]["project_id"], "P")
        self.assertEqual(m["linear"]["issue_map"], {})

    def test_record_issues_merges_no_dup(self):
        m = upsert_linear(self.base(), team_id="T", team_name="N",
                          project_id="P", url="u")
        m = record_issues(m, {"abc": "LUC-1"})
        m = record_issues(m, {"abc": "LUC-1", "def": "LUC-2"})  # re-run + new
        self.assertEqual(m["linear"]["issue_map"], {"abc": "LUC-1", "def": "LUC-2"})

    def test_ensure_scrum_defaults(self):
        m = ensure_scrum(self.base())
        self.assertEqual(m["scrum"]["cadence_weeks"], 2)
        self.assertEqual(m["scrum"]["estimate_scale"], "fibonacci")
        self.assertEqual(m["scrum"]["states"]["blocked_label"], "blocked")

    def test_ensure_scrum_is_idempotent(self):
        m = ensure_scrum(self.base())
        m["scrum"]["velocity_target"] = 13     # user-set value must survive
        m2 = ensure_scrum(m)
        self.assertEqual(m2["scrum"]["velocity_target"], 13)
```

- [ ] **Step 2: Run to verify failure**

Run: `cd ~/my-skills/linear-scrum/scripts && python3 -m unittest test_scripts -v`
Expected: FAIL with `ModuleNotFoundError: No module named 'meta_linear'`.

- [ ] **Step 3: Write the minimal implementation**

Create `linear-scrum/scripts/meta_linear.py`:

```python
#!/usr/bin/env python3
"""meta_linear.py — upsert the `linear` and `scrum` blocks of .workflow/meta.json.

Pure dict transforms (unit-testable) plus a thin CLI. Linear is the source of
truth after setup; these helpers only track the mapping + config on our side.
issue_map is keyed by task_key(), so merges are naturally duplicate-free.
"""
from __future__ import annotations

import argparse
import json
from datetime import datetime, timezone
from pathlib import Path

DEFAULT_STATES = {
    "backlog": "Backlog", "todo": "Todo", "in_progress": "In Progress",
    "in_review": "In Review", "done": "Done", "blocked_label": "blocked",
}


def _now() -> str:
    return datetime.now(timezone.utc).strftime("%Y-%m-%dT%H:%M:%SZ")


def upsert_linear(meta: dict, *, team_id: str, team_name: str,
                  project_id: str, url: str) -> dict:
    block = meta.get("linear", {})
    block.update({
        "team_id": team_id, "team_name": team_name,
        "project_id": project_id, "url": url,
        "issue_map": block.get("issue_map", {}),
        "last_synced_at": _now(),
    })
    meta["linear"] = block
    return meta


def record_issues(meta: dict, mapping: dict) -> dict:
    block = meta.setdefault("linear", {})
    im = block.setdefault("issue_map", {})
    im.update(mapping)                 # dict keyed by task_key → no duplicates
    block["last_synced_at"] = _now()
    return meta


def ensure_scrum(meta: dict, *, cadence_weeks: int = 2,
                 estimate_scale: str = "fibonacci") -> dict:
    s = meta.get("scrum", {})
    s.setdefault("cadence_weeks", cadence_weeks)
    s.setdefault("estimate_scale", estimate_scale)
    s.setdefault("velocity_target", None)
    s.setdefault("states", dict(DEFAULT_STATES))
    meta["scrum"] = s
    return meta


def _load(p: Path) -> dict:
    return json.loads(p.read_text())


def _save(p: Path, meta: dict) -> None:
    p.write_text(json.dumps(meta, indent=2) + "\n")


def main() -> None:
    ap = argparse.ArgumentParser()
    ap.add_argument("meta_path")
    sub = ap.add_subparsers(dest="op", required=True)

    ln = sub.add_parser("upsert-linear")
    for f in ("team-id", "team-name", "project-id", "url"):
        ln.add_argument(f"--{f}", required=True)

    ri = sub.add_parser("record-issues")
    ri.add_argument("--mapping", required=True, help="JSON object key->identifier")

    sub.add_parser("ensure-scrum")

    args = ap.parse_args()
    p = Path(args.meta_path)
    meta = _load(p)
    if args.op == "upsert-linear":
        meta = upsert_linear(meta, team_id=args.team_id, team_name=args.team_name,
                             project_id=args.project_id, url=args.url)
        meta = ensure_scrum(meta)
    elif args.op == "record-issues":
        meta = record_issues(meta, json.loads(args.mapping))
    elif args.op == "ensure-scrum":
        meta = ensure_scrum(meta)
    _save(p, meta)


if __name__ == "__main__":
    main()
```

- [ ] **Step 4: Run to verify pass**

Run: `cd ~/my-skills/linear-scrum/scripts && python3 -m unittest test_scripts -v`
Expected: 8 tests total (4 from Task 2 + 4 here), all PASS.

- [ ] **Step 5: Commit**

```bash
git add linear-scrum/scripts/meta_linear.py linear-scrum/scripts/test_scripts.py
git commit -m "feat(linear-scrum): meta_linear.py upsert of linear/scrum blocks"
```

---

### Task 4: Write the `SKILL.md` body (three modes)

**Files:**
- Modify: `linear-scrum/SKILL.md` (replace the `(body written in Task 4)` placeholder)

- [ ] **Step 1: Replace the placeholder line with the full body**

Replace the single line `(body written in Task 4)` with exactly this content:

````markdown
This skill owns the **Linear side** of a dev-flow project. It writes to Linear (via the connected Linear MCP) and to the `linear` / `scrum` blocks of `.workflow/meta.json`. It **never** touches `app/`, `apps/agent/`, or other framework files, and it **does not change the `phase` enum** — like `eve-agent` and the discipline skills it records state in `meta.json` and appends `history`, and is never a phase gate.

Read `references/contracts.md` for the `.workflow/` contract, `references/scrum-model.md` for the scrum conventions, and `references/linear-mcp.md` for the exact Linear MCP operations.

## Preconditions

- A **Linear MCP must be connected** in the session. Confirm with `list_teams` before any write. For the headless server loop use the API-key path in `docs/loop-engineering.md` instead — this skill is the interactive counterpart.
- `.workflow/meta.json` exists (dev-flow project). If not, tell the user and stop.
- `.workflow/tasks.md` exists for Setup mode (run `prd-to-tasks` first otherwise).

## Read state, then pick a mode

Read `.workflow/meta.json`:
- No `meta.json#linear` and `tasks.md` present → **Setup mode**.
- A Linear Project already exists for this project but `meta.json#linear` is absent/partial → **Adopt mode**.
- `meta.json#linear` present → **Sync mode**.

Do **one** logical operation per invocation, then stop. Every mode is idempotent.

## Setup mode (new project)

1. `list_teams` → resolve the team (single team today: `Lucadigerlando`). One dev-flow project = one **Linear Project** under that team, never a new team.
2. Ensure scrum config on the team: **cycles enabled** and a **Fibonacci estimate scale**. If the MCP cannot toggle these, stop and ask the user to enable "Cycles" + estimates once in Linear, then re-run. `[VERIFY]` against the installed MCP whether these are settable.
3. Create/link the Linear Project (`save_project`); capture its id + url.
4. Push `tasks.md` → issues (`save_issue`), one per `- [ ]` task:
   - **estimate**: from the task's `Estimated:` hint mapped to the nearest Fibonacci point; for tasks with no hint, **batch-prompt once** (list them, ask for points in one message) and default to unestimated if skipped.
   - **labels**: `area:web` / `area:agent` (from `meta.json#stack` + the task's `*(addressed by …)*` tag), plus `type:scaffold` for setup tasks.
   - **milestone**: map epics/milestones via `save_milestone` when `tasks.md` groups by epic.
   - record each `task_key(line) → issue identifier` for the next step.
5. Create the first cycle (`list_cycles` to check, then create; 2-week cadence).
6. Persist state:
   ```bash
   python3 scripts/meta_linear.py <root>/.workflow/meta.json upsert-linear \
     --team-id <id> --team-name <name> --project-id <pid> --url <url>
   python3 scripts/meta_linear.py <root>/.workflow/meta.json record-issues \
     --mapping '{"<task_key>":"LUC-123", ...}'
   ```
   Then append a `history` entry via `dev-flow/scripts/update_meta.py`:
   `{ "skill": "linear-scrum", "action": "setup", "ran_at": "<ISO8601>" }` (no phase bump).

## Adopt mode (existing project already partly in Linear)

1. Discover the existing Project and its issues (`list_projects`, `list_issues`).
2. Match issues to `tasks.md` lines by title (`task_key`) and **backfill** `issue_map` without creating anything (`meta_linear.py record-issues`).
3. Fill any missing scrum config (`meta_linear.py ensure-scrum`), then `upsert-linear` with the discovered project id/url.
4. Append history `{ "skill": "linear-scrum", "action": "adopt", … }`.

## Sync mode (ongoing, horizontal)

- **Push new tasks**: for each `tasks.md` line whose `task_key` is not in `issue_map`, `save_issue` + `record-issues`. Never edit or re-push existing issues — Linear owns status/ordering/estimates.
- **Pull status/velocity**: read issue states + completed points from Linear (`list_issues`, `list_cycles`).
- **Sprint planning**: move items from `Backlog`/`Todo` into the active cycle up to the **velocity target** (`meta.json#scrum.velocity_target`; learned from past cycles, asked on the first). See `references/scrum-model.md`.
- **Report**: produce a sprint/velocity summary **to chat** (points committed vs done, carry-over, blocked issues). Do not write a report file.
- Append history `{ "skill": "linear-scrum", "action": "sync", … }`.

## Definition of Done (per run)

- **Setup**: `list_issues` on the Project returns the pushed issues; `meta.json#linear` populated; a re-run creates **no duplicates** (verify by re-running Setup and confirming the issue count is unchanged).
- **Sync**: the active cycle holds planned issues up to the velocity target and a report was produced.
- Scripts stay green: `cd linear-scrum/scripts && python3 -m unittest test_scripts`.

## What this skill does NOT do

- Doesn't write/estimate tasks (that's `prd-to-tasks`).
- Doesn't build the app or agent (`design-md-to-app` / `eve-agent`).
- Doesn't run human ceremonies (standup/retro) or generate ceremony artifacts.
- Doesn't create a Linear team per project — one team, many Projects.
- Doesn't bump `phase`.
````

- [ ] **Step 2: Lint the completed skill**

Run: `cd ~/my-skills && python3 scripts/lint_skills.py linear-scrum`
Expected: exit 0/1, no errors for `linear-scrum`. Cross-references to sibling skills (`prd-to-tasks`, `eve-agent`, `design-md-to-app`) resolve — those folders exist.

- [ ] **Step 3: Commit**

```bash
git add linear-scrum/SKILL.md
git commit -m "feat(linear-scrum): SKILL.md body — Setup/Adopt/Sync modes + DoD"
```

---

### Task 5: `references/scrum-model.md`

**Files:**
- Create: `linear-scrum/references/scrum-model.md`

- [ ] **Step 1: Write the reference**

Create `linear-scrum/references/scrum-model.md`:

```markdown
# Scrum model for dev-flow projects

The conventions `linear-scrum` applies. Depth = **structure + planning + reports**; human ceremonies (standup, retro) stay human.

## Cadence
- Default **2-week** cycles (`meta.json#scrum.cadence_weeks`). One Linear cycle = one sprint.

## Estimates
- **Fibonacci** story points (1, 2, 3, 5, 8, 13). Map a task's `Estimated: <2-8h>` hint to the nearest point (≤2h→1, ~half-day→2, day→3, 2-3 days→5, ~week→8, more→13, and split if >13).
- Tasks with no hint: batch-prompt the user once; default to unestimated rather than guessing.

## Workflow states (this workspace)
`Backlog · Todo · In Progress · In Review · Done · Canceled`. There is **no `Blocked` state** — use a **`blocked` label** on the issue instead (`meta.json#scrum.states.blocked_label`).

## Sprint planning
1. Determine the velocity target: `meta.json#scrum.velocity_target` if set, else the average completed points of the last 2-3 cycles, else ask the user for the first cycle.
2. Pull issues from `Todo`/`Backlog` in priority order until the sum of estimates reaches the target (don't exceed by more than the smallest remaining item).
3. Assign them to the active cycle; leave the rest in the backlog.

## Velocity report (to chat)
For the active/last cycle report: committed points, completed points, carry-over, blocked issues (by label), and the rolling velocity (last 3 cycles). No file is written.

## Labels
- `area:web` / `area:agent` — deduced from `meta.json#stack` and each task's `*(addressed by …)*` owner.
- `type:scaffold` — setup/foundation tasks owned by another skill.
- `blocked` — the stand-in for a Blocked state.
```

- [ ] **Step 2: Lint + commit**

Run: `cd ~/my-skills && python3 scripts/lint_skills.py linear-scrum`
Expected: no new errors.

```bash
git add linear-scrum/references/scrum-model.md
git commit -m "docs(linear-scrum): scrum-model reference"
```

---

### Task 6: `references/linear-mcp.md`

**Files:**
- Create: `linear-scrum/references/linear-mcp.md`

- [ ] **Step 1: Write the reference**

Create `linear-scrum/references/linear-mcp.md`:

```markdown
# Linear MCP cookbook

How each `linear-scrum` operation maps to Linear MCP tools. **Verify names/params against the connected MCP** before relying on them — tool names are workspace-specific and may change. `[VERIFY]`

| Operation | MCP tool | Notes |
|---|---|---|
| Resolve team | `list_teams` | Single team today (`Lucadigerlando`). Always call first. |
| List / find project | `list_projects` | Adopt mode discovery. |
| Create / link project | `save_project` | One per dev-flow project. Capture id + url. |
| Create milestone | `save_milestone` | Map epics from `tasks.md`. |
| Create issue | `save_issue` | Pass title, description, estimate, labels, project, milestone. `[VERIFY]` that `estimate` is accepted (needs the team estimate scale enabled). |
| List issues | `list_issues` | Status/velocity pull; duplicate check in DoD. |
| Issue statuses | `list_issue_statuses` | Confirms the state names in `scrum-model.md`. |
| Cycles | `list_cycles` | Check/create the active sprint. `[VERIFY]` whether cycles are creatable via MCP or must be enabled in Linear settings first. |
| Comment | `save_comment` | Optional: post the sprint report as a project comment (still also to chat). |

## Idempotency
Never create an issue whose `task_key` is already in `meta.json#linear.issue_map`. Re-running Setup/Sync must be a no-op for already-pushed tasks. Verify by re-running and confirming `list_issues` count is unchanged.

## Headless loop handoff
The autonomous server loop (Linear → Claude Code → PR) uses a **Linear API key**, not this MCP — see `docs/loop-engineering.md` §1. The states, atomic-issue rule, and `area:*` labels there are the same conventions this skill sets up, so a project bootstrapped here drops straight into that loop. The Linear meter is separate from both billing meters in that runbook (it's just the PM tool).
```

- [ ] **Step 2: Lint + commit**

Run: `cd ~/my-skills && python3 scripts/lint_skills.py linear-scrum`
Expected: no new errors.

```bash
git add linear-scrum/references/linear-mcp.md
git commit -m "docs(linear-scrum): linear-mcp cookbook reference"
```

---

### Task 7: Vendor `references/contracts.md`

**Files:**
- Create: `linear-scrum/references/contracts.md` (copy of `dev-flow/references/contracts.md`)

- [ ] **Step 1: Copy the canonical contract**

Run: `cd ~/my-skills && cp dev-flow/references/contracts.md linear-scrum/references/contracts.md`

- [ ] **Step 2: Verify it copied and lint**

Run: `cd ~/my-skills && head -1 linear-scrum/references/contracts.md && python3 scripts/lint_skills.py linear-scrum`
Expected: first line is `# `.workflow/` — Contract between dev-flow skills`; lint has no new errors.

- [ ] **Step 3: Commit**

```bash
git add linear-scrum/references/contracts.md
git commit -m "docs(linear-scrum): vendor .workflow contract copy"
```

---

### Task 8: dev-flow routing + policy + contract blocks

**Files:**
- Modify: `dev-flow/SKILL.md`
- Modify: `dev-flow/references/contracts.md`

- [ ] **Step 1: Add the routing rows in `dev-flow/SKILL.md` Step 2 table**

In the `## Workflow` → Step 2 phase table, change the `prd_drafted` and `tasks_split` rows to append the Linear proposal. Replace:

```markdown
| `prd_drafted` | `prd-to-tasks` if user wants explicit task breakdown; OR `figma-to-design-md` if user has a Figma; OR `image-to-design-md` if user has 1+ raster images (PNG/JPG screenshots, mockups, Pinterest pins); OR jump to `design-md-to-app` if simple project + DESIGN.md will be hand-written. |
| `tasks_split` | `figma-to-design-md` or `image-to-design-md` or `design-md-to-app`. |
```

with:

```markdown
| `prd_drafted` | `prd-to-tasks` if user wants explicit task breakdown; OR `figma-to-design-md` if user has a Figma; OR `image-to-design-md` if user has 1+ raster images (PNG/JPG screenshots, mockups, Pinterest pins); OR jump to `design-md-to-app` if simple project + DESIGN.md will be hand-written. |
| `tasks_split` | **Propose `linear-scrum` (Setup)** to take the project into Linear + set up scrum (default, skip only on explicit opt-out); then `figma-to-design-md` or `image-to-design-md` or `design-md-to-app`. |
```

- [ ] **Step 2: Add a policy paragraph after the Step 2 table**

Immediately after the phase table (before "The orchestrator must propose, not impose."), insert:

```markdown
**Project-management policy (Linear + scrum).** Every project is run in **Linear with agile scrum** unless the user explicitly opts out. When `tasks.md` exists (phase `tasks_split`, or `prd_drafted` once tasks are generated), propose `linear-scrum` **Setup**. `linear-scrum` is also a **horizontal capability** — invoke it any time for **Sync** (push new tasks, plan the sprint, report velocity), regardless of `phase`. It records `meta.json#linear` + `#scrum` and appends `history`, but never bumps `phase` and never gates progression. For an existing project already partly in Linear, `linear-scrum` **Adopt** backfills the link.
```

- [ ] **Step 3: Document the meta.json blocks in `dev-flow/references/contracts.md`**

After the `### `artifacts` field` section (before `### `phase` enum (canonical)`), insert:

```markdown
### `linear` and `scrum` fields (optional, owned by `linear-scrum`)

Present once a project is taken into Linear. Written only by the `linear-scrum` skill; other skills must not touch them.

```json
"linear": {
  "team_id": "…", "team_name": "…", "project_id": "…", "url": "…",
  "issue_map": { "<task_key>": "LUC-123" },
  "last_synced_at": "<ISO-8601 UTC>"
},
"scrum": {
  "cadence_weeks": 2, "estimate_scale": "fibonacci", "velocity_target": null,
  "states": { "backlog": "Backlog", "todo": "Todo", "in_progress": "In Progress",
              "in_review": "In Review", "done": "Done", "blocked_label": "blocked" }
}
```

`issue_map` keys are `task_key()` digests of `tasks.md` lines (see `linear-scrum/scripts/task_key.py`) so re-syncing never duplicates issues. Linear is the source of truth for status/ordering/estimates after Setup; `tasks.md` stays the append-only intake for new work.
```

- [ ] **Step 4: Lint the whole repo**

Run: `cd ~/my-skills && python3 scripts/lint_skills.py`
Expected: exit 0/1; no errors. The new `linear-scrum` cross-references from `dev-flow` resolve.

- [ ] **Step 5: Commit**

```bash
git add dev-flow/SKILL.md dev-flow/references/contracts.md
git commit -m "feat(dev-flow): route to linear-scrum + document linear/scrum meta blocks"
```

---

### Task 9: Register the skill (install scripts, README, registry, bundle)

**Files:**
- Modify: `install.sh`, `uninstall.sh`
- Modify: `README.md`
- Regenerate: `skills.json`, `dist/linear-scrum.skill`

- [ ] **Step 1: Add `linear-scrum` to the install/uninstall skill lists**

Inspect how skills are enumerated: `cd ~/my-skills && grep -n "prd-to-tasks" install.sh uninstall.sh`. Add a `linear-scrum` entry in the exact same shape (array element / line) right after the `prd-to-tasks` entry in **both** files.

- [ ] **Step 2: Add a README catalog entry**

Find the core-skills section: `cd ~/my-skills && grep -n "prd-to-tasks" README.md | head`. Add a one-line/one-row `linear-scrum` entry mirroring the format of the neighboring `prd-to-tasks` entry (name + one-sentence description: "Take a project into Linear and run it with agile scrum — cycles, estimates, sprint planning, velocity reports; Linear as source of truth").

- [ ] **Step 3: Regenerate the registry and the bundle**

Run:
```bash
cd ~/my-skills
python3 scripts/build_skills_registry.py
python3 scripts/build_skill_bundles.py linear-scrum
```
Expected: `skills.json` updated to include `linear-scrum` (family `core`); `✓ dist/linear-scrum.skill` printed.

- [ ] **Step 4: Verify registry + bundle + full lint**

Run:
```bash
cd ~/my-skills
grep -c '"name": "linear-scrum"' skills.json      # expect 1
test -f dist/linear-scrum.skill && echo bundle-ok
python3 scripts/lint_skills.py                     # expect exit 0/1, no errors
(cd linear-scrum/scripts && python3 -m unittest test_scripts)   # expect all PASS
```
Expected: `1`, `bundle-ok`, clean lint, all unittests PASS.

- [ ] **Step 5: Commit**

```bash
git add install.sh uninstall.sh README.md skills.json dist/linear-scrum.skill
git commit -m "chore(linear-scrum): register skill in install/README/registry + bundle"
```

---

## Self-review notes (author)

- **Spec coverage**: Setup/Adopt/Sync (Task 4), Linear=source-of-truth (Task 4 Sync + contracts Task 8), scrum depth structure+planning+reports (Tasks 4-5), non-blocking policy (Task 8), meta.json blocks (Tasks 3, 8), one-team-many-projects + `blocked` label + `[VERIFY]` on cycles/estimates (Tasks 4-6). Open questions resolved in the header.
- **No new `phase` value** — consistent across Tasks 4 and 8.
- **Idempotency** is the tested correctness property (Tasks 2-3) and the DoD re-run check (Task 4).
- Names consistent: `task_key`, `upsert_linear`, `record_issues`, `ensure_scrum`, `issue_map`, `scrum.states.blocked_label` across all tasks.

## Manual verification (post-implementation, needs the live Linear MCP)

Not automatable offline — do once with the connected MCP on a throwaway `.workflow/` project:
1. Setup on a 3-task `tasks.md` → confirm a Linear Project + 3 issues + 1 cycle; `meta.json#linear.issue_map` has 3 entries.
2. Re-run Setup → issue count unchanged (idempotent).
3. Sync after appending a 4th task → exactly one new issue.

Files in this skill

  • 2026-05-16-rn-expo-skills-wave1.md98.9 KB
  • 2026-07-22-linear-scrum-skill.md30.3 KB
  • 2026-10-17-founder-intelligence.md26.6 KB
  • 2026-10-18-app-security.md18.5 KB
  • 2026-10-18-app-verification.md21.5 KB
  • 2026-10-18-debt-audit.md22.8 KB
  • 2026-10-18-measurement.md23 KB
  • 2026-10-18-scope-changes.md8.8 KB
  • 2026-10-18-scrum-trackers.md17.7 KB
  • 2026-10-18-web-observability.md11.8 KB
  • 2026-10-19-skeleton-shift.md2.6 KB
  • 2026-10-19-taste-v2-port.md9.2 KB

Attribution

Is this your skill, or is something wrong with this listing? Request removal or report an issue. Author removals are honored within 72 hours.

Comments

Loading comments…

Newsletter

Get the weekly list of safe new skills, and the ones we pulled.