Skip to content
Back to skills

Implement Issue

ASecurity

Ticket playbook for claude-ui: how to plan, build, test and verify one GitHub issue (GH-<n>) of this repo in its own worktree: context to read, plan contents, test-first build, real-app verification against OpenCode, finish and commit. Use when asked to implement, build or fix a GH-<n> issue in claude-ui, and when an orchestrate-tickets worker reads this repo's playbook.

  • 10 stars
  • 0 votes
  • 0 copies
  • 0 views
  • Added October 10, 2026
developmentpythongobashexpressdebugginggitapi

Works with

  • cli
  • api

Security analysis

A100/100

Scanned November 10, 2026

npx -y skills add cuonghuunguyen/claude-code-ui --skill implement-issue --agent claude-code

Installs into .claude/skills of the current project.

Are you the author of Implement Issue?

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

Security grade badge for Implement Issue
[![Security: A — Skills Directory](https://www.skillsdirectory.com/api/skills/cuonghuunguyen-implement-issue/badge)](https://www.skillsdirectory.com/skills/cuonghuunguyen-implement-issue)

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
---
name: implement-issue
description: "Ticket playbook for claude-ui: how to plan, build, test and verify one GitHub issue (GH-<n>) of this repo in its own worktree: context to read, plan contents, test-first build, real-app verification against OpenCode, finish and commit. Use when asked to implement, build or fix a GH-<n> issue in claude-ui, and when an orchestrate-tickets worker reads this repo's playbook."
---
# Ticket playbook: claude-code-ui

This is the repo's playbook for the `orchestrate-tickets` skill (stages, models and the
RESULT contract live there), and the how-to for anyone implementing an issue by hand. One
issue, one worktree, one branch, one diff.

## Profile
tracker:     github-issues   (https://github.com/cuonghuunguyen/claude-code-ui/issues/<n>)
query:       --label ready-for-agent
states:      started → —, implemented → —, in-review → —
host:        github
push:        coordinator   (the pre-push data scan in CLAUDE.md still applies; push the issue branch only, never main)
merge:       auto   (see Merge; overrides orchestrate-tickets "never merge" for this repo)
remote-branch: same
ci:          none
base:        main
branch:      GH-<n>-<slug>   (a prefix added by a worktree tool is fine)
scratch:     <main checkout>/development-docs/GH-<n>/   ignored by: .gitignore
acceptance:  playwright-bdd

## Commands
install:     npm ci
test:        npm test        scoped: npx vitest run <files>
typecheck:   npm run typecheck
e2e:         E2E_PORT=<port> E2E_SCRATCH=<scratch>/e2e E2E_EVIDENCE_DIR=<scratch>/evidence npm run e2e -- -g "<Feature or scenario name>"
             (packages/e2e: Cucumber via playwright-bdd, real Claude on Haiku, own isolated daemon; see its README)
             `-g` is the filter: `npm run e2e -- <feature>` alone runs the whole suite (~30 min). Several features: `-g "A|B"`.
             Check the first log lines list only your scenarios. One run shares one daemon: a scenario that needs a
             clean setting (e.g. no Jira) sets it itself.
             steps: npm run e2e:steps   (lists every step, from `bddgen export`)
app:         mkdir -p <scratch>/ui-config; env -u ANTHROPIC_API_KEY -u ANTHROPIC_AUTH_TOKEN XDG_CONFIG_HOME=<scratch>/ui-config PORT=<port> nohup npm start > <scratch>/app.log 2>&1 & echo $! > <scratch>/app.pid
             (the pairing URL is in the log: #token=...)   stop: kill that PID's process tree
ports:       one per worker, assigned by the coordinator; never the user's own daemon port

## Plan skip rules
Also plan first when the issue changes the protocol or part model, or `docs/spec.md`.

## Plan: read before designing
- `gh issue view <n> --comments` (read-only), `README.md`, `docs/spec.md`, `CONTEXT.md` (use
  its terms), `docs/adr/` (binding).
- UI issues: `development-docs/opencode-parity/reference.md` (tokens, layout, anatomy) and the
  `gaps.md` rows mapped to the issue. OpenCode source: `development-docs/opencode-parity/src`;
  screenshots: `.../screenshots`.
- Before using a library or SDK API (`@anthropic-ai/claude-agent-sdk`, `@pierre/diffs`,
  AI Elements): current docs via context7. Check runtime behaviour with a throwaway probe when
  types are not enough (e.g. `getSessionMessages()` strips `tool_use_result`); delete it after.
- Trace the data flow end to end: daemon (`packages/daemon`) → adapter/parts
  (`packages/protocol`) → client store (`packages/web/src/store.ts`) → component. Prefer
  deriving UI state from parts on the client; change the protocol only when the data does not
  reach the client.

## Implement
- Scratch: the folder is shared by every worker on the issue (planner, implementer, tester,
  reviewer): prefix your files, delete only files you created. Never commit it; never remove
  `development-docs/` from `.gitignore`.
- Failing test first (tdd skill): pure logic in a `*.ts` module with a `*.test.ts`; components
  with `// @vitest-environment jsdom` + `createRoot` + `act` (see `tool-expand.test.tsx`,
  `changes-panel.test.tsx`); adapter changes in `packages/protocol/test/adapter.test.ts`
  against recorded fixtures.
- Reuse existing helpers (`diffStats`, `InputDiff`, `DIFF_OPTIONS`, `Button`, theme tokens)
  before writing new ones.
- UI: OpenCode tokens and anatomy; ui-ux-pro-max Quick Reference §1-§3 must hold (contrast,
  44px touch targets below `md`, focus, no horizontal scroll at 390px). Focused checks:
  `python3 .claude/skills/ui-ux-pro-max/scripts/search.py "<query>" --domain ux`.
- Update `docs/spec.md` when behaviour or the wire/part model changes.
- While building: the scoped command on the touched test files. Finish: `npm run typecheck`
  and one full `npm test` (record pass/fail/skip); commit the fix only as
  `GH-<n>: <what the user now sees>`; `git rebase main`, resolve, `npm ci`, re-run the
  typecheck and the scoped tests.
- A full-suite failure outside your diff: check the batch BASELINE, then re-run that file
  alone (see orchestrate-tickets › worker-specs). Known Windows load flakes: daemon git and
  server tests timing out, `AttachConsole failed`, `files-panel` breadcrumb, the orchestration
  `worker_wait`/permission timing tests. Never set up a base worktree just to compare.
- **Don't edit `CHANGELOG.md` on a ticket branch.** The PR title (`GH-<n>: <what the user now
  sees>`) is the changelog line; the release step writes `## Unreleased` from the merged PRs.
  Every parallel branch adding a bullet at the same spot conflicted on every merge.
- `notes.md`: design decisions, files, per acceptance criterion the evidence (test name,
  scenario name or screenshot), start command and port, manual steps for a UI tester,
  out-of-scope findings (report, do not fix).
- Do not merge into main, do not comment on GitHub, do not push (a stage worker never pushes; the coordinator pushes). Merging
  is the coordinator's job (Merge).

## Merge
The owner allows an automatic merge in this repo. It overrides orchestrate-tickets' "never
merge" (`SKILL.md` §3, `adapters/github.md` forbidden calls) for `gh pr merge` only; every
other forbidden call stays forbidden.

- Who: the root coordinator, or whoever runs the issue by hand. Never a stage worker.
- When, all of these:
  - the PR is open on `main`;
  - the latest review on the PR's head commit is green (no must-fix), whether a full review
    or a delta review after fixes;
  - acceptance is green on that commit, or only test-side commits came after it;
  - every acceptance criterion of the issue is met. A criterion the owner must check by hand
    (for example "user verification on Windows/WSL") holds the merge until the owner confirms it;
  - `gh pr view <n> --json mergeable` says `MERGEABLE`.
- How: `gh pr merge <n> --merge --delete-branch=false`. No squash or rebase: the history keeps
  merge commits. The `Closes #<n>` line in the PR body closes the issue.
- After, test on `main`: pull the merged `main` into a clean checkout (a worker in the main
  checkout or a fresh worktree on `origin/main`, never one with the owner's uncommitted edits),
  `npm ci`, `npm run typecheck`, one full `npm test`, and the issue's e2e scenarios with
  `-g "<name>"` of every merged issue. Judge failures against the batch baseline.
- Batch it: merge every PR that is ready at that moment, then run one test phase on `main`
  for all of them. A PR that becomes ready while the phase runs waits for the next phase.
- A new failure: stop further merges, find the merged PR that caused it (the failing test's
  files, else re-run it on `main` at each merge commit), and report the test name and that PR
  to the owner; the owner decides revert or fix-forward.
- Then record the merge and the `main` test result in the ledger. A worktree based on an older
  `main` rebases onto the new `main` before its next stage.
- Merge refused or not `MERGEABLE`: do not force it. Send the conflict to the implementer
  (rebase onto `main`, re-run scoped tests), then check again.
- Run `gh pr merge` as one plain command per call, no chaining. Read `mergeable` in a separate call (`UNKNOWN` right after a base PR merged: read again).

## Acceptance
Scripted: each visible acceptance criterion is a Cucumber scenario in `packages/e2e/features`,
run against a real daemon and real Claude (Commands › e2e). The run starts its own daemon on
`E2E_PORT` with `XDG_CONFIG_HOME=<run>/ui-config` and the API key variables unset, stops it by
PID, and fails if anything of the run landed in the real config dir.

1. Write or extend the `.feature` scenario for each visible acceptance criterion. Reuse steps
   (`npm run e2e:steps`); add a step only when none fits. Assert structure (reply shown, card
   shown, turn stopped), never reply wording. End each scenario with `Then I capture "<name>"`;
   cover 1440×900 and 390×844 (`scrollWidth` = viewport width) for layout criteria, and the
   daemon-restart step for the restored-transcript path.
2. Run it: Commands › e2e with `-g "<Feature name>"`.
3. Green: look at every captured screenshot in `<scratch>/evidence` and record them per
   criterion in `notes.md`. For OpenCode parity, put each next to its OpenCode screenshot
   (`<nn>-<what>-claudeui.png` / `-opencode.png`).
4. Red, or a new step is needed: re-run that one scenario in the background with
   `-g "<scenario>" --debug=cli`, wait for `Debugging Instructions`, then
   `playwright-cli attach tw-XXXX` and `playwright-cli -s=tw-XXXX pause-at <file>.steps.ts:<line>`
   (bare file name: a path never matches on Windows). Drive the page from the failing step,
   copy the printed Playwright code into the step or fix the app, stop the run by killing the
   runner's PID tree (`close` only detaches, and a debug run never times out), go to 2.
   Stop it with `npm run e2e:stop -- <absolute run folder>`: it kills the runner's tree by the PID in
   `<run>/runner.pid`, then any live daemon of that run (`daemon-w*.pid`, checked against the run
   folder in its command line). The run folder is printed as `e2e run folder:` at the top of the
   log (`<E2E_SCRATCH>/<stamp>-<pid>`). Never use `$!` from Git Bash (an MSYS PID, not a Windows
   one). Manual fallback only: `MSYS_NO_PATHCONV=1 taskkill /T /F /PID <pid from runner.pid>`.
5. Clean up: every background run stopped (`npm run e2e:stop -- <run>` for each run folder under
   `<scratch>/e2e`), `<scratch>/e2e` deleted. Evidence stays.

Hand-driving `playwright-cli -s=w<port> open` against Commands › app is only for a check a
scenario cannot express; say why in `notes.md`, and stop it by PID the same way.

Under the coordinator this loop is the stage-2 worker's: it edits and commits only
`packages/e2e/features`, `packages/e2e/steps` and `packages/e2e/support`, never the app; a
broken app is a failed case that goes back to the implementer.

## Traps
1. **Never `pkill -f` or `killall`.** Other workers run the same command line; kill by PID.
2. **Never start the app on the user's daemon port**, and unset `ANTHROPIC_API_KEY` /
   `ANTHROPIC_AUTH_TOKEN` (subscription login only, ADR 0002).
3. **`getSessionMessages()` strips `tool_use_result`.** Types are not enough for SDK
   behaviour; probe it.
4. **Never run a test app on the real config dir** (`%APPDATA%\claude-ui` on Windows,
   `~/.config/claude-ui` elsewhere): it adds test projects and sessions to the user's install.
   Always `XDG_CONFIG_HOME=<scratch>/ui-config` (the e2e run does this itself).

## Handoff
A stage role of the `orchestration` plugin (`orchestration:planner|implementer|ui-tester|reviewer`, skill `orchestration:orchestrate-tickets`) reads this playbook for its repo-specific steps. Its prompt is a HANDOFF block: `report-dir` is the scratch folder `development-docs/GH-<n>/` above; use the one port the handoff sender gave you.

## Without a coordinator
Working an issue by hand: run the stages yourself in order (plan when the skip rules in
`orchestrate-tickets` › `references/planning.md` fail, implement, acceptance), with the model
table in `CLAUDE.md` for any subagent you dispatch.

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.

View all in development →

Comments

Loading comments…

Newsletter

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