Drive the ticket skill through the live ticket page

This commit is contained in:
Gabriel Brown
2026-09-22 14:54:05 -04:00
parent fd24b69039
commit 88032490c0
2 changed files with 301 additions and 230 deletions
+156 -230
View File
@@ -1,14 +1,14 @@
---
name: ticket
description: End-to-end Jira ticket workflow — fetch a ticket into .claude/docs/epics/, write a plan and its plan.html review page, implement it with clean commits, drive the pre-mr-review audit to convergence (the user runs it themselves from their work Claude account), and write the MR doc. Use when the user gives you a Jira ticket key (e.g. KACP-11111) to work, or asks to plan/implement/wrap up a ticket.
description: End-to-end Jira ticket workflow — fetch a ticket into a live ticket page under .claude/docs/epics/, plan it, implement it with clean commits, drive the pre-mr-review audit to convergence (the user runs it themselves from their work Claude account), and write the MR doc. Use when the user gives you a Jira ticket key (e.g. KACP-11111) to work, or asks to plan/implement/wrap up a ticket.
disable-model-invocation: true
---
# Ticket Workflow
Turns a Jira ticket key into: a saved ticket record, a reviewed plan, implemented and
committed code, a converged `pre-mr-review` audit, and an MR doc ready to paste into
GitLab/GitHub. This skill is user-level (`~/.agents/skills/ticket/`) and works the same
Turns a Jira ticket key into: a live ticket page carrying the ticket, a reviewed plan,
progress, and proof, implemented and committed code, a converged `pre-mr-review` audit, and
an MR doc ready to paste into GitLab/GitHub. This skill is user-level (`~/.agents/skills/ticket/`) and works the same
way in any repo that has a `.claude/docs/` directory and a `pre-mr-review` skill.
Invoked as `/ticket KACP-11111`. The ticket key is passed as the skill's arguments. Called
@@ -19,38 +19,48 @@ Jira story, the estimate, risk level, and testing tables, as part of authoring a
before anyone builds it. It has nothing to do with reviewing code. The code review in
this skill is Phase 2 step 5 and is performed by a dispatched agent, not by a skill.
## The ticket page
Every ticket gets a **ticket page**, a web page Gib keeps open while you work: the ticket as
Jira has it, the plan with its mocks, live progress, proof beside the mocks, and the review.
Every epic gets an **epic index** listing its stories in build order. Both render from data
files that only `ticket-page` writes, and both update themselves as it writes.
**Read [SITE.md](SITE.md) before the first write of every run.** It holds the command for each
moment the phases below name, the plan's JSON shape, how to draw mocks, and how the epic order
is seeded. Record each moment when it happens, not in a batch at the end, because Gib is
watching the page change.
## Directory layout this skill maintains
```
.claude/docs/epics/
_site/ # the shared renderer, refreshed by ticket-page
<EPIC-KEY>/
epic.md # brief epic context, written once, best-effort
index.html, epic.data.js # the epic index
<TICKET-KEY>/
index.html, ticket.data.js # the ticket page
resources/
ticket.md # title, description, dev review instructions
issue.raw.json # the fetched issue, kept for re-imports
<attachments...> # images, videos, other files as downloaded
<video>.transcript.txt # only for videos that got transcribed
<video>.transcript.srt
plan.md
plan.html # the plan as a page, with mocks when a step changes what a user sees
deliverables/ # only for tickets whose output is documents, not code
<video>.frames/
deliverables/ # only for tickets whose output is documents, not code
<deliverable-slug>/
<deliverable-slug>.md
<deliverable-slug>.typ
<deliverable-slug>.pdf
proof/ # captured working feature proof, uploaded to Jira by the user
proof/ # captured working feature proof, uploaded to Jira by the user
mr.md # the MR description only, written once pre-mr-review says Ready to Open MR
tickets/
<TICKET-KEY>/ # same shape, for tickets with no epic
resources/...
plan.md
plan.html
deliverables/...
mr.md
<TICKET-KEY>/ # same shape, for tickets with no epic, and no index
```
## Scripts this skill uses
- `~/.agents/skills/ticket/scripts/ticket-page <command> <KEY> ...` — the only writer of the
ticket page and epic index. See [SITE.md](SITE.md), or `ticket-page help`. Runs under bun.
- `~/.agents/skills/ticket/scripts/jira-fetch-issue.sh <KEY> <OUT.json>` — fetches one
issue (`fields=*all`, rendered HTML description, field-name map) via `JIRA_BASE_URL` /
`JIRA_CREDENTIALS`.
@@ -101,8 +111,8 @@ rerun it, and verify the device log instead of accepting the fallback transcript
## House style (applies to everything this skill writes or commits)
- No code comments unless the user explicitly asks for one in that spot.
- No em dashes, en dashes, semicolons, or arrow glyphs in commit messages, `ticket.md`,
`plan.md`, or `mr.md`. Plain punctuation only.
- No em dashes, en dashes, semicolons, or arrow glyphs in commit messages, the ticket page,
or `mr.md`. Plain punctuation only.
- Commits are `git commit -m "<short imperative message>"` only — no body, no
co-author line, no `Claude` / `Codex` / `AI` trailer of any kind.
- Never `git push`. Never open an MR/PR. This skill prepares everything needed to open
@@ -161,19 +171,18 @@ the document.** Being aligned first is the point, and the writing should read as
you always were.
This governs `mr.md`, `customfield_10260` and `10261`, every other Jira field, and PM
deliverables. It does not govern files only he reads. `plan.md`, `bugs.md`, audits and
deliverables. It does not govern files only he reads. The ticket page, `bugs.md`, audits and
working notes can name him and record who decided what, because that history is useful.
The House style punctuation rule above is the stricter one where the two overlap. Keep
it. Personal working files (`plan.md`, `plan.html`, `ticket.md`) are exempt, nobody else
reads them.
it. The ticket page is a personal working file and exempt, nobody else reads it.
## Verification tools available
The goal isn't "a plausible-sounding plan" or "code that compiles" — it's a plan and
an implementation that are actually correct, checked against the real system rather
than assumed from the ticket text. Use whichever of these actually verify the thing
in question, before writing `plan.md` (Phase 1) and again whenever Phase 2's
in question, before writing the plan (Phase 1) and again whenever Phase 2's
implementation makes a claim that's checkable:
- **The codebase itself.** Don't take the ticket's description of "how things work
@@ -199,7 +208,7 @@ implementation makes a claim that's checkable:
about explicitly rather than assume the file's DB credential is fair game by default.
If something would need access you don't have (a different repo, a system you can't
reach), say so explicitly in `plan.md`'s Open questions instead of silently skipping
reach), say so explicitly in the plan's open questions instead of silently skipping
the check.
**How to write what you found, in a deliverable.** State it as a plain confirmed
@@ -213,7 +222,7 @@ codebase equally.
Some tickets (spikes, research, migration proposals, the KACP-22764 kind of ticket)
don't produce code, they produce documents for a PM or another engineer to read. When
a `plan.md` Step produces a document rather than a code change, it becomes its own
a plan step produces a document rather than a code change, it becomes its own
subfolder under `<target-dir>/deliverables/<deliverable-slug>/` with three files:
- `<slug>.md` — the source of truth for content. Write this first, get the content
@@ -228,11 +237,10 @@ subfolder under `<target-dir>/deliverables/<deliverable-slug>/` with three files
- `<slug>.pdf` — compiled from the `.typ` file.
**Write for the actual audience, not for yourself.** The reader is a PM or another
engineer who has never seen `.claude/docs/`, doesn't know what `plan.md` or
`ticket.md` are, and doesn't have this repo (or any other local repo) checked out.
Never reference `.claude/docs/` or any local file path in a deliverable's content.
Never say "see plan.md" or "see ticket.md", those are personal working files, not
things a reader can open. Refer to "this ticket" or a plain Jira link, not a local
engineer who has never seen `.claude/docs/` or the ticket page, and doesn't have this
repo (or any other local repo) checked out. Never reference `.claude/docs/` or any local
file path in a deliverable's content. Never point the reader at the ticket page or the
plan, those are personal working files, not things a reader can open. Refer to "this ticket" or a plain Jira link, not a local
path. If a fact came from another repo on this machine, state the fact, don't cite
the local checkout path it came from.
@@ -318,7 +326,7 @@ describe it only as a quick UI mockup.
When a ticket calls for mocks or datamodel changes (a spike proposing new screens or a
new schema, for instance), don't stop at wireframes or a schema proposal document.
Build it for real, on the ticket's branch. Mark these steps `(mock)` in `plan.md`.
Build it for real, on the ticket's branch. Give these steps `"kind": "mock"` in the plan.
A mock deliverable is **two artifacts**, not one, and they answer different questions.
The code proves the thing is buildable and surfaces the problems a picture hides. The
@@ -351,7 +359,7 @@ what was quick to wire up. Ship both, and don't let either stand in for the othe
feature. Don't chase every edge case and don't fix unrelated pre-existing bugs you
happen to notice along the way, just implement enough of the real thing to be
screenshotted and evaluated. If doing it for real would take significantly longer
than a static mock would, say so in `plan.md` rather than silently scoping it down
than a static mock would, say so in the plan's approach rather than silently scoping it down
without mentioning the tradeoff.
- **Screenshot the real thing.** Once it's built and running, use the `run` skill to
get the app up and a browser automation tool (e.g. `claude-in-chrome`) to capture
@@ -372,41 +380,6 @@ Reach for it when the spike's real open question is which of several directions
build. Once a direction is settled, the deliverable is the two artifacts above, and both
ship with the ticket.
## The plan page (`plan.html`)
`plan.md` is the plan. `plan.html` is the same plan as a page, saved beside it, so Gib
reads the plan in a browser before approving it instead of scrolling a markdown file. It
opens from `file://`, ticks acceptance criteria and steps as he reviews them, and shows
the mocks in place. It is the review artifact, and it exists to be opened.
- **Fill the template.** Copy `~/.agents/skills/ticket/templates/plan.html` to
`<target-dir>/plan.html` and fill every slot its header comment lists, in the order
`plan.md` carries them: the same criteria, the same open questions, the same Approach,
the same steps with the same `(mock)` and `(deliverable)` marks as badges, the same
risks, the same test plan. Fill the slots only. The CSS and script ship as they are, so
every plan page looks and behaves the same. Done when a reader of either file learns
nothing the other omits.
- **Mocks, when a step changes what a user sees.** A new screen, a changed layout, a new
control, or copy a user reads means the page keeps its Mocks section: one artboard per
screen state the plan introduces, drawn in plain HTML and CSS inside its own
`<template>`, with the app's real navigation and density around it rather than a
wireframe of boxes. When the direction is Gib's call, draw each candidate as its own
artboard (Option A, Option B) and name the choice in Open questions, pointing at the
artboards. When the direction is settled, one artboard per screen. Each artboard's
`data-note` states what it shows that the implementation will not do. This is how the
house rule about several static mocks before touching real components is satisfied
inside the ticket flow, so no separate mock server is needed. When no step changes
what a user sees, delete the Mocks section and its nav link.
- **Open it.** After saving, run `xdg-open <target-dir>/plan.html` so it lands in the
browser, and give both paths in the Phase 1 wrap-up.
- **`plan.md` stays the source of truth.** When Phase 0 routes to rewriting the plan,
rewrite `plan.html` with it. Phase 2 checks steps off in `plan.md` only. The page is a
snapshot for the review and is left alone once the plan is approved.
- **Relation to spike mocks.** The plan page's artboards are design intent drawn before
any code exists. A deliverable's polished `mocks/` (see Mocks and datamodel changes)
can start from the approved artboard, and the built screens are still screenshotted
for real.
---
## Branch setup — run this before any repo work in Phase 1 or Phase 2
@@ -450,7 +423,7 @@ through, and the branch needs to already exist when that happens.
Once this resolves you're on the correct branch, and Phase 1/Phase 2 work happens
there. `.claude/docs/` itself is gitignored in this repo, so which branch you're on
doesn't affect `ticket.md`/`plan.md`/`mr.md` directly — this is about making sure any
doesn't affect the ticket page or `mr.md` directly — this is about making sure any
actual code (mockup prototypes, spike code, real implementation) lands in the right
place from the start.
@@ -460,21 +433,23 @@ place from the start.
correct key.
2. Look for an existing directory for this ticket without hitting the network:
`find .claude/docs/epics -mindepth 2 -maxdepth 2 -type d -name "<KEY>"`
(this matches both `epics/<EPIC>/<KEY>` and `epics/tickets/<KEY>`).
(this matches both `epics/<EPIC>/<KEY>` and `epics/tickets/<KEY>`). If it holds a
`plan.md` but no `ticket.data.js`, it predates the ticket page: convert it first, as
[SITE.md](SITE.md) describes under **Tickets from before the page**, then continue.
`ticket-page show <KEY>` says where the ticket stands.
3. Branch on what you find:
- **Nothing found** — this is a fresh ticket. Go to **Phase 1**.
- **Directory exists, no `plan.md`** — a previous run was interrupted before writing
a plan. Go to **Phase 1** and regenerate from scratch (re-fetch, overwrite
`ticket.md`, re-check attachments); it's idempotent and cheap.
- **`plan.md` exists, no `mr.md`** — ask the user (AskUserQuestion) what they want:
- Re-fetch the ticket and rewrite the plan (`plan.md` and `plan.html`) from scratch
(they want to start over)
- Proceed to implementing the existing `plan.md` as-is (they reviewed and approved it)
- Resume implementation (some plan steps are already checked off / some commits
already exist on the ticket branch — pick up from the first unchecked step)
- **A page with no plan** — a previous run was interrupted before writing a plan. Go
to **Phase 1** and regenerate from scratch (re-fetch, re-import, re-check
attachments); it's idempotent and cheap.
- **A plan, no `mr.md`** — ask the user (AskUserQuestion) what they want:
- Re-fetch the ticket and rewrite the plan from scratch (they want to start over)
- Proceed to implementing the existing plan as-is (they reviewed and approved it)
- Resume implementation (some steps are already done on the page / some commits
already exist on the ticket branch — pick up from the first open step)
- Move to the pre-mr-review stage (implementation looks done, just need the audit + MR doc; the user runs the audit from their work account, see Phase 2 step 7)
Route to **Phase 1** or **Phase 2** accordingly.
- **Both `plan.md` and `mr.md` exist** — this ticket looks finished. Tell the user
- **A plan and `mr.md`** — this ticket looks finished. Tell the user
`mr.md` already exists at its path and ask whether they want you to refresh it
(e.g. they made more changes since) or leave it alone. Only re-enter Phase 2's
verification/pre-mr-review loop if they say the code changed since `mr.md` was
@@ -485,83 +460,39 @@ place from the start.
## Phase 1 — fetch, scaffold, and plan
1. Do **Branch setup** above first, unconditionally.
2. Run `jira-fetch-issue.sh <KEY> <tmp-path>` (a scratch path is fine here — you don't
yet know the final directory). Read the resulting JSON.
3. Determine the epic: `fields.parent.key`, if present. If absent, this ticket has no
epic.
4. Resolve the target directory:
- With epic: `.claude/docs/epics/<EPIC-KEY>/<KEY>/`
- Without epic: `.claude/docs/epics/tickets/<KEY>/`
Create it and its `resources/` subdirectory.
5. Move/copy the fetched JSON to `<target-dir>/resources/issue.raw.json` for your own
reference while writing `ticket.md` — this raw file is scratch, not part of the
deliverable; feel free to leave it (it's harmless context for later) or delete it
once `ticket.md` is written, your call.
6. If there's an epic and `.claude/docs/epics/<EPIC-KEY>/epic.md` doesn't already exist:
best-effort fetch the epic issue too (`jira-fetch-issue.sh <EPIC-KEY> <tmp>`) and
write a short `epic.md` (title + description, converted to markdown, a couple
paragraphs at most). If this fetch fails for any reason, skip it and continue — it's
context, not a blocker.
7. Write `resources/ticket.md` by reading the fetched issue JSON yourself:
- Title, type, status, priority, assignee, epic key (or "None"), and a link
(`<JIRA_BASE_URL>/browse/<KEY>`).
- Description: convert `renderedFields.description` (HTML) to clean markdown. If
empty, say so.
- Developer review instructions: the field name varies by project and isn't a fixed
custom field ID. Look at the `names` map in the response for any field whose name
matches something like "dev review instructions" / "review instructions"
(case-insensitive substring match), then render that field's value the same way as
the description. If nothing matches, write "None provided" — don't guess a field.
- Test cases: same approach for any field whose name matches something like
"test cases" / "test table" / "working feature proof" / "qa" (case-insensitive
substring match), e.g. KACP's "Test Cases & Working Feature Proof" field. This
is part of the ticket's testing section and MUST be captured into `ticket.md`
whenever it holds anything beyond an empty template. Often it's just the bare
instruction panel and empty table, note that and move on. But when it has real
content, that content is part of the implementation and has to be known from
the start: render every row verbatim as a markdown table under its own
`## Test cases and working feature proof` heading, keeping the ticket's own
column headers, and carry any prose or instructions in the field alongside the
table. These are the scenarios the developer must prove, so Phase 1's plan must
already say how each row gets proven, Phase 2's verification must actually
prove each row, and Phase 2 step 9 fills the proof column back into the Jira
field itself. If nothing matches, write "None provided".
- Risk mitigation: same approach for any field whose name matches something like
"risk mitigation" / "risk management" / "risks" (case-insensitive substring
match), e.g. KACP's "Risk Mitigation" field. Same rule as test cases: an empty
template gets noted, real content gets captured in full because it shapes the
implementation from the start. Render it as a markdown table under its own
`## Risk mitigation` heading, keeping the ticket's own column headers and
every row verbatim. The PM and Lead Developer write the risks and mitigation
strategies; implementing those strategies and proving each one is the developer's
job, so every row needs a real mitigation in the code and its proof filled into
the Jira field in Phase 2 step 9. If nothing matches, write "None provided".
- Developer fill-in tables, in general: any ticket table with a column the developer
is meant to complete (Working Feature Proof, Mitigation Proof, and the like) is
copied into `ticket.md` in full, with that column left showing where its answer
goes rather than dropped. `ticket.md` is the record of what the ticket actually
asks for. Never substitute your own invented test cases or risks for the ticket's,
in `ticket.md` or in the Jira fields. Your own additional cases are welcome, but
they go alongside the ticket's rows, clearly marked as additional, never in place
of them.
- Sweep for anything else populated: list every key in the `names` map whose field
actually has a non-null, non-empty value on this issue (jq over `.fields` joined
with `.names`), and skim any populated field not already captured above. Capture
the ones relevant to implementing or verifying the ticket; ignore workflow
plumbing (ranks, sprints boards, reviewer assignments, rich-field duplicates).
This exists because real content sometimes hides in per-project custom fields
with unpredictable names — a name-pattern miss must not silently drop content.
- Any other fields on the ticket that look clearly relevant to implementing it
(acceptance criteria field, story points, labels, components) are worth a short
line each; don't dump every custom field verbatim.
8. Run `jira-download-attachments.sh <issue.raw.json> <target-dir>/resources/`. For each
2. Run `jira-fetch-issue.sh <KEY> <tmp-path>` (a scratch path is fine here — the next
step knows the final directory). Read the resulting JSON.
3. Import it: `ticket-page init <KEY> --issue <tmp-path>`. This files the ticket under its
epic (or `tickets/` without one), imports every rich-text field verbatim as sanitized
HTML, writes the page, and prints the ticket folder. Copy the fetched JSON to
`<target-dir>/resources/issue.raw.json`, which later re-imports and the attachment
script read. Record the branch from **Branch setup** with `ticket-page meta`, then open
the page with `ticket-page open <KEY>` so Gib can follow from here on.
4. `init` ends by listing populated Jira fields the page does not show. Read each one and
add any that bear on implementing or verifying the ticket as a note. This sweep exists
because real content sometimes hides in per-project custom fields with unpredictable
names, and a name-pattern miss must not silently drop it.
5. Read what the Ticket tab now holds, and treat its tables as the ticket's contract.
- **Test cases** (KACP's "Test Cases & Working Feature Proof") and **risk mitigation**
are imported with every row verbatim. Often a field is just the bare instruction
panel and an empty table, which needs no further thought. When it has real rows,
those are the scenarios the developer must prove and the mitigations the developer
must implement: the plan names each one (`"fromTicket": true`), Phase 2 proves each
one, and Phase 2 step 9 fills its proof column back into the Jira field. The PM and
Lead write the risks. Implementing and proving each is the developer's job.
- Never substitute your own invented test cases or risks for the ticket's, in the plan
or in the Jira fields. Your own additional cases are welcome alongside the ticket's
rows, never in place of them.
6. If the ticket has an epic, seed or refresh its epic index as [SITE.md](SITE.md)
describes under **The epic index**. This is best-effort context: if the epic search
fails, say so and continue.
7. Run `jira-download-attachments.sh <issue.raw.json> <target-dir>/resources/`. For each
attachment with a `video/*` mime type, check whether another attachment already looks
like its transcript (same base filename with `.txt`/`.srt`/`.vtt`, or a filename
containing "transcript"). Collect any videos with no matching transcript.
9. If there are untranscribed videos, ask the user (AskUserQuestion, one question,
8. If there are untranscribed videos, ask the user (AskUserQuestion, one question,
multiSelect if more than one video) whether to transcribe them now. For each they
approve, run `transcribe.sh <video-path> <target-dir>/resources/`. Note in `ticket.md`
under Attachments which videos have a transcript and which were skipped.
approve, run `transcribe.sh <video-path> <target-dir>/resources/`.
For every video that gets transcribed, also extract frames — ticket videos are
almost always screen recordings, and the transcript alone misses what was on
screen (the UI being pointed at, the annotation, the row that's wrong). Extract to
@@ -574,6 +505,10 @@ place from the start.
and treat what's visible on screen as part of the ticket's content the same way
the transcript is. Skip frame extraction only when the video is confirmed
audio-only or the user says the visuals don't matter.
9. Run `ticket-page scan <KEY>` so the page carries the attachments, each video with its
transcript and frames. When a video, spreadsheet, or linked document holds the
ticket's real requirements, write what it says as a note (`ticket-page note`), the
way "What the video says" summarizes a recording point by point.
10. Now actually understand the ticket in the context of this codebase: read whatever
source files, tests, or docs are relevant to what's being asked. Use Explore/grep
as needed — this is normal engineering research, not scripted. Use the
@@ -588,54 +523,33 @@ place from the start.
**architectural** is a new subsystem, a schema change other stories depend on, or
anything that alters an interface neighbouring work relies on. Bounded is the common
case and goes straight to step 11. For architectural, call the Skill tool with
"grilling" and resolve its frontier with Gib BEFORE writing `plan.md` — those
"grilling" and resolve its frontier with Gib BEFORE writing the plan — those
questions get answered in conversation, not deferred into the plan's Open questions
section, because an architectural plan built on a wrong assumption is the expensive
one to discover in Phase 2. Bounded measures the repo, not your familiarity: if the
flow being changed isn't already there to read, it isn't bounded. In doubt, take the
heavier path.
11. Write `plan.md` in `<target-dir>/plan.md` (a sibling of `resources/`, not inside
it). Structure:
```
# Plan: <KEY> — <short title>
heavier path. Either way, record the classification with `ticket-page meta`.
11. Write the plan as `plan.json` in a scratch path, in the shape [SITE.md](SITE.md) gives,
and load it with `ticket-page plan <KEY> --file plan.json`. Its parts: acceptance
criteria restated precisely from the ticket, each naming the steps that satisfy it;
open questions that genuinely need Gib's answer, empty if there really aren't any
(don't manufacture questions to look thorough); the approach, meaning what you're
going to do and why, which files, modules and functions are involved, and how it fits
the existing patterns; decisions already settled with Gib; steps, each roughly one
commit's worth of work, with `"kind": "deliverable"` for a step that produces a
document via **Deliverable documents** and `"kind": "mock"` for a real schema or UI
change built to demonstrate a mock (see **Mocks and datamodel changes**); risks and
edge cases, each with how the plan handles it; and the test plan, meaning how each
criterion gets verified, the existing suites to run, and any new tests.
## Acceptance criteria
- [ ] <criterion from the ticket, restated precisely>
...
## Open questions
<Anything genuinely blocking or ambiguous that needs the user's answer before or
during implementation. "None" if there really aren't any — don't manufacture
questions to look thorough.>
## Approach
<Narrative: what you're going to do and why, which files/modules/functions are
involved, how it fits the existing patterns in this codebase.>
## Steps
- [ ] <step 1, roughly one commit's worth of work — mark it `(deliverable)` if it
produces a document via the Deliverable documents process instead of code, or
`(mock)` if it's a real schema/UI change built to demonstrate a mock rather than
ship a finished feature, see Mocks and datamodel changes>
- [ ] <step 2>
...
## Risks and edge cases considered
- <edge case> — <how the plan handles it>
...
## Test plan
- <how you'll verify each acceptance criterion, including existing test suites to
run and any new tests to add>
```
Be thorough: address every acceptance criterion, resolve as many open questions as
you reasonably can by reading the code first, and don't leave logic gaps. Steps
should be concrete enough that Phase 2 can execute them without re-deriving the
approach. The Test plan must name every test case `ticket.md` captured and say how
that exact case gets proven, and the Risks section must name every risk
`ticket.md` captured and say which code enforces its mitigation. Read the notes
column of a ticket test case as part of the case, not as commentary: a note like
"confirm the certificate matches the emailed version" is its own thing to prove.
approach. The test plan must name every test case on the Ticket tab and say how
that exact case gets proven, and the risks must name every risk on the Ticket tab
and say which code enforces its mitigation. Read the notes column of a ticket test
case as part of the case, not as commentary: a note like "confirm the certificate
matches the emailed version" is its own thing to prove.
**Where the shape is in question, borrow the vocabulary.** When the plan has to
decide how deep a module should be, where a seam belongs, or what an interface
@@ -645,7 +559,7 @@ place from the start.
written for real.
**The Test plan is a design decision, not a formality.** Beyond the cases
`ticket.md` already captured, call the Skill tool with "tdd" to decide which
the ticket already carries, call the Skill tool with "tdd" to decide which
additional tests are worth writing, where the seam under test goes, and whether a
mock is warranted. That judgment is what keeps this section focused instead of
padded: a test plan listing one case per changed file is slop, and three tests that
@@ -662,13 +576,14 @@ place from the start.
step stays green because the old form still exists until the last one. Say in the
Approach that this is what you're doing and why, since the step count looks inflated
otherwise.
12. Write `plan.html` beside it, per **The plan page** above, and open it with
`xdg-open`.
13. Stop here. Tell the user `plan.md` and `plan.html` are ready at their paths and that
the page is open in the browser, summarize the approach in a couple of sentences, and
mention any open questions that need their input before you'd implement it. Do not
start implementing in this same run — wait for them to review the plan (editing
`plan.md` directly if they want) and invoke `/ticket <KEY>` again.
12. When a step changes what a user sees, draw its mock artboards with `ticket-page mock`,
as [SITE.md](SITE.md) describes under **Mocks**.
13. Stop here. Tell the user the plan is on the ticket page, give its path from
`ticket-page path <KEY>`, summarize the approach in a couple of sentences, and mention
any open questions that need their input before you'd implement it. Do not start
implementing in this same run — wait for them to review the plan and invoke
`/ticket <KEY>` again. They change the plan by asking, and you rewrite it with
`ticket-page plan`.
## Phase 2 — implement, verify, and hand off
@@ -677,14 +592,16 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
1. Do **Branch setup** above first, unconditionally — even if Phase 1 already did this
for the same ticket earlier, confirm you're still actually on that branch now (the
user may have switched branches between runs).
2. Read `plan.md` fresh — the user may have hand-edited it.
3. Work through `plan.md`'s Steps checklist in order. For each step: implement it (a
step marked `(deliverable)` follows the **Deliverable documents** process above
instead of writing code; a step marked `(mock)` follows **Mocks and datamodel
changes** — real schema/UI work, not a static wireframe), then check it off
(`- [x]`) in `plan.md`, then make one commit for it (or a few, if the step
naturally splits into independent units). Commit messages are short and imperative,
describing what changed, following House style above.
2. Read the plan fresh with `ticket-page md <KEY>`, then mark it approved with
`ticket-page approve <KEY>`.
3. Work through the plan's steps in order. For each step: implement it (a
`deliverable` step follows the **Deliverable documents** process above instead of
writing code; a `mock` step follows **Mocks and datamodel changes** — real schema/UI work, not a static wireframe), and make one commit for it (or
a few, if the step naturally splits into independent units). Commit messages are short
and imperative, describing what changed, following House style above. Mark the step
running on the page when you start it and done with its commits when they exist, and
mark each acceptance criterion met the moment it is (SITE.md has the commands). A step
is done when `ticket-page show <KEY>` lists its commit.
**Write the test first for steps that carry real logic**, meaning a router
procedure, a derivation, a permission or authorization check, a migration, or a
@@ -696,11 +613,13 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
House style rejects. When unsure whether a step qualifies, ask whether the test could
ever fail for a reason worth knowing about. If it couldn't, skip it.
If a step fails in a way `plan.md` didn't predict, and the cause isn't obvious within
If a step fails in a way the plan didn't predict, and the cause isn't obvious within
a couple of minutes, call the Skill tool with "diagnosing-bugs" rather than trying
fixes to see what sticks. Note anything it turns up that changes the plan.
fixes to see what sticks. Note anything it turns up that changes the plan as an event on
the page. When it changes the steps, amend the plan as [SITE.md](SITE.md) describes, which
keeps the progress already made.
4. Do not run the full verification suite after every commit (checks-at-the-end mode).
Once every step is implemented and checked off, find and run this project's standard
Once every step is implemented and marked done, find and run this project's standard
verification (check `package.json` scripts for something like `ci:check`,
`typecheck`, `lint`, `test`, or fall back to whatever the repo's README documents).
Fix anything broken, committing fixes as their own commits, until it's clean. If you
@@ -736,8 +655,9 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
change cannot provide it.
- **Give the reviewer its reading order explicitly**, or it produces noise about
conventions the project already settled: the epic summary if there is one
(`.claude/docs/epics/<EPIC-KEY>/summary.md`), then this ticket's `resources/` and
`plan.md`, then `AGENTS.md`, and only then the diff. Give it the exact commit range.
(`.claude/docs/epics/<EPIC-KEY>/summary.md`), then the ticket and plan as
`ticket-page md <KEY>` prints them, then this ticket's `resources/`, then
`AGENTS.md`, and only then the diff. Give it the exact commit range.
- **Tell it to verify rather than suspect**, to try to refute its own findings before
reporting them, to mark each finding CONFIRMED or PLAUSIBLE, and to give exact
`file:line` plus a concrete failure scenario. Tell it explicitly not to write,
@@ -748,12 +668,13 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
from checking.
- **Ask the spec questions explicitly**, or the review only finds bugs in what you
did write and never notices what you didn't. Three questions, each answered against
`ticket.md` and `plan.md` rather than the diff alone: what the ticket asked for that
the ticket and the plan rather than the diff alone: what the ticket asked for that
is missing or only partly done; what behavior is in the diff that nothing asked for;
and what looks implemented but is implemented wrongly. Have it quote the AC ID or
the `plan.md` line each finding traces to.
the plan step each finding traces to.
Then respond to the review, in writing:
Then respond to the review, in writing, and put each finding on the page as you judge
it: added, then fixed with its commit or rejected with the reason.
- **Findings are not automatically true.** Verify each one against the codebase
before acting on it. Reviewers do produce confident wrong answers. Say plainly
@@ -785,7 +706,7 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
skill prints the exact paths). Check the `last_reviewed_head` in the audit
header matches the current HEAD; if the branch moved after their run, say so
and ask them to rerun before acting on a stale audit.
3. Read the verdict.
3. Read the verdict, and record it on the page with `ticket-page verdict`.
- **Ready to Open MR**: continue to step 8.
- **Almost Ready / Not Ready Yet**: fix what it flagged (each meaningful fix
as its own commit), then ask the user to rerun the review from the work
@@ -811,8 +732,8 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
Make each one count:
- **Feed it context on the first run.** It reviews the diff, and left alone it
re-derives intent from the branch name and re-raises decisions that are
already settled. Ask the user to point it at the ticket's
`resources/ticket.md` and `plan.md`. This is the cheapest thing that reduces
already settled. Ask the user to point it at the ticket's `ticket.data.js`, or give
them the output of `ticket-page md <KEY>` to paste in. This is the cheapest thing that reduces
repeat findings.
- **Answer its decisions explicitly in the rerun.** When it parks a finding
under "Decide before opening", write the decision and its reasoning into the
@@ -836,7 +757,7 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
- New ENVs: list any env vars actually introduced; "None" if none.
- Additional Notes: a bulleted checklist of the changes you actually made, not a
dumping ground. Between Summary and this section, every acceptance criterion from
`plan.md` (or, for a bug ticket, the bug itself) needs to be directly addressed,
the plan (or, for a bug ticket, the bug itself) needs to be directly addressed,
not just gestured at. If there's genuinely unrelated work worth mentioning (e.g.
an incidental fix along the way), it goes after everything ticket-relevant, still
as short bullets. Five bullet points max total, ticket-relevant and incidental
@@ -876,7 +797,8 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
(`customfield_10260`, "For The Engineer: Patch Notes"). Step 9 writes it.
Scattered coverage across notes does not count as having answered a named
requirement, and neither does burying it in the MR description.
Save this as `<target-dir>/mr.md` (next to `plan.md`, not under `resources/`).
Save this as `<target-dir>/mr.md` (next to `index.html`, not under `resources/`), and
record it with `ticket-page mr <KEY>`.
**`mr.md` is the template plus the pasted handoff, and NOTHING ELSE.** This is a
hard shape, not a starting point. Its only headings are the template's own numbered
@@ -897,7 +819,8 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
9. Fill the Jira ticket fields directly, by issue type. Rich text fields are ADF:
render markdown with `python3 ~/.agents/skills/review-ticket/scripts/review2adf.py
render <file.md>` and PUT via `{"fields": {...}}` to `/rest/api/3/issue/<KEY>`.
After every PUT, re-fetch and verify the field landed (compare extracted text,
After every PUT, re-fetch and verify the field landed, then record it with
`ticket-page jira <KEY> "<field name>"` (compare extracted text,
treating empty `attrs` objects and `localId`/`colwidth`/`width` as noise). The
field-to-type map below is verified against the project's edit screens, don't PUT
a field to a type that doesn't carry it.
@@ -910,7 +833,8 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
yourself wherever possible: run the app (`run` skill) and screenshot the real
feature with browser automation, or capture test output for behavior with no UI.
Save artifacts under `<target-dir>/proof/` with names that match the test-case
rows they prove. A proof cell references its artifact by filename plus "attached"
rows they prove, and put each on the page with `ticket-page proof`, naming the
criteria it proves and the mock it answers. A proof cell references its artifact by filename plus "attached"
(Gib uploads every attachment, this skill NEVER uploads files to Jira) or names
the passing test. When proof genuinely can't be captured here (needs prod, a real
inbox, a flag flip), the cell says exactly what's needed and step 10 lists it for
@@ -920,7 +844,8 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
**Cap the proof at five files, and aim for two or three.** Gib uploads every
attachment by hand, so each file has a real cost and a wall of near-duplicate
screenshots buries the two that matter. Before filling the proof column, pick the
smallest set that actually carries the evidence and delete the rest from `proof/`.
smallest set that actually carries the evidence, delete the rest from `proof/`, and
drop them from the page with `ticket-page unproof`.
- One screenshot can prove several rows at once. A company page that shows the
navigation, the tabs, and the absent section proves three criteria in one image,
@@ -976,12 +901,13 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
ticket, Drive, or both, per the ticket's instructions). Fill `customfield_10260`
only if the spike actually shipped code.
Before moving on, re-read `ticket.md` row by row: every test case and every risk
the ticket carries now has its developer column filled in Jira. Leave none
Before moving on, re-read the Ticket tab's test case and risk tables row by row: every
test case and every risk the ticket carries now has its developer column filled in
Jira. Leave none
unanswered.
10. Tell the user: implementation is committed on `<branch>`, `pre-mr-review` verdict is
Ready to Open MR, the Jira fields are filled (name which), and `mr.md` is ready at
its path. Then the manual list, which should only ever be:
Ready to Open MR, the Jira fields are filled (name which), `mr.md` is ready at its
path, and the ticket page shows the finished work beside its mocks. Then the manual list, which should only ever be:
- push the branch, open the MR, and paste `mr.md` in as the description
- upload the named proof or deliverable files (from `proof/` or `deliverables/`)
to the ticket, plus anything flagged as proof only Gib can capture
@@ -989,8 +915,8 @@ Entered only when the user has confirmed (per Phase 0) that the plan is approved
## Resuming mid-implementation
If Phase 0 routes here because some steps in `plan.md` are already checked off: check
`git log` on the current branch against the plan's steps to sanity-check they actually
If Phase 0 routes here because some steps are already done on the page: check
`git log` on the current branch against the page's step commits to sanity-check they actually
match reality (a checked-off step should have a corresponding commit), then continue
from the first unchecked step. If the plan and the git history disagree, stop and ask
from the first open step. If the plan and the git history disagree, stop and ask
the user rather than guessing which one is right.