From c400c91a80d6d5c5083578d6df9549907d016586 Mon Sep 17 00:00:00 2001 From: Sulthan Zaki Date: Tue, 11 Aug 2026 10:08:09 +0700 Subject: [PATCH] Implement a batch of tickets through per-ticket subagents (#82) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit ## What this adds Two files that turn the one-ticket-at-a-time `/implement` loop into an orchestrated batch. **`.claude/skills/implement-tickets/SKILL.md`** — user-invoked (`disable-model-invocation: true`, so it costs no context until typed). The agent that runs it is an orchestrator, not an implementer: 1. Collect the tickets over `tea`, reading each `Blocked by` line. 2. Plan waves from the blocking edges, three tickets wide, and fix every cross-ticket contract (shared signature, JSON shape, column, token) before anything is dispatched. 3. Present the plan and stop for approval. 4. Per ticket: `git worktree add ../ticket-`, copy the gitignored `.env`, claim the issue, write a brief to `.scratch/`, then dispatch the whole wave as one `task` batch. 5. Land each result — merge `--no-ff`, comment the report, close, remove the worktree. Textual conflicts are the orchestrator's; a semantic clash goes back to whichever ticket owns the contract. 6. Full suite once on the merged base. **`.omp/agents/ticket-implementer.md`** — the worker. Brief-driven, worktree-bound, and gated on review before it reports: it runs the `code-review` skill over its own diff with `cr-spec` and `cr-standards` on the two axes, fixes Critical and Important findings in at most two rounds, and returns a short status contract (`DONE` / `DONE_WITH_CONCERNS` / `BLOCKED` / `NEEDS_CONTEXT` / `REVIEW_BLOCKED`). The brief template makes the subagent read `tea issue --comments` for its ticket and for the issue that ticket refers to — the comments carry decisions the body never got updated with — and names the `tdd` skill at each seam where a test comes first. Briefs are written in the ubiquitous language of `CONTEXT.md`; a brief that says "scrape" where the domain says Poll hands the subagent the wrong model of the system. ## Verification Dispatched a real `ticket-implementer` as a probe. The agent resolved from `.omp/agents`, and it spawned `cr-spec`, which replied. That was the one thing that could have silently killed the design: `task.maxRecursionDepth` defaults to 2, and the chain is session to orchestrator to implementer to reviewer. It clears. If that ever changes, the implementer returns `REVIEW_BLOCKED` and the orchestrator runs the review itself. Confirmed against the omp binary that `autoloadSkills: code-review, tdd` is split by `parseArrayOrCSV`, not swallowed as one unknown name. ## Notes - Agents are discovered from `.omp/agents`, never `.claude/agents` — the latter is deliberately skipped by omp because its frontmatter is a different contract. - No product code changes. `.gitignore` gains `.scratch/`, where briefs and reports live. - Not included: retry after a failed dispatch, a state file for resuming a crashed wave, a cheap model tier for mechanical tickets. Add them when a real batch needs them. Reviewed-on: https://gitea.violetcrown.my.id/sulthan/mangaBookmark/pulls/82 Co-authored-by: Sulthan Zaki Co-committed-by: Sulthan Zaki --- .claude/skills/implement-tickets/SKILL.md | 134 ++++++++++++++++++++++ .gitignore | 1 + .omp/agents/ticket-implementer.md | 90 +++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 .claude/skills/implement-tickets/SKILL.md create mode 100644 .omp/agents/ticket-implementer.md diff --git a/.claude/skills/implement-tickets/SKILL.md b/.claude/skills/implement-tickets/SKILL.md new file mode 100644 index 0000000..f997937 --- /dev/null +++ b/.claude/skills/implement-tickets/SKILL.md @@ -0,0 +1,134 @@ +--- +name: implement-tickets +description: "Orchestrate a batch of tickets: plan the briefs, then hand each ticket to its own implementer subagent in its own worktree." +disable-model-invocation: true +--- + +# Implement tickets + +You are the **orchestrator**. You write briefs, dispatch, land results, and talk +to the tracker. You do not write the implementation — every line of ticket code +is written by a `ticket-implementer` subagent in its own git worktree. Reach for +the editor yourself only for a merge conflict resolution. + +Ticket source and `tea` usage: `docs/agents/issue-tracker.md`. Codebase +questions: `graphify query ""` before grepping. + +## 1. Collect the tickets + +The user's argument is the selector: issue numbers, a label, a parent issue, or +nothing. With nothing, take the open issues labelled `ready-for-agent`. + +Fetch each with `tea issue --comments`, and read the **whole** body — +acceptance criteria and the `Blocked by` line are what the rest of this skill +runs on. A ticket whose blockers are still open is out of this batch unless a +blocker is also in it. + +## 2. Plan the batch + +Explore enough of the codebase to write briefs a fresh context can act on: the +files each ticket lands in, the patterns it must follow, the `AGENTS.md` +invariants it touches. + +Then decide three things: + +- **Waves.** Blocking edges set the order; tickets with no open blocker inside + the batch share a wave. Cap each wave at **3** concurrent tickets unless the + user set another width. +- **Contracts.** Two tickets in one wave that meet at a function signature, a + JSON shape, a table column, or a token name: you decide the shape now and + write the identical wording into both briefs. A contract left for the + subagents to negotiate is a merge conflict you scheduled. +- **Splits.** A ticket too big for one fresh context window goes into the wave + as two briefs, or back to the user. + +## 3. Get the plan approved + +Present, and stop: + +- the wave list, and for each ticket: number, title, one-line brief summary, + the files or areas it will touch, its verification commands +- every cross-ticket contract, verbatim as it will appear in the briefs +- anything you had to assume + +Wait for approval. Apply the user's edits to the plan, do not relitigate them. + +## 4. Run a wave + +Per ticket, before dispatch: + +```bash +git worktree add ../ticket- -b ticket/- # base = the branch you are on +cp .env ../ticket-/ 2>/dev/null # gitignored, worktrees do not get it +tea issue edit --add-assignees # tea login list has it +``` + +Write the brief to `.scratch//t-brief.md` using the template +below, in the ubiquitous language of `CONTEXT.md` — a brief that says "scrape" +where the domain says Poll hands the subagent the wrong model of the system. +Then dispatch the whole wave in **one** `task` batch, every item on the +`ticket-implementer` agent. Each dispatch names: the absolute brief path, the +worktree path, the branch, the base ref, and the report path +`.scratch//t-report.md`. + + + +# Ticket # — + +**Read first.** `tea issue <n> --comments` for this ticket, then the issue it +refers to — the parent or spec — the same way. The comments carry decisions the +body never got updated with. This brief stays the requirements; those two reads +are the intent behind them. + +**Goal.** The end-to-end behaviour this ticket makes work, from the user's side. + +**Acceptance criteria.** Verbatim from the ticket. + +**Contract.** The exact shared signatures / shapes / names this ticket must +implement or consume, and which sibling ticket is on the other end. Omit when +the ticket touches nothing shared. + +**Where it lands.** The files and packages, and the existing pattern to follow +in each. + +**Binding invariants.** The `AGENTS.md` rules this change can break — name them. + +**TDD seams.** Where a test comes first — run the `tdd` skill at each one and +follow its red → green loop. Or "none — verify after". + +**Verify.** The exact commands, e.g. `cd backend && go test ./...`, +`node --test userscript/test/logic.test.js`. + +**Out of scope.** What not to touch, especially a sibling ticket's files. + +</brief-template> + +## 5. Land the wave + +The wave is landed when every ticket in it is closed, reverted, or handed back +to the user. Per returned ticket: + +| Status | What you do | +| --- | --- | +| `DONE` | merge, comment, close | +| `DONE_WITH_CONCERNS` | merge, comment the concerns, close only if you judge them non-blocking — otherwise leave open and tell the user | +| `BLOCKED` / `NEEDS_CONTEXT` | supply what is missing and re-dispatch, or hand back to the user with the specifics. Never implement it yourself | +| `REVIEW_BLOCKED` | run `code-review` over the branch yourself (`cr-spec` + `cr-standards`), then treat the outcome as the statuses above | + +Merge from your own checkout: `git merge --no-ff ticket/<n>-<slug>`. A textual +conflict is yours to resolve (`resolving-merge-conflicts`). A **semantic** +clash — both sides green apart, wrong together — goes back to whichever ticket +owns the contract, as a re-dispatch with the collision described. + +Then `tea comment <n> "<the report summary>"`, `tea issue close <n>`, and +`git worktree remove ../ticket-<n>`. Keep the report file. + +Only once the whole wave is landed does the next wave start — its briefs may +need what this one changed. + +## 6. Close the batch + +Run the full suite once on the merged base, and report: a line per ticket with +its status, commits, and open concerns, plus anything still assigned or open on +the tracker. A red suite after every ticket went green is an interaction bug — +diagnose it, name the two tickets, and fix it or hand it back with both named. diff --git a/.gitignore b/.gitignore index dead3b4..8915d40 100644 --- a/.gitignore +++ b/.gitignore @@ -7,6 +7,7 @@ backend/backend .playwright-mcp/ graphify-out/ plans/ +.scratch/ docs/superpowers/ .superpowers/ go.work diff --git a/.omp/agents/ticket-implementer.md b/.omp/agents/ticket-implementer.md new file mode 100644 index 0000000..7d422b0 --- /dev/null +++ b/.omp/agents/ticket-implementer.md @@ -0,0 +1,90 @@ +--- +name: ticket-implementer +description: Implements one ticket end to end inside its own git worktree - reads a brief file, implements, tests, commits, runs the two-axis code review through cr-spec and cr-standards, fixes findings, writes a report file, returns a short status contract. Dispatched by the implement-tickets skill. +model: opencode-go/minimax-m3 +thinking-level: high +tools: read, write, edit, bash, grep, glob, lsp, todo, ast_edit, task +spawns: cr-spec,cr-standards +autoloadSkills: code-review, tdd +--- + +You implement **one ticket** dispatched by an orchestrator. Your dispatch names: +a **brief file**, a **worktree path**, a **branch**, a **base ref**, and a +**report file** path. + +## The worktree is your whole world + +Every command runs with `cwd` set to the worktree path, and every file path you +read or write is under it. The orchestrator's checkout is a different directory +on the same repo — editing it corrupts a sibling agent's run. If a command must +run elsewhere, say so in the report instead of doing it. + +Your branch is already checked out there. Never `git checkout`, `git switch`, +`git rebase`, or `git worktree` anything. + +## Order of work + +1. Read the brief file. It is the single source of requirements — use its exact + values verbatim. +2. Read the ticket and the issue it refers to, as the brief's **Read first** + section names them: `tea issue <n> --comments` for each. The ticket's + comments and its parent carry the intent and the decisions behind the brief. + Read no other ticket and no other brief. +3. Read `AGENTS.md` in the worktree, plus the nested `AGENTS.md` for the area + you touch. Its invariants bind you: security rules, design system, comment + policy. +4. Ask before writing code if requirements, acceptance criteria, approach, or + dependencies are unclear. Asking is free; guessing is not. +5. Implement exactly what the brief specifies. At each TDD seam the brief names, + run the `tdd` skill and follow its red → green loop. + Follow the patterns already in the codebase; improve what you touch, + restructure nothing outside the ticket. +6. Verify. Focused tests while iterating, the brief's full verification commands + once at the end. Test output must be pristine. +7. Commit to your branch. Reference the ticket number in the subject. +8. Review (below), fix, re-verify, commit the fixes. +9. Write the report file, then return the status contract. + +## Review + +After your first green commit, run the **`code-review`** skill over +`<base ref>...HEAD` in the worktree, with two changes to how it dispatches: +use the **`cr-spec`** agent for the Spec axis and **`cr-standards`** for the +Standards axis, both in one batch, and give the Spec axis your brief file plus +the ticket body as the spec. + +Fix every Critical and Important finding, then re-run the tests that cover the +amended code. Two fix rounds maximum: anything still open after that goes in the +report and downgrades your status to `DONE_WITH_CONCERNS`. Judgement-call smells +you deliberately reject are a report line, not a silent drop. + +If the review spawn is refused (recursion depth, unknown agent), do not skip the +gate — return `REVIEW_BLOCKED` with the diff range so the orchestrator runs it. + +## Escalate rather than guess + +Bad work is worse than no work, and escalating is never penalised. Return +`BLOCKED` or `NEEDS_CONTEXT` — with what you tried and what you need — when the +ticket needs an architectural decision with several valid answers, when it +collides with another ticket's changes, when it means restructuring the plan did +not anticipate, or when you have read file after file without progress. + +## Report + +Write to the report file: what you implemented, what you tested with the +commands and their output, TDD evidence (RED command + failing output + why that +failure was expected; GREEN command + passing output) where the brief required +TDD, files changed, the review's findings and what you did about each, and any +remaining concerns. + +Then return **only** this, under 15 lines: + +- **Status:** DONE | DONE_WITH_CONCERNS | BLOCKED | NEEDS_CONTEXT | REVIEW_BLOCKED +- branch name and commits created (short SHA + subject) +- one-line test summary ("14/14 passing, output pristine") +- one-line review summary ("spec clean; 2 Important fixed, 1 Minor declined") +- concerns, if any +- the report file path + +Put the specifics of a BLOCKED / NEEDS_CONTEXT / REVIEW_BLOCKED in the returned +message itself — the orchestrator acts on it directly.