Drive the ticket skill through the live ticket page
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
# The ticket page
|
||||
|
||||
Every ticket this skill works has a **ticket page**, and every epic has an **epic index**. The
|
||||
page is where Gib follows the work: the ticket as Jira has it, the plan, live progress, proof
|
||||
beside the mocks, and the review. It updates while you work, so a write you skip is a gap he
|
||||
sees. It is a personal working file, exempt from the prose bar like the rest of `.claude/docs/`.
|
||||
|
||||
```
|
||||
.claude/docs/epics/
|
||||
_site/site.css, site.js # shared renderer, refreshed from templates/site/ on every write
|
||||
<EPIC-KEY>/
|
||||
index.html, epic.data.js # the epic index, in build order
|
||||
<TICKET-KEY>/
|
||||
index.html, ticket.data.js # the ticket page
|
||||
resources/ proof/ deliverables/ mr.md
|
||||
tickets/<TICKET-KEY>/ # same shape, for a ticket with no epic, and no index
|
||||
```
|
||||
|
||||
## Write through `ticket-page`, always
|
||||
|
||||
`~/.agents/skills/ticket/scripts/ticket-page` is the only writer of the data files. It validates
|
||||
before every write and refuses a broken one, writes atomically so the open page never reads half
|
||||
a file, appends each change to the page's live log, and keeps the ticket's row on the epic index
|
||||
current. Editing a `.data.js` file by hand skips all four. `ticket-page help` lists every
|
||||
command and its flags.
|
||||
|
||||
It finds the ticket by key under the current repo's `.claude/docs/epics/`, so run it from inside
|
||||
the repo.
|
||||
|
||||
## What to record, and when
|
||||
|
||||
| Moment | Command |
|
||||
|---|---|
|
||||
| Issue fetched to a scratch path | `init <KEY> --issue <issue.raw.json>`. Prints the ticket folder, then any populated Jira fields the page does not show. |
|
||||
| Attachments downloaded, a video transcribed, frames extracted | `scan <KEY>` |
|
||||
| Ticket branch resolved, ticket classified | `meta <KEY> --branch <name> --classified bounded` |
|
||||
| Research notes worth keeping: what a video says, what a spreadsheet holds, a check against prod | `note <KEY> "<Title>" --file notes.html` |
|
||||
| Plan written | `plan <KEY> --file plan.json`, then one `mock` per artboard |
|
||||
| Plan approved, Phase 2 begins | `approve <KEY>` |
|
||||
| Starting a step | `step <KEY> <n> running` |
|
||||
| A step's commit exists | `step <KEY> <n> done --commit <sha>`, one `--commit` per commit |
|
||||
| A criterion is met | `criterion <KEY> <ID> done --evidence "<what shows it>"` |
|
||||
| Proof captured, or dropped from `proof/` | `proof <KEY> --file proof/<name> --proves AC1,AC3 --caption "<test case>" [--mock <id>]`, `proof <KEY> --test "<name>" ...`, `unproof <KEY> <file>` |
|
||||
| Reviewer finding, and its resolution | `finding <KEY> add "<text>" --severity CONFIRMED --where <file:line>`, then `finding <KEY> F1 fixed --commit <sha>` or `rejected --reason "<why>"` |
|
||||
| pre-mr-review audit read | `verdict <KEY> <verdict text> --recommendation <R> --head <sha>` |
|
||||
| A Jira field PUT and verified | `jira <KEY> "<field name>"` |
|
||||
| `mr.md` written | `mr <KEY>` |
|
||||
| Anything else Gib would want to see happen: suite green, a surprise, a blocker | `event <KEY> "<text>"` |
|
||||
|
||||
A step is done when its commit exists and `ticket-page show <KEY>` lists that commit beside it.
|
||||
`step done` refuses without `--commit`. A step with nothing to commit, such as a verification
|
||||
pass, takes `--no-commit`.
|
||||
|
||||
## `plan.json`
|
||||
|
||||
The plan is one JSON file, written with the Write tool and loaded with `ticket-page plan`. Prose
|
||||
fields are HTML fragments: `<p>`, `<b>`, `<code>`, `<ul><li>`. `plan` replaces any earlier plan
|
||||
and clears its progress and proof, because a rewritten plan is a restart. To change a plan
|
||||
mid-build (a new step, a reworded criterion), print it with `plan-json <KEY> > plan.json`, edit
|
||||
that, and load it with `plan --amend`: steps whose number and text are unchanged, and
|
||||
criteria whose id remains, keep their progress, and proof keeps the criteria that still exist.
|
||||
|
||||
```json
|
||||
{
|
||||
"criteria": [
|
||||
{ "id": "AC1", "text": "Every company can carry a five character canonical ID.", "steps": [1, 2] },
|
||||
{ "id": "AC2", "text": "A duplicate canonical ID is rejected with a readable message.", "steps": [3] }
|
||||
],
|
||||
"questions": [],
|
||||
"approach": "<p><b>Company.canonicalId</b> is the only new state. The engagement ID is derived by <code>formatEngagementCanonicalId</code>, so the two cannot drift.</p>",
|
||||
"decisions": [["Engagement canonical ID", "Derived at read time", "Matches the spreadsheet formula, with no second column to backfill."]],
|
||||
"steps": [
|
||||
{ "n": 1, "text": "Add the canonicalId module, tests first.", "tdd": true },
|
||||
{ "n": 2, "text": "Schema, migration and backfill." },
|
||||
{ "n": 3, "text": "Map the unique violation to CONFLICT.", "tdd": true },
|
||||
{ "n": 4, "text": "Canonical ID field on the company form.", "kind": "mock" }
|
||||
],
|
||||
"risks": [
|
||||
{ "risk": "Two companies given the same code", "handling": "The unique index blocks it and step 3 returns a readable CONFLICT.", "criteria": ["AC2"], "fromTicket": true }
|
||||
],
|
||||
"tests": [
|
||||
{ "kind": "integration", "text": "companies.update with a taken code throws CONFLICT.", "criteria": ["AC2"] }
|
||||
]
|
||||
}
|
||||
```
|
||||
|
||||
- Steps are numbered 1 to N in order. `kind` is `code` (the default), `mock`, or `deliverable`.
|
||||
- Every criterion names the steps that satisfy it. Risks and tests name the criteria they cover.
|
||||
- A risk or test that comes from the ticket's own table carries `"fromTicket": true`. Your own
|
||||
additions leave it off, so the page shows which rows the ticket asked for.
|
||||
- `questions` holds only what genuinely needs Gib's answer, each as a direct question. Empty
|
||||
means none.
|
||||
|
||||
## Mocks
|
||||
|
||||
When a step changes what a user sees (a new screen, a changed layout, a new control, copy a
|
||||
user reads), the plan carries mock artboards:
|
||||
|
||||
```
|
||||
ticket-page mock <KEY> <id> --title "<screen>" --criteria AC3,AC8 --note "<what the build will not do>" --file artboard.html
|
||||
```
|
||||
|
||||
- An artboard is a complete small HTML document with its own `<style>`, rendered in a sandboxed
|
||||
frame beside the proof. Draw the screen state the plan introduces, with the app's real
|
||||
navigation and density around it rather than a wireframe of boxes.
|
||||
- One artboard per new screen state. When the direction is Gib's call, draw each candidate as
|
||||
its own artboard (`option-a`, `option-b`) and ask the choice in `questions`, naming the
|
||||
artboards. This is how the house rule of several static mocks before touching real components
|
||||
is met inside the ticket flow.
|
||||
- `--criteria` names what the artboard illustrates. The Progress tab shows it under those
|
||||
criteria, and replaces the empty half with the proof once a `proof ... --mock <id>` lands.
|
||||
- `--note` states what the artboard shows that the implementation will not do.
|
||||
- A spike deliverable's polished mocks (see **Mocks and datamodel changes**) can start from the
|
||||
approved artboard. The built screens are still screenshotted for real.
|
||||
|
||||
## The epic index
|
||||
|
||||
The index lists the epic's stories in build order: the order they should be completed in, which
|
||||
is not key order and not always the Jira link graph. Each worked ticket keeps its own row current.
|
||||
The rest of the index is yours to seed and keep true.
|
||||
|
||||
- **Seed it** the first time a ticket under the epic is worked, and refresh it on later runs.
|
||||
Search the epic's children (`parent = <EPIC> ORDER BY rank`). For each, record the title, the
|
||||
estimate as `--hours`, the Story Risk Level as `--risk`, the stories it is blocked by as
|
||||
`--after`, and `--status done` if Jira says Done: `ticket-page epic <EPIC> story <KEY> ...`.
|
||||
Closed spikes go in with `epic <EPIC> closed <KEY> --title T`.
|
||||
- **Set the order** with `ticket-page epic <EPIC> order <KEY> <KEY> ...`, listing every story. If
|
||||
the epic already records a build order (a `summary.md` or plan in its folder), that order wins.
|
||||
Otherwise order by the blocked-by links, then Jira rank. Write the reasons the order is what it
|
||||
is with `epic <EPIC> why --file reasons.txt`, one reason per line.
|
||||
- **Gib reorders by asking.** When he does, rerun `order` and update `why`.
|
||||
- A story is marked done only by `--status done`, once it has merged or Jira says Done. The index
|
||||
never guesses that.
|
||||
|
||||
## Reading it as an agent
|
||||
|
||||
`ticket-page md <KEY>` prints the ticket and the plan as markdown. Use it when you resume a
|
||||
ticket, and hand it to the fresh reviewer as the ticket and plan in its reading order.
|
||||
|
||||
## Tickets from before the page
|
||||
|
||||
Tickets planned before the page existed have `resources/ticket.md` and `plan.md` and no
|
||||
`ticket.data.js`. Leave those files alone. When Phase 0 resumes such a ticket, convert it
|
||||
first: `init` from `resources/issue.raw.json`, `plan` from the plan's content, then `approve` and
|
||||
`step ... done` for each step `git log` shows committed. From then on the page is the record.
|
||||
Reference in New Issue
Block a user