# 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 / index.html, epic.data.js # the epic index, in build order / index.html, ticket.data.js # the ticket page resources/ proof/ deliverables/ mr.md tickets// # 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 --issue `. Prints the ticket folder, then any populated Jira fields the page does not show. | | Attachments downloaded, a video transcribed, frames extracted | `scan ` | | Ticket branch resolved, ticket classified | `meta --branch --classified bounded` | | Research notes worth keeping: what a video says, what a spreadsheet holds, a check against prod | `note "" --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.