@rallycry/conveyor-skills 0.1.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md ADDED
@@ -0,0 +1,61 @@
1
+ # @rallycry/conveyor-skills
2
+
3
+ Shared [Claude Code skills](https://code.claude.com/docs) for projects managed
4
+ with [Conveyor](https://github.com/rallycry/conveyor). Installing this package
5
+ and linking it once gives every Claude Code session in your repo the Conveyor
6
+ workflow skills (`/conveyor-plan`, `/conveyor-local-loop`, …), and updates
7
+ arrive through normal dependency bumps — no git submodules, no manual syncing.
8
+
9
+ ## Install (once per repo)
10
+
11
+ ```bash
12
+ bun add -d @rallycry/conveyor-skills # or: pnpm add -D / npm i -D
13
+ bunx conveyor-skills link # or: pnpm exec / npx conveyor-skills link
14
+ ```
15
+
16
+ Then make it stick:
17
+
18
+ 1. **Add the link step to your root `package.json` `prepare` script** so every
19
+ install refreshes the links (new skills in a version bump appear
20
+ automatically; `prepare` runs at the end of every `bun install` — nobody
21
+ ever runs it by hand):
22
+
23
+ ```jsonc
24
+ { "scripts": { "prepare": "conveyor-skills link" } }
25
+ ```
26
+
27
+ If you already have a `prepare` script, chain it: `"husky && conveyor-skills link"`.
28
+
29
+ 2. **Commit the symlinks** it created in `.claude/skills/`. They point into
30
+ `node_modules`, so on a fresh clone they are briefly broken — Claude Code
31
+ silently ignores broken symlinks — and come alive as soon as the first
32
+ install finishes. Committing them means CI agents and teammates get the
33
+ skills with zero extra setup.
34
+
35
+ ## How updates flow
36
+
37
+ Skill content is read live from `node_modules`, so any dependency bump
38
+ (`bun update @rallycry/conveyor-skills`, Renovate/Dependabot, a `^` range
39
+ refresh) updates the skills with no further action. Re-linking is only needed
40
+ when a version adds or removes a skill — which the `prepare` hook already
41
+ covers.
42
+
43
+ ## What `link` does (and won't do)
44
+
45
+ `conveyor-skills link` writes one relative symlink per skill into
46
+ `<repo>/.claude/skills/`, pointing at the stable
47
+ `node_modules/@rallycry/conveyor-skills/skills/<name>` path. It is idempotent
48
+ and safe:
49
+
50
+ - It only ever replaces or prunes symlinks it owns (target contains
51
+ `conveyor-skills/skills/`).
52
+ - A real directory or a foreign symlink with the same name is left alone with
53
+ a warning — your local skills always win.
54
+ - `conveyor-skills link --check` verifies the links without changing anything
55
+ (exit 1 when out of date) — useful in CI.
56
+
57
+ ## Versioning
58
+
59
+ Versions are cut automatically from git tags on the Conveyor repo's `main`
60
+ branch (`conveyor-skills-v<semver>`); every shipped-source change publishes a
61
+ patch release. Consume with a `^` range and bump like any other dependency.
package/bin/cli.mjs ADDED
@@ -0,0 +1,179 @@
1
+ #!/usr/bin/env node
2
+ // conveyor-skills — link this package's Claude Code skills into a consumer
3
+ // repo's .claude/skills/ as relative symlinks. Claude Code follows symlinks
4
+ // there and silently skips broken ones, so the links are safe to commit: they
5
+ // are dead on a fresh clone and come alive after the first install.
6
+ //
7
+ // Usage:
8
+ // conveyor-skills link (default) create/refresh links, prune stale ones
9
+ // conveyor-skills link --check verify links; exit 1 if missing/stale
10
+ //
11
+ // Only links owned by this package (target path contains conveyor-skills/skills/)
12
+ // are ever replaced or pruned. Real directories and foreign symlinks are left
13
+ // alone with a warning.
14
+
15
+ import {
16
+ existsSync,
17
+ lstatSync,
18
+ mkdirSync,
19
+ readdirSync,
20
+ readlinkSync,
21
+ realpathSync,
22
+ rmSync,
23
+ symlinkSync,
24
+ } from "node:fs";
25
+ import { dirname, join, relative, sep } from "node:path";
26
+ import { fileURLToPath } from "node:url";
27
+ import process from "node:process";
28
+
29
+ const OWNED_MARKER = `conveyor-skills${sep}skills${sep}`;
30
+
31
+ function fail(msg) {
32
+ process.stderr.write(`conveyor-skills: ${msg}\n`);
33
+ process.exit(1);
34
+ }
35
+
36
+ function warn(msg) {
37
+ process.stderr.write(`conveyor-skills: warning: ${msg}\n`);
38
+ }
39
+
40
+ /** Nearest ancestor of cwd that looks like a repo root (.git first, then package.json). */
41
+ function findRepoRoot(startDir) {
42
+ for (const marker of [".git", "package.json"]) {
43
+ let dir = startDir;
44
+ for (;;) {
45
+ if (existsSync(join(dir, marker))) return dir;
46
+ const parent = dirname(dir);
47
+ if (parent === dir) break;
48
+ dir = parent;
49
+ }
50
+ }
51
+ return null;
52
+ }
53
+
54
+ function isSymlink(path) {
55
+ try {
56
+ return lstatSync(path).isSymbolicLink();
57
+ } catch {
58
+ return false;
59
+ }
60
+ }
61
+
62
+ function ownedBy(linkPath) {
63
+ try {
64
+ return readlinkSync(linkPath).split("/").join(sep).includes(OWNED_MARKER);
65
+ } catch {
66
+ return false;
67
+ }
68
+ }
69
+
70
+ const args = process.argv.slice(2);
71
+ const checkOnly = args.includes("--check");
72
+ const command = args.find((a) => !a.startsWith("-")) ?? "link";
73
+ if (command !== "link")
74
+ fail(`unknown command '${command}' — only 'link' (with optional --check) exists`);
75
+
76
+ // This package's own skills/ dir, resolved through any install symlink (bun
77
+ // may install the package as a symlink into its store).
78
+ const packageDir = realpathSync(dirname(dirname(fileURLToPath(import.meta.url))));
79
+ const sourceSkillsDir = join(packageDir, "skills");
80
+ if (!existsSync(sourceSkillsDir))
81
+ fail(`no skills directory at ${sourceSkillsDir} (broken install?)`);
82
+
83
+ const repoRoot = findRepoRoot(process.cwd());
84
+ if (!repoRoot)
85
+ fail("could not find a repo root (.git or package.json) above the current directory");
86
+
87
+ const linkDir = join(repoRoot, ".claude", "skills");
88
+
89
+ // Prefer the stable node_modules path as the link target so committed links
90
+ // never embed package-store hashes; fall back to the real package location
91
+ // (the monorepo dogfood case resolves to the same node_modules path).
92
+ const stableSkillsDir = join(repoRoot, "node_modules", "@rallycry", "conveyor-skills", "skills");
93
+ const targetSkillsDir = existsSync(stableSkillsDir) ? stableSkillsDir : sourceSkillsDir;
94
+
95
+ const skillNames = readdirSync(sourceSkillsDir, { withFileTypes: true })
96
+ .filter((entry) => entry.isDirectory())
97
+ .map((entry) => entry.name)
98
+ .sort();
99
+ if (skillNames.length === 0) fail(`no skills found in ${sourceSkillsDir}`);
100
+
101
+ let problems = 0;
102
+ let linked = 0;
103
+ let pruned = 0;
104
+
105
+ if (!checkOnly) {
106
+ try {
107
+ mkdirSync(linkDir, { recursive: true });
108
+ } catch (err) {
109
+ fail(`cannot create ${linkDir}: ${err?.message ?? err}`);
110
+ }
111
+ }
112
+
113
+ for (const name of skillNames) {
114
+ const linkPath = join(linkDir, name);
115
+ const desiredTarget = relative(linkDir, join(targetSkillsDir, name));
116
+
117
+ const linkExists = isSymlink(linkPath);
118
+ const upToDate = linkExists && readlinkSync(linkPath) === desiredTarget;
119
+
120
+ if (upToDate) continue;
121
+
122
+ if (checkOnly) {
123
+ warn(`link for '${name}' is ${linkExists ? "stale" : "missing"}`);
124
+ problems += 1;
125
+ continue;
126
+ }
127
+
128
+ if (existsSync(linkPath) && !linkExists) {
129
+ warn(`${linkPath} exists and is not a symlink — leaving it alone`);
130
+ continue;
131
+ }
132
+ if (linkExists && !ownedBy(linkPath)) {
133
+ warn(
134
+ `${linkPath} is a symlink owned by something else (${readlinkSync(linkPath)}) — leaving it alone`,
135
+ );
136
+ continue;
137
+ }
138
+
139
+ try {
140
+ rmSync(linkPath, { force: true });
141
+ symlinkSync(desiredTarget, linkPath);
142
+ linked += 1;
143
+ process.stdout.write(`linked .claude/skills/${name} -> ${desiredTarget}\n`);
144
+ } catch (err) {
145
+ // Symlink-hostile filesystems (e.g. Windows without developer mode) get a
146
+ // warning, not a broken install.
147
+ warn(`could not link '${name}': ${err?.message ?? err}`);
148
+ }
149
+ }
150
+
151
+ // Prune owned links whose skill no longer ships in the package.
152
+ if (existsSync(linkDir)) {
153
+ for (const entry of readdirSync(linkDir)) {
154
+ const linkPath = join(linkDir, entry);
155
+ if (skillNames.includes(entry) || !isSymlink(linkPath) || !ownedBy(linkPath)) continue;
156
+ if (checkOnly) {
157
+ warn(`link for removed skill '${entry}' still present`);
158
+ problems += 1;
159
+ continue;
160
+ }
161
+ try {
162
+ rmSync(linkPath);
163
+ pruned += 1;
164
+ process.stdout.write(`pruned stale link .claude/skills/${entry}\n`);
165
+ } catch (err) {
166
+ warn(`could not prune '${entry}': ${err?.message ?? err}`);
167
+ }
168
+ }
169
+ }
170
+
171
+ if (checkOnly) {
172
+ if (problems > 0) fail(`${problems} link(s) out of date — run 'conveyor-skills link'`);
173
+ process.stdout.write(`conveyor-skills: all ${skillNames.length} links up to date\n`);
174
+ } else {
175
+ const relLinkDir = relative(repoRoot, linkDir) || linkDir;
176
+ process.stdout.write(
177
+ `conveyor-skills: ${skillNames.length} skill(s) in ${relLinkDir} (${linked} updated, ${pruned} pruned)\n`,
178
+ );
179
+ }
package/package.json ADDED
@@ -0,0 +1,24 @@
1
+ {
2
+ "name": "@rallycry/conveyor-skills",
3
+ "version": "0.1.0",
4
+ "description": "Shared Claude Code skills for Conveyor consumer repos, linked into .claude/skills via the conveyor-skills CLI",
5
+ "keywords": [
6
+ "claude",
7
+ "claude-code",
8
+ "conveyor",
9
+ "skills"
10
+ ],
11
+ "license": "MIT",
12
+ "bin": {
13
+ "conveyor-skills": "bin/cli.mjs"
14
+ },
15
+ "files": [
16
+ "bin",
17
+ "skills",
18
+ "README.md"
19
+ ],
20
+ "type": "module",
21
+ "publishConfig": {
22
+ "access": "public"
23
+ }
24
+ }
@@ -0,0 +1,157 @@
1
+ ---
2
+ name: conveyor-local-loop
3
+ description: Run this machine as a serial local claudespace — pick the session owner's highest-priority Open Conveyor card, claim it, execute its plan to the PR finish line, repeat. One invocation = one iteration; run continuously with "/loop /conveyor-local-loop" (no interval) and it self-paces (~1-2 min between cards while the queue has work, ~25 min idle polls when empty). Use when the user says "/conveyor-local-loop", "start the local loop", "work my open cards locally", or wants planned cards executed with full local CPU/RAM instead of spawning claudespaces.
4
+ ---
5
+
6
+ # Conveyor Local Loop
7
+
8
+ Turn a queue of researched Open cards (usually authored via `/conveyor-plan`)
9
+ into review-ready PRs using this machine, one card at a time. Behave exactly as
10
+ a claudespace agent would: the card IS the spec, task chat is the log, and the
11
+ host repo's CLAUDE.md governs gates, verification, and PR mechanics. This skill
12
+ adds only selection, claiming, cadence, and local-machine hygiene.
13
+
14
+ ## Ground rules
15
+
16
+ - **Conveyor is the state store.** The loop session spans days and gets
17
+ compacted; at each iteration re-derive state from MCP reads, never from
18
+ conversation memory.
19
+ - **All Conveyor tools fully-qualified** (`mcp__conveyor__get_task`; bare names
20
+ fail).
21
+ - **WIP = 1.** At most one card claimed by this loop at a time.
22
+ - **Never `start_task`** — that boots a cloud pod and duplicates the work. Only
23
+ exception: the opt-in offload valve below.
24
+ - **Never approve or merge your own PRs.** Finish line = card in ReviewPR with
25
+ green CI; the user takes it from there.
26
+ - **Only cards created by the session owner** (match against
27
+ `mcp__conveyor__get_connection_context`), status Open, unassigned. Teammates'
28
+ cards and pod-claimed cards are off limits.
29
+ - **Pack cards: parents never; children only while the pack is parked.** A
30
+ card with children is the coordinator itself — never claimable. A card with
31
+ a `parentTaskId` IS claimable, but only while the parent's status is neither
32
+ InProgress nor ReviewPR: the pack watchdog and the child-event notifier both
33
+ ignore parked parents, but an actively-orchestrating parent reads a headless
34
+ InProgress child as a dead agent environment and "recovers" it onto a cloud
35
+ pod (observed 2026-07-28 — duplicate implementation). Working a pack locally
36
+ means: leave the parent parked (never Build/start it), work the children
37
+ serially in dependency order, and the user merges each child PR into the
38
+ pack branch to unblock the next.
39
+ - **Clean tree before any branch switch — hard rule.** Dirty `git status` at
40
+ iteration start → touch nothing, report, and idle: the cwd is probably the
41
+ user's interactive checkout. Recommend a dedicated checkout once
42
+ (`git worktree add ../<repo>-loop dev`) and running the loop session there.
43
+ - **Push early.** There is no pod WIP-autosync locally; committed-and-pushed is
44
+ the only durable state. Push the branch (`-u origin`) as soon as it exists.
45
+
46
+ ## Iteration order
47
+
48
+ Each invocation does the FIRST of these that produces work, then paces:
49
+
50
+ 1. **Recover** — a card with my `[local-loop] claimed` chat marker still
51
+ InProgress without a PR? Resume it. The branch may already exist locally or
52
+ on origin — check both before re-implementing anything.
53
+ 2. **Babysit** — my loop-opened PRs (ReviewPR cards): red CI → fix now;
54
+ request-changes or unanswered review comments → address now; green and
55
+ quiet → leave alone.
56
+ 3. **Claim** the next card (below).
57
+ 4. **Idle** — nothing claimable: pace long.
58
+
59
+ ## Claiming
60
+
61
+ 1. `mcp__conveyor__list_tasks` with `status: "Open"` — results are already
62
+ priority-then-newest ordered; board priority IS the intelligence, don't
63
+ invent your own ranking. Walk top-down, `mcp__conveyor__get_task` each until
64
+ one passes: created by me, no assignee or active session, an executable
65
+ plan, all `mcp__conveyor__get_dependencies` blockers Complete, no
66
+ `[local-loop] parked:` chat marker without a later human reply (a reply
67
+ un-parks), no child tasks of its own, and — if it has a `parentTaskId` —
68
+ the parent's status is neither InProgress nor ReviewPR (see ground rules).
69
+ Skip `followParentStatus` mirror children. A blocker counts as met only
70
+ when merged-or-beyond (ReviewDev/ReviewLive/Complete) or Cancelled — a
71
+ blocker sitting in ReviewPR is NOT met until its PR merges.
72
+ 2. Claim: re-confirm via `get_task` it is still Open, then
73
+ `mcp__conveyor__update_task` → `status: "InProgress"`, then
74
+ `mcp__conveyor__post_to_chat`: `[local-loop] claimed — working locally on
75
+ <hostname>`. Status changed under you → someone else took it; next
76
+ candidate.
77
+ 3. Plan missing or failing the context-free-reader bar → don't wing it: post
78
+ what's missing to chat, leave the card Open, skip it.
79
+
80
+ ## Execute and finish
81
+
82
+ 1. `mcp__conveyor__get_task` (full plan) + `mcp__conveyor__read_task_chat`
83
+ (addenda, user answers). The card must stand alone — if you find yourself
84
+ relying on loop-session memory, stop and re-read the card instead.
85
+ 2. Branch from the card's base, never blindly dev: `base` = the card's
86
+ `baseBranch` (a pack child's base is the PARENT's feature branch). If the
87
+ card already has a `githubBranch` with commits on origin, resume THAT
88
+ branch — a prior pod may have landed real work; audit it before
89
+ re-implementing anything. Else `git fetch origin <base> && git checkout -B
90
+ <feat|fix|chore>/<slug> origin/<base>`. Reinstall deps if the lockfile
91
+ changed.
92
+ 3. Work the plan. Post chat updates at real milestones only (claim, blocking
93
+ discovery, gates green, PR) — not play-by-play.
94
+ 4. Verify per the host repo's CLAUDE.md policy (scoped gates). UI-visible
95
+ change → capture screenshot/recording evidence with the repo's tooling and
96
+ attach via `mcp__conveyor__upload_attachment` before opening the PR.
97
+ 5. Refresh against the card's base (`git fetch origin <base> && git merge
98
+ origin/<base> --no-edit && git push`), then
99
+ `mcp__conveyor__create_pull_request` with `head:` your branch and `base:`
100
+ the card's base branch. Pack child: re-check the parent's status first —
101
+ if it went InProgress/ReviewPR, park instead (post `[local-loop] parked:
102
+ pack coordinator active — yielding`, leave the branch pushed) and let the
103
+ coordinator take over. The card moves to ReviewPR. Post a chat summary:
104
+ what shipped, how verified, what to look at.
105
+ 6. Confirm CI actually started (read-only `gh pr checks`); do NOT wait on it —
106
+ later iterations babysit.
107
+
108
+ **Parked protocol** — after 2 genuinely different failed approaches, or on a
109
+ decision only the user can make: post `[local-loop] parked: <reason + the
110
+ specific question>`, set status back to `"Open"`, restore the tree
111
+ (`git checkout dev`), move on. The user's next chat reply is the un-park
112
+ signal.
113
+
114
+ ## Pacing (dynamic /loop only)
115
+
116
+ Under `/loop` with no interval, end EVERY iteration with exactly one
117
+ `ScheduleWakeup` (prompt = the original /loop input verbatim):
118
+
119
+ | State | Delay | Reason should say |
120
+ |-------|-------|-------------------|
121
+ | A background gate/agent is in flight — its completion notification is the real wake | 1200–1800s fallback | "fallback while <gate> runs — its notification wakes me sooner" |
122
+ | ANY actionable work exists: claimable cards, a PR still to open, red/pending CI, review comments | 60–90s | queue depth / which item is next |
123
+ | Queue enumerated as empty THIS iteration, all loop PRs green and quiet | 1200–1800s | queue empty, idle poll |
124
+ | Loop-fatal: MCP dead after 2 tries, dirty tree, broken repo | notify the user (PushNotification if available), then 1800s — or `stop: true` if continuing is unsafe | what is wrong |
125
+
126
+ The long idle tier is EARNED, never defaulted: it requires having enumerated
127
+ the queue this very iteration and found zero actionable work. When unsure
128
+ which tier applies, take the short one — a wasted 60s wake costs less than a
129
+ 30-minute stall on live work.
130
+
131
+ Invoked bare (no /loop)? Run one iteration, report, and suggest
132
+ `/loop /conveyor-local-loop` — don't self-schedule.
133
+
134
+ ## Offload valve (opt-in)
135
+
136
+ Only with an explicit `offload=N` argument: when 4+ claimable cards queue up,
137
+ `mcp__conveyor__start_task` up to N of the smallest into claudespaces and say
138
+ so in the iteration summary. Without the argument, never — just report backlog
139
+ depth each iteration so the user can offload manually.
140
+
141
+ ## What this is not
142
+
143
+ - Not a reviewer: never `approve_task`, `approve_and_merge_pr`, or
144
+ `request_changes` on anything.
145
+ - Not a pod: no sandbox, no WIP snapshots, and the dev DB + dev-server ports
146
+ are shared with the user's interactive sessions — no destructive
147
+ experiments, never reset the dev DB, reuse a running dev stack rather than
148
+ fighting over ports.
149
+ - Not a parallel executor: one card at a time is the point (the full machine
150
+ per gate). Backlogged? That is what claudespaces — or the offload valve —
151
+ are for.
152
+
153
+ ## Improve This Skill
154
+
155
+ If this skill was insufficient or slowed the work down, file it with
156
+ `mcp__conveyor__create_suggestion` on the Conveyor project: the issue,
157
+ evidence, and proposed fix.
@@ -0,0 +1,98 @@
1
+ ---
2
+ name: conveyor-plan
3
+ description: Turn a raw feature or bug idea into a research-backed, immediately-buildable Conveyor card for a claudespace agent to execute later. Use when the user says "/conveyor-plan <idea>", "plan this as a conveyor card", "write this up for a claudespace agent", or wants deep planning that ends in a handoff card rather than local implementation. Runs plan-mode-quality research (codebase + prior Conveyor cards + prod logs), asks clarifying questions only when scope is genuinely ambiguous, then creates the card and moves it to Open so identification auto-fills story points, tags, icon, and agent.
4
+ ---
5
+
6
+ # Conveyor Plan → Handoff Card
7
+
8
+ Produce ONE artifact: a Conveyor card whose plan a fresh claudespace agent can
9
+ execute with zero session context. Everything the executor needs must be ON the
10
+ card — it never sees this conversation.
11
+
12
+ The division of labor is fixed: **you research and recommend; Conveyor's
13
+ identification decides.** Moving the card to Open fires identification
14
+ automatically (story points, icon, agent, and tags if you set none). Never set
15
+ icon or story points yourself, and never `start_task` unless the user asks.
16
+
17
+ ## Phase 0 — Resolve context
18
+
19
+ 1. `mcp__conveyor__get_connection_context` (all Conveyor tools fully-qualified;
20
+ bare names fail). No default project? `mcp__conveyor__list_projects` and
21
+ match `githubRepoOwner/Name` to the cwd's `git remote`. Ambiguous → ask.
22
+ 2. Copy project IDs exactly — a mistyped `projectId` surfaces as
23
+ "Insufficient permissions", not "not found". Verify the ID before
24
+ concluding you lack access.
25
+
26
+ ## Phase 1 — Research (parallelize)
27
+
28
+ Scale to the idea's size; a one-file tweak needs minutes, not a survey.
29
+
30
+ - **Codebase**: use a code-graph or architecture skill if the repo provides
31
+ one for architecture/flow questions; `rg` for exact strings. Fan out
32
+ Explore subagents for broad sweeps.
33
+ - **Prior art**: `mcp__conveyor__search_tasks` on 2-3 keyword variants
34
+ (`typeFilters` to include incidents/suggestions when relevant), then
35
+ `mcp__conveyor__get_task` on the closest hits. You're looking for duplicates
36
+ (stop and surface), related shipped work (reuse its patterns), and
37
+ cancelled attempts (learn why before re-proposing).
38
+ - **Prod signals** (only when the feature touches live behavior):
39
+ `mcp__conveyor__query_gcp_logs` / `mcp__conveyor__query_grafana_logs` for
40
+ error rates, actual usage, current behavior.
41
+ - **Blast radius**: enumerate callers/consumers of every surface the plan
42
+ touches (shared packages, DB schema, published packages, webhooks, other
43
+ cards in flight on the same files). Unintended impact goes in the plan's
44
+ Notes, not in your head.
45
+
46
+ ## Phase 2 — Clarify (only if needed)
47
+
48
+ Ask the user only decisions that change the plan's shape — scope cuts, UX
49
+ choices, irreversible tradeoffs. Batch them in one round; never drip. Facts
50
+ the repo can answer are yours to find, not theirs.
51
+
52
+ ## Phase 3 — Draft the plan
53
+
54
+ Use the plan format in [references/plan-format.md](references/plan-format.md):
55
+ Objective / Approach / Implementation Steps / Testing / Notes.
56
+
57
+ Actionability bar — every step must survive a context-free reader:
58
+
59
+ - Name exact repo-relative files and symbols, with the pattern to follow
60
+ ("mirror `apps/api/src/services/task/methods/mutations.ts`").
61
+ - Testing = runnable commands + observable acceptance criteria.
62
+ - Notes = risks, blast radius, dependencies, and decisions already made (so
63
+ the executor doesn't relitigate them).
64
+ - No "as discussed", no links to this chat, no TODOs the executor must
65
+ research from scratch.
66
+
67
+ Size it: 1 SP default, 2 multi-file, 3 complex patterns/design, 5 hard. Larger
68
+ → propose a pack (parent + `mcp__conveyor__create_subtask` children with
69
+ `mcp__conveyor__add_dependency` edges) instead of one mega-card.
70
+
71
+ Show the user title + description + plan + SP/tag recommendation before
72
+ touching Conveyor, unless they asked you to just ship it.
73
+
74
+ ## Phase 4 — Create and hand off (order matters)
75
+
76
+ Identification fires the moment the card lands beyond Planning and reads task
77
+ chat for your recommendation — so chat BEFORE the status flip:
78
+
79
+ 1. `mcp__conveyor__create_task` — `status: "Planning"`, concise imperative
80
+ title, 2-4 sentence description (the board-card summary), full plan.
81
+ Optionally `tags`: only names from `mcp__conveyor__list_tags` that clearly
82
+ fit (unknown names are rejected; pre-set tags make identification skip tag
83
+ assignment — when unsure, omit and let it choose).
84
+ 2. `mcp__conveyor__post_to_chat` — one message: recommended SP + one-line
85
+ rationale, tag suggestion, any executor warnings.
86
+ 3. `mcp__conveyor__update_task` — `status: "Open"` (+ `risk` when the plan
87
+ touches critical surface). This triggers identification.
88
+ 4. Verify with `mcp__conveyor__get_task`: status Open, `agentId` set. SP and
89
+ tags are NOT in the `get_task` response — confirm on the board if needed.
90
+ 5. Report the card URL (`<project url>/cards/<slug>`), what identification
91
+ filled, and that it's ready for `start_task`. Park-in-Planning instead if
92
+ the user wants to review first; offer (don't run) `start_task`.
93
+
94
+ ## Improve This Skill
95
+
96
+ If this skill was insufficient or slowed the work down, file it with
97
+ `mcp__conveyor__create_suggestion` on the Conveyor project: the issue,
98
+ evidence, and proposed fix.
@@ -0,0 +1,56 @@
1
+ # Conveyor Plan Format & Sizing
2
+
3
+ ## Plan Format
4
+
5
+ ```markdown
6
+ ## Objective
7
+ One sentence: what this task accomplishes and why.
8
+
9
+ ## Approach
10
+ High-level strategy with relevant files/patterns/APIs.
11
+
12
+ ## Implementation Steps
13
+ 1. Concrete step.
14
+ 2. Concrete step.
15
+
16
+ ## Testing
17
+ - Commands and manual checks.
18
+ - Edge cases.
19
+
20
+ ## Notes
21
+ - Dependencies, risks, blockers, or files likely touched.
22
+ ```
23
+
24
+ ## Sizing
25
+
26
+ Default **1 SP**. Use **2 SP** for multi-file work, **3 SP** for complex
27
+ patterns/design choices, **5 SP** only for hard work. Split anything larger.
28
+
29
+ ## Packs (parent + child tasks)
30
+
31
+ A **pack** is Conveyor's bundle shape: a parent card with child cards.
32
+ `create_subtask(parentTaskId, ...)` is the **only** way to parent a card —
33
+ `create_task`/`update_task` have no parent field. Each child is a full card
34
+ (own chat, plan, story points). Split into a pack only when the work is
35
+ genuinely multiple independently buildable pieces (8-SP-tier); otherwise keep
36
+ one card.
37
+
38
+ - **Orchestration packs** (future work): `start_task` on the parent boots a
39
+ *pack runner* that starts each ready child's own build/PR, honoring
40
+ `add_dependency` edges (independent children run in parallel). Give each
41
+ child a detailed plan with a **Testing / Verification** section and a
42
+ `storyPointValue`. The parent's `featureBranch` toggle (default on) makes
43
+ children branch off and PR back into the parent's branch instead of dev.
44
+ - **Mirror packs** (already-done work shipping in ONE PR on the parent's
45
+ branch): create children with **`followParentStatus: true`** — title and a
46
+ plain-language description only. A follower mirrors its parent's status
47
+ automatically through the whole pipeline, identification sizes it, and it
48
+ never posts its own Slack card. Evidence rolls up to the **parent**. Never
49
+ `start_task` a mirror pack's parent, and leave children's PR fields empty —
50
+ the parent owns the PR.
51
+
52
+ ## Status Flow
53
+
54
+ `Planning -> Open -> InProgress -> ReviewPR -> ReviewDev -> ReviewLive -> Complete`
55
+
56
+ `Cancelled` is terminal. A task must be `Open` before `start_task`.