projector-dev

star 0

Selection-driven work orchestrator backed by the Projector MCP server. The human curates a selection of work on the board; this skill reads that selection as a single self-contained super-prompt, executes the whole scope in one continuous pass, and writes deliveries + status back in one batched call. Use when the user asks you to "work the selected backlog", "do my next task", "run the selection", "iterate on the project", or otherwise drive selected work to completion.

dragonworx By dragonworx schedule Updated 6/10/2026

name: projector-dev description: Selection-driven work orchestrator backed by the Projector MCP server. The human curates a selection of work on the board; this skill reads that selection as a single self-contained super-prompt, executes the whole scope in one continuous pass, and writes deliveries + status back in one batched call. Use when the user asks you to "work the selected backlog", "do my next task", "run the selection", "iterate on the project", or otherwise drive selected work to completion.

Instructions

Projector is a backlog the human curates and the agent executes. The human selects an epic, a story, or individual tasks on the board (selecting a parent cascades to its children); that selection is persisted per-project and is the scope — "what the user wants worked next". Nothing about what to do is invented by the server or by you: the selection is the source of truth.

Project → Epic → Story → Task          (Task is the leaf unit; there are no work-items)
backlog → todo → in_progress → in_review → done | cancelled

"Active" excludes done, cancelled, and in_review. The selection is the scope; the active, unblocked tasks within it are the next work.

The core idea: read once, work continuously, write once

The latency that hurts an agent walking a backlog is the round trip — work a task, ask the server "what's next?", work the next, ask again. Every round trip stalls generation. Projector removes it with two primitives that bookend a single continuous work session:

backlog.next_prompt → the entire selection as one self-contained brief (read once) → you do all the work in one pass (no per-task polling) → backlog.complete records every delivery + status flip in one call (write once).

That is the fast path, and it is the default. Reach for sub-agents and chunking (below) only when the scope is too big for one session or the work is provably parallel — not as a matter of course.

If nothing is selected, say so and stop. Do not pick work for the user. An empty selection means "the human hasn't queued work yet."

The two primitives

backlog.next_prompt { project_id? } — read the whole scope, once

Returns { project_id, empty, reason, selected_count, open_tasks, prompt }. The server does all the enrichment that used to need many reads:

  • prompt — a ready-to-execute Markdown brief over the whole selection: project context, every task grouped by epic→story in board order, acceptance criteria inlined, named blockers (flagged unresolved), prior deliveries (so resumed work isn't redone), open work numbered "do these in order", finished work dropped to a context block, and a writeback section. Each open task prints its id as task_id: … under its heading.
  • open_tasks — the machine-readable companion: the workable tasks in the same numbered order, each { id, title, story_id, status }. Use these ids for writeback — you never need a second read to map a title back to an id.
  • empty: true with reason empty_selection | no_projectprompt already explains it; report and stop.

Always pass project_id so the session stays pinned to one project.

backlog.complete { tasks } — write the whole result, once

Records deliveries/artifacts and flips statuses for many tasks in one call — the writeback half of the fast path. Shape:

{
  "tasks": [
    {
      "task_id": "<from open_tasks / the task_id: line>",
      "deliveries": [ { "location": "src/foo.ts:42", "comment": "what you delivered" },
                      { "location": "src/foo.ts:80-96", "comment": "..." } ],
      "artifacts":  [ { "kind": "pr_url", "value": "https://…", "label": "PR" } ],   // optional
      "status": "done"     // optional; defaults to "done"
    }
    // …one object per finished task
  ]
}
  • Record a delivery for every file/range you changed (path:N or path:N-M; :1 for a new file).
  • A done flip with zero deliveries is rejected — the server enforces "every completed task has at least one delivery". Each task is applied in its own transaction, so one rejection never rolls back the others; the response reports per-task { ok, status, deliveries_added, artifacts_added, error? }. Read it and re-handle any ok: false.
  • Story/epic status is derived, not written. Parent stories and epics roll up their tasks automatically — a story is done once all its live tasks are done, in_progress while any remain. You only ever set task status; there is no story writeback. For human sign-off on a unit of work, leave its tasks in in_review (humans flip in_review → done).

Interrogating the backlog (the escape hatch)

The brief is meant to be complete, so you should rarely need more. When it genuinely is missing something, read it directly — task.get, delivery.list, dependency.blockers, dependency.is_blocked, artifact.list, or backlog.selected / backlog.tree for shape. These are reads; they don't mutate the selection. Prefer them over guessing, but don't pre-fetch what the brief already gave you — that reintroduces the round trips the fast path exists to remove.

Project selection

The selection is per-project, so decide which project the session drives before the first read:

  1. User named a project — resolve to a project_id (project.list if needed) and use it.
  2. No project named, exactly one active project — use it; emit one line ("Driving project <name> (<id>).") so the user can correct.
  3. No project named, multiple activestop and ask via AskUserQuestion (call project.list once, present active projects, wait).
  4. No active project — report "No active project — invoke projector-plan first." and stop.

Execution tiers — pick the smallest that fits

Tier 1 — direct one-shot (DEFAULT)

For a selection that fits one focused session (a story, a handful of tasks, a small epic):

  1. backlog.next_prompt { project_id }. If empty, report the reason and stop.
  2. Work the numbered tasks in order, in this session — resume any in_progress task first, then take the rest top-to-bottom. Respect blockers: don't start a task whose blocker isn't done; surface it instead. Treat the "already in place" context block as fixed — read it, extend it, don't rebuild it.
  3. Run the project's tests/build; only treat a task as done when its acceptance criteria verifiably pass.
  4. One backlog.complete call at the end: every changed file/range as a delivery and each finished task's status. Parent story/epic status rolls up automatically — leave a task in_review if its work needs human sign-off. Check the per-task results.

This is the fastest path: one read, continuous work, one write — no sub-agent spawn, no per-task polling.

Direct vs. one sub-agent. Doing the work directly in the main session is fastest (no spawn, no context handoff). Dispatch a single sub-agent with the prompt only when you want main to stay a thin ledger across several chunks (Tier 2) — the sub-agent does steps 2–4 and returns a ≤120-token line. For a single chunk, prefer direct.

Tier 2 — chunked (large selection, still one pass per chunk)

When the selection spans many stories or is too large to hold in one focused session, work it one story at a time, each story a continuous session ending in its own backlog.complete. Because next_prompt always reflects live status, re-calling it between chunks shows finished stories as context and surfaces the next open work — so re-invoking naturally resumes. Decide a cap (default: ask the user how many stories/hand-offs if more than one) and stop at it. Never mutate the selection to track progress — status does that.

Tier 3 — parallel / isolated (only when provably independent)

Fan out concurrent sub-agents only when the chunk's tasks are genuinely independent: no dependency edge between them, disjoint files/areas, none needs another's output, comparable size. Give each its slice of the prompt and its own task ids; each writes back via backlog.complete (or returns deliveries for the main agent to batch). Reserve isolation: worktree for the rare case where parallel agents must edit overlapping areas; disjoint-file parallelism needs only plain concurrent sub-agents. When in doubt, go linear (Tier 1/2) — a wrong parallel call races on shared files and corrupts work, costing far more than the wall-clock it saves.

Per-hand-off display (Tier 2/3 only)

When you dispatch sub-agents across multiple chunks, main emits exactly one banner + one result line per chunk, nothing else (no diffs, no tool narration):

━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━
 hand-off <N>/<cap>   <story title or "task: <title>">
━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━

…then after backlog.complete returns:

hand-off <N>/<cap>: <story_id> → <status>

For empty/no_project, drop the id and print the reason, e.g. hand-off 1/1: nothing selected — ask the user to select work on the board. Long output (summaries, PR urls) goes to artifact.add / event.append, never the chat. In Tier 1 you don't need the banner — just do the work and report the backlog.complete outcome plainly.

Progress

Progress is calculated, never set. A story = done tasks / total tasks, an epic = done stories / total, a project = done epics / total. Moving a task to done (with its delivery) is what advances the story bar; flipping a story to done advances the epic bar. There is no progress.set.

Interruption recovery

A run can be cut off mid-flight — tasks worked but never written back. Projector is the source of truth — never reconstruct from the transcript. On resume, re-read state: backlog.next_prompt will show whatever is still open (anything you completed and wrote back has dropped to the context block), so simply re-running the fast path resumes the remaining work. To see what was left mid-flight, task.list { status: "in_progress" }.

Tool reference

All tools are MCP tools on the projector server; every tool named here exists (verified against src/mcp/tools/).

The fast path

  • backlog.next_prompt { project_id? }{ project_id, empty, reason, selected_count, open_tasks, prompt }. The whole selection as one brief + the open task ids. Read once.
  • backlog.complete { tasks:[{ task_id, deliveries?, artifacts?, status?, allow_no_delivery? }] } → per-task results. Records deliveries+artifacts and flips task statuses in one call; enforces the done-needs-delivery gate (override per task with allow_no_delivery: true for a genuinely delivery-free item); per-task transactional. Parent story/epic status is derived, never written here. Write once.

Interrogation (reads only — use when the brief is genuinely insufficient)

  • backlog.selected { project_id? }{ project, tasks } flat in board order (null = no project; tasks: [] = nothing selected). For inspecting shape or building a richer/custom prompt.
  • task.get / story.get / epic.get / project.get { id } — resolve lineage.
  • dependency.blockers { item_type, item_id } / dependency.is_blocked { item_type, item_id } — name/filter blockers.
  • delivery.list { task_id } / artifact.list { task_id } — prior outputs on a task.
  • backlog.tree { project_id? } and resource projector://project-state/{id} — full snapshots; heavy, use sparingly.

Granular mutations (Tier 3, or when backlog.complete isn't enough)

  • delivery.add { task_id, location, comment } / artifact.add { task_id, kind, value, label? } — single delivery/artifact (the per-item form of what backlog.complete batches).
  • task.update { id, status?, … } — single task status flip. (story.update / epic.update patch title/description/color only — their status is derived from tasks, not settable.)
  • event.append { item_type, item_id, kind, actor?, message?, payload? } — free-form audit; the only mutation that accepts actor ("orchestrator" / "sub-agent:<n>").

What does NOT exist

loop.configure/start/status/checkpoint, backlog.next, work_item.*, task.claim/release, and progress.set do not exist. There is no assignee/claim concept — this is a tight dev↔AI loop, not multi-author tracking. Tasks are leaves; there is no sub-task layer. Iteration (Tier 2) is a plain counter you keep.

Rules

  • The selection is the contract. Work only what the user selected, filtered to active + unblocked tasks. Never invent work. Empty selection → report it and stop.
  • One read, one write — by default. backlog.next_prompt then backlog.complete. Don't poll per task; don't pre-fetch what the brief already contains. Interrogate the backlog only to fill a genuine gap.
  • Every task→done has ≥1 delivery. Record one per file/range you changed, in the same backlog.complete call. The server rejects a done flip with none — read the per-task results and re-handle any ok: false. The rare genuinely delivery-free task (a spike, a decision, docs tracked elsewhere) can be closed deliberately with allow_no_delivery: true on that task; it's an explicit opt-out, not the norm.
  • Set task status forward. in_progress when work starts, in_review when a task's output needs human sign-off, done only when acceptance criteria pass (and a delivery exists). Humans own in_review → done. Story/epic status follows the tasks automatically — never try to set it.
  • Linear by default; parallel only when provably independent. Disjoint files, no dependency edges, no shared output. When in doubt, linear.
  • Don't mutate the user's selection. Curation is the human's job; track progress via status, not by deselecting.
  • Trust the server, not the transcript, after any disruption. Re-read with backlog.next_prompt; what's still open is what's left.
  • Keep chat output thin in multi-chunk runs. Banner + result line per chunk; long content to artifact.add / event.append.

Quality checklist

  1. Did you resolve a target project_id before the first read — asking the user if more than one active project was viable?
  2. On empty/no selection, did you clearly say "nothing selected — select work on the board" and stop, rather than inventing work?
  3. Did you read the scope once (backlog.next_prompt) and work it continuously, instead of polling per task?
  4. Did every changed file/range get a delivery, and did you write everything back in one backlog.complete (per chunk) keyed by open_tasks ids?
  5. Did you check the backlog.complete per-task results and re-handle any ok: false (e.g. a missing delivery)?
  6. Did you keep to the smallest tier that fits — direct one-shot unless the scope was too big or provably parallel?
  7. Did you leave tasks needing human sign-off in in_review rather than auto-closing them done? (Story/epic status rolls up from the tasks — you don't set it.)
  8. After any disruption, did you re-read server state instead of reconstructing from memory?
Install via CLI
npx skills add https://github.com/dragonworx/projector --skill projector-dev
Repository Details
star Stars 0
call_split Forks 0
navigation Branch main
article Path SKILL.md
More from Creator