diff --git a/plugins/corbits-skills/skills/linear-issue-workflow/SKILL.md b/plugins/corbits-skills/skills/linear-issue-workflow/SKILL.md index 5d11147c7..d5318b543 100644 --- a/plugins/corbits-skills/skills/linear-issue-workflow/SKILL.md +++ b/plugins/corbits-skills/skills/linear-issue-workflow/SKILL.md @@ -1,23 +1,32 @@ --- name: linear-issue-workflow user-invocable: false -description: Skywalker implements a Linear issue by fetching it via MCP then running the /implement spawn loop. DIY tiny/bounded issue edits; spawn build for substantial landings. +description: Skywalker recipe — use_skill("linear-issue-workflow"). Fetch a Linear issue via MCP, then DIY tiny/bounded edits or spawn build for substantial landings. argument-hint: " [--reviewer ]" --- # Linear Issue Workflow -You are Skywalker. Host is Corbits Code. This skill is a spawn recipe for substantial issue work. Tiny / single-file / one-route / clear bounded product edits: DIY with write_file/edit_file/delete_file. Substantial landings: Linear MCP on the primary, then the `/implement` spawn loop. +You are Skywalker. Load with `use_skill("linear-issue-workflow")`. Host is Corbits Code. + +**Classify first:** + +- **DIY** — tiny / single-file / one-route / clear bounded product edits → `write_file` / `edit_file` / `delete_file` on this session. Still fetch the issue and keep Linear status/checkboxes honest. Skip the worktree + `/implement` spawn loop unless the operator wants an isolated branch. +- **Spawn build** — substantial / multi-file / parallel / specialist landings → Linear MCP on the primary, worktree via intern, then the `/implement` spawn loop (greybeard → build → intern/tester → critique). If Linear MCP (`mcp__linear__*`) is missing, stop and tell the operator. Do not invent Claude-only tools. +**Fleet:** This recipe is mostly sequential — prefer `task()` for one blocking spawn at a time. Use `spawn_agent` + `wait_agents` only when independent lanes truly run together. No worker-count or fan-out ceiling. Close the verify → fix → re-verify loop, then report Blockers or `ask_operator` — do not invent hard round ceilings. Track progress with `manage_tasks`. + ## Phase 1: Fetch the issue Fetch with `mcp__linear__get_issue`. The returned issue includes title, description, status, branch name, and other metadata. If the scope is unclear, `ask_operator` before proceeding. Do not guess. -## Phase 2: Worktree — intern +After fetch: if the ask is DIY-tiny, do the edit, update Linear as needed, and stop. Otherwise continue. + +## Phase 2: Worktree — intern (substantial) Read `branchName` from the issue (call `mcp__linear__get_issue` again if needed). @@ -36,7 +45,7 @@ If intern fails, stop and `ask_operator`. If the operator rejects the issue befo ## Phase 3: Plan, attach, mark In Progress 1. Spawn `task(agent="explore")` if the codebase map is not already known. Brief it with the absolute worktree path (it must work there) and the issue: where changes go, existing patterns, related code. -2. Follow the `/implement` loop's greybeard step (Phase 4) for the approach. Present the plan to the operator and `ask_operator` whether to proceed. Do not start implementation until approved. +2. Follow the `/implement` loop's greybeard step for the approach. Present the plan to the operator and `ask_operator` whether to proceed. Do not start implementation until approved. 3. If the operator rejects the plan and the issue cannot be salvaged, intern tears down the worktree (Phase 7) rather than leaving it stranded. 4. Attach the plan to the Linear issue. **Do not post the plan as a comment** — comments are for discussion, not archives. @@ -50,14 +59,14 @@ If intern fails, stop and `ask_operator`. If the operator rejects the issue befo 5. Mark the issue "In Progress" with `mcp__linear__save_issue`. -## Phase 4: Implement — spawn loop +## Phase 4: Implement — spawn loop (substantial) -Do not implement on Skywalker. For each commit-sized unit, run `/implement`: +When this loop is running, do not DIY the unit — spawn build. For each commit-sized unit, run `/implement`: 1. `task(agent="greybeard")` on the approach before any code is written. 2. `task(agent="build")` with a typed brief (`intent`, `success_criteria`, `do_not`, `report_focus`) and the absolute worktree path. Bug fixes start from a failing test. Features ship tests with the change. 3. `task(agent="intern")` or `task(agent="tester")` for the project build/test gate. -4. `task(agent="critique")` on the diff. Blocking findings → re-dispatch build (cap two re-fix rounds), then re-run the gate and critique. +4. `task(agent="critique")` on the diff. Blocking findings → re-dispatch build with those findings in `success_criteria` / `do_not`, then re-run the gate and critique. Close the loop; if still blocked, report Blockers — do not loop forever. Track units with `manage_tasks`. Copy style/philosophy into worker briefs (`use_skill` on the primary before spawning; workers do not mount `use_skill`). @@ -75,7 +84,7 @@ After the last unit's critique is clean, spawn `task(agent="critique")` on the * - Findings with `file:line` — not PR-comment prose - Do not implement fixes -Fix-every-finding: treat surviving findings as a worklist and re-enter Phase 4 for each. Cap three whole-branch re-reviews. If findings remain, `ask_operator` — do not push. +Fix-every-finding: treat surviving findings as a worklist and re-enter Phase 4 for each. If findings remain after a few whole-branch passes, `ask_operator` — do not push. The only path to leaving a finding unfixed is a greybeard waiver: `task(agent="greybeard")` with the finding, proposed disposition, and relevant diff. Accept the ruling by default. Escalate with `ask_operator` only if you disagree or greybeard is unreachable. Never waive on Skywalker's own authority. @@ -154,7 +163,8 @@ If the worktree directory was already deleted: `git worktree prune`. ## Hard rules -- Tiny / single-file / one-route / clear bounded edits: DIY with write_file/edit_file/delete_file. Substantial issue landings: spawn build (this recipe). -- Spawn with `task(agent="greybeard")`, `task(agent="build")`, `task(agent="intern")` or `task(agent="tester")`, and `task(agent="critique")`. +- Tiny / single-file / one-route / clear bounded edits: DIY with write_file/edit_file/delete_file. Substantial issue landings: spawn build (this recipe's Phase 2–7). +- When the spawn loop is running, spawn with `task(agent="greybeard")`, `task(agent="build")`, `task(agent="intern")` or `task(agent="tester")`, and `task(agent="critique")`. - Clarifying questions use `ask_operator`. - Shell is `run_shell`, not a Bash tool. +- No false caps: do not invent worker-count, fan-out, or hard re-fix ceilings that the harness does not enforce. diff --git a/tests/unit/corbits-skills-catalog.test.ts b/tests/unit/corbits-skills-catalog.test.ts index ac0a83361..ad82525b1 100644 --- a/tests/unit/corbits-skills-catalog.test.ts +++ b/tests/unit/corbits-skills-catalog.test.ts @@ -25,7 +25,14 @@ const SKILL_DIRS = [ "plan", ] as const; -const SPAWN_RECIPE_SKILLS = ["implement", "scribe", "review", "dispatch", "plan"] as const; +const SPAWN_RECIPE_SKILLS = [ + "implement", + "scribe", + "review", + "dispatch", + "plan", + "linear-issue-workflow", +] as const; const USE_SKILL_ONLY = [ "dispatch", @@ -106,6 +113,23 @@ test("spawn-recipe skills contain task(agent=", async () => { } }); +test("linear-issue-workflow is a Skywalker recipe with DIY vs spawn build and no false caps", async () => { + const skill = await Bun.file(join(pluginRoot, "skills/linear-issue-workflow/SKILL.md")).text(); + expect(skill).toContain("You are Skywalker"); + expect(skill).toContain('use_skill("linear-issue-workflow")'); + expect(skill).toContain(USER_INVOCABLE_FALSE); + expect(skill).toContain("mcp__linear__get_issue"); + expect(skill).toContain("**DIY**"); + expect(skill).toContain("**Spawn build**"); + expect(skill).toContain('task(agent="build")'); + expect(skill).toContain("No false caps"); + expect(skill).toContain("No worker-count or fan-out ceiling"); + expect(skill).not.toContain("Do not implement on Skywalker"); + expect(skill).not.toContain("cap two re-fix"); + expect(skill).not.toContain("Cap three whole-branch"); + expect(skill).not.toContain("hard cap"); +}); + test("create-issue selects Linear MCP, GitHub gh, and MEMORY.md preference", async () => { const skill = await Bun.file(join(pluginRoot, "skills/create-issue/SKILL.md")).text(); expect(skill).toContain("mcp__linear__");