Skip to content

feat(DST-1522): add the /create-ticket skill and correct CLAUDE.md's Jira section - #5778

Open
sarahgm wants to merge 7 commits into
mainfrom
feat/DST-1522-create-ticket-skill
Open

feat(DST-1522): add the /create-ticket skill and correct CLAUDE.md's Jira section#5778
sarahgm wants to merge 7 commits into
mainfrom
feat/DST-1522-create-ticket-skill

Conversation

@sarahgm

@sarahgm sarahgm commented Sep 2, 2026

Copy link
Copy Markdown
Member

Description

Adds .claude/skills/create-ticket/SKILL.md, a nine-step skill that turns a settled scope (normally the summary /grill ends with) into one DST-conformant Jira issue: emoji title, issue type, the fields that type actually requires, and the description template Jira carries for that type. It confirms the whole rendered ticket before anything is written.

Closes DST-1522

This PR deliberately overrules the ticket's Suggested Solution. DST-1522 asks to "hardcode the metadata from CLAUDE.md so the skill never has to guess". It does the opposite: step 4 reads the field metadata from Jira on every run and the skill carries no field table at all. Two reasons, both found while building it:

  1. The copy was already wrong. CLAUDE.md listed the three custom fields under "Required Custom Fields (Task)" as though that settled where they apply. Appetite and Rollout Communication are on Task alone, Requires UI Kit Update is on Task and Unplanned, and Bug carries none. Sending a field the chosen type does not have fails the whole create.
  2. The copy could never have been right. Each issue type carries its own description template as that type's description field default. A hardcoded skill would have filed every Bug on Task's template, losing the reproduce steps a bug report exists for. Nobody had written this down and nobody knew to.

The decision was ratified in-session before this landed.

It also departs from the ticket's Expected Outcome on sprint placement. DST-1522 says the skill "then links it to the sprint". It does not. A new ticket lands in the backlog, and reaching the active sprint takes --sprint or an explicit choice at the confirm gate. Adding to a running sprint changes that sprint's committed scope, which is a person's decision rather than a skill's default. Flagging it here so the call is the reporter's to overturn.

Also in the diff:

  • CLAUDE.md, three corrections to the Jira section. Unplanned is not the ad-hoc exception the issue-type table described: over the last 90 days DST filed 123 Tasks against 121 Unplanned, so it is half the board. The two differ by planning provenance, not by kind of work, which is why a feature and a defect can both be Unplanned. The custom-field section now says which types each field is actually on. The description template is marked as Task's, with each type's headings quoted literally rather than slash-separated. That last part fixed a live error: the old wording rendered Unplanned's Context / Trigger heading as "Context or Trigger", because a slash-separated list whose middle item contains a slash cannot be read back.
  • .claude/README.md, the confirmation rule restated. It required the gate to be step 1 of the workflow, which was written from vrt and only ever fit vrt. A skill whose gate exists to show what it is about to do cannot meet it, so create-pr (step 6 of 8) and create-ticket (step 7 of 9) both broke it. The rule is now a hard stop immediately before the first outward call, which is what all three actually do. Alongside it, a note that a gate only holds if the question reaches a human. AskUserQuestion is resolved by the permission component, so under permissions.defaultMode: "auto" with skipAutoPermissionPrompt set it never renders and returns a default indistinguishable from a real answer. Found the slow way in this session.
  • .claude/skills/grill/SKILL.md, one line: its handoff no longer says "once that skill exists".

N/A on most of the checklist below: no UI, no components, no published package. .claude/ is not published, so no changeset is required. Every skill commit in this repo's history has landed without one.

Test Instructions

  1. Run /create-ticket with a thin brief, e.g. align the checkbox and radio styles. It should stop at step 2, name which completeness checks failed, and route to /grill without drafting.
  2. Run /create-ticket with a scoped brief (a /grill summary, or a path to a file holding one). It should reach the confirm gate having read the issue type's fields from Jira and searched for duplicates.
  3. At the gate, switch the issue type to Unplanned. It should re-read the metadata and drop Appetite from both payload and gate, since that field is not on Unplanned's create screen.
  4. Cancel. Nothing should reach Jira.

Steps 8 and 9 are untested. No issue has been created through the skill yet, so createJiraIssue, createIssueLink and the read-back have never run. Everything up to and including the gate has been exercised against live Jira.

Breaking Changes

No

Checklist

  • Storybook preview and Marigold docs preview are available
  • Stories added/updated (with component-test tag where applicable) — N/A, no components
  • Unit tests added/updated — N/A, markdown only
  • Component documentation added/updated (if it exists) — N/A
  • Accessibility reviewed against ARIA APG (for new/changed interactive components) — N/A
  • Visual regression tests updated (for UI changes) — N/A, no file touches packages/components/src, packages/system/src, themes/, .storybook/, *.stories.tsx or *.styles.ts
  • Changeset added (pnpm changeset) — N/A, .claude/ is not a published package

…Jira section

`/create-ticket` turns a settled scope, normally the summary `/grill` ends with,
into one DST issue. Nine steps: assemble a brief, gate it on completeness,
classify, read the issue type's fields from Jira, collect the appetite, search for
duplicates, render the whole ticket and confirm, create, then link and read back.

The ticket's Suggested Solution asks to hardcode the field metadata from
`CLAUDE.md` so the skill "never has to guess". That is overruled, deliberately.
The copy in `CLAUDE.md` was already wrong when this was written: it listed the
three custom fields under "Required Custom Fields (Task)" as though that settled
where they apply. They do not all apply to the same types. `Appetite` and `Rollout
Communication` are on `Task` alone, `Requires UI Kit Update` is on `Task` and
`Unplanned`, and `Bug` carries none of them. Sending a field the chosen type does
not have fails the whole create. Copying a stale table into a second file produces
two stale tables, so step 4 reads the required set with
`getJiraIssueTypeMetaWithFields` per run instead. A fallback table would rescue no
failure mode, since an unreachable Atlassian MCP blocks the create anyway.

The same call answers a second thing a hardcoded table would have got wrong: each
issue type carries its own description template, as that type's `description` field
default. `Task` uses Problem / Expected Outcome / Scope, `Unplanned` uses Summary /
Context / Impact, `Bug` uses Marigold version / How to reproduce / Expected
behavior. Step 8 takes its section headings from that response rather than from a
fixed template, so a Bug filed through the skill keeps its reproduce steps.

Because the required set belongs to the issue type, switching type at the confirm
gate re-runs step 4 before re-rendering. Without that, a payload built for a `Task`
is sent for an `Unplanned` and Jira rejects the whole create.

`CLAUDE.md` is corrected in three places. Unplanned is not the ad-hoc exception the
issue-type table described: over the last 90 days DST filed 123 Tasks against 121
Unplanned, so it is half the board. The two differ by planning provenance rather
than by the kind of work, which is why a feature and a defect can both be
Unplanned, and the title emoji is what carries the kind. The custom-field section
now says which types each field is actually on, and that only `Appetite` lacks a
server-side default. The description template is marked as `Task`'s, with a pointer
to where the others live.

The confirm gate uses `AskUserQuestion`, matching `create-pr` and `review-pr`. Worth
knowing: that prompt does not reach the user when `skipAutoPermissionPrompt` is set,
and the tool then returns a default answer that is indistinguishable from a real
one. The gate holds only where the question actually renders.

Out of scope: picking the ticket up (DST-1523), epic-tree creation, self-assignment,
status transitions. Steps 8 and 9 are untested - no issue has been created through
the skill yet.

`.claude/` is not a published package, so no changeset.
`.claude/README.md` requires a side-effecting skill to confirm before dispatching,
but says nothing about what that confirmation depends on. `AskUserQuestion` is
resolved by the permission component, so under `permissions.defaultMode: "auto"`
with `skipAutoPermissionPrompt` set it never renders. The tool returns the first
option and the result is indistinguishable from a real answer, so the model
proceeds believing a human approved.

Found the slow way: seven questions in one session all came back selecting the
recommended option, and the person at the keyboard had seen none of them.

Worth writing down because of how it fails. It fails toward performing the outward
action, and it is invisible on a machine where the setting is off, so a gate that
works for its author can be silently open for everyone else on the team.
…eteness gate

Step 2 said to route a thin brief to `/grill` and "not fill the gap with your own
assumptions". That wording has a loophole, and it got used on the first real run:
given the one-line brief "align the checkbox and radio styles", the skill read the
two style files, derived an outcome and a Not-included list from them, and carried
on to the confirm gate. Nothing was invented in the sense the old sentence meant.
Everything in the draft traced to the code. The exclusions were still not the
agent's to make.

Three things now close that route. The gate runs before any investigation, so it
cannot be preceded by research that quietly fills the gaps. Failing it forbids
drafting rather than only forbidding assumptions. And the distinction the loophole
turned on is stated outright: reading the repo can evidence a problem someone has
already stated, which is what `/grill` does too, but it cannot supply what counts
as done or what is deliberately left out, because those are the user's calls and
nothing in the repo contains them.

The incident is recorded in the step, in the style `.claude/README.md` already uses
for DST-1529. A rule with the specific rationalization that beat it is more useful
than the rule alone.

Worth noting against the earlier testing: the same gate was tested against a
deliberately thin brief and passed, but the tester knew the fully-scoped version.
It then failed on the first real use. The live failure is the better evidence, and
it points where the weak test said it would: the model judging the gate is the same
one that would rather keep going.
Ran the review checklist over the branch diff against origin/main, since the branch
has no PR yet.

Three things were wrong.

The step-7 gate example rendered "Problem / Expected Outcome / Scope / Suggested
Solution / References", which is Task's template, inside a skill whose steps 4 and 8
both insist the template is per issue type. That is the third instance of the same
mistake, after CLAUDE.md and the frontmatter description.

The no-MCP edge case described a state the workflow cannot reach. It said to stop
after step 7, but without step 4 there is no payload and without step 6 the gate's
duplicate line would be a "none found" that nobody checked. It now stops at the
first failed call and says which checks did not run.

A Notes bullet claimed the late confirm gate was unlike "the other side-effecting
skill" in the repo. `create-pr`'s gate is step 6 of 8, equally late, and there are
four such skills now.

Three things were vague.

Step 6 said to search on "the distinctive nouns of the scope", which is prose where
the rest of the file gives rules. It now prefers proper nouns, a component, file or
skill name, falls back to a repeated compound term, ORs up to three, and refuses to
search on generic words like padding or layout. The important half is the fallback:
with no usable term it says so rather than running a weak query.

The Edit option could not carry what to change. `AskUserQuestion` always offers a
free-text answer, so the edit now arrives with the choice instead of costing another
round trip.

The gate example showed a bare `blocks` three lines below the rule that link type
names are numbered. It reads `3 Blocks` now.

Two of the six share a shape worth naming: a check that did not run reporting itself
as a check that found nothing.
@changeset-bot

changeset-bot Bot commented Sep 2, 2026

Copy link
Copy Markdown

⚠️ No Changeset found

Latest commit: 63aeecb

Merging this PR will not cause a version bump for any packages. If these changes should not result in a new version, you're good to go. If these changes should result in a version bump, you need to add a changeset.

This PR includes no changesets

When changesets are added to this PR, you'll see the packages that this PR includes changesets for and the associated semver types

Click here to learn what changesets are, and how to add one.

Click here if you're a maintainer who wants to add a changeset to this PR

@vercel

vercel Bot commented Sep 2, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated
marigold-docs Ready Ready Preview Sep 2, 2026 11:14am UTC
marigold-storybook Ready Ready Preview Sep 2, 2026 11:14am UTC
1 Skipped Deployment
Project Deployment Actions Updated
marigold-production Ignored Ignored Sep 2, 2026 11:14am UTC

Request Review

A second review pass over the branch, this time checking the file's claims about the
Atlassian MCP against live Jira instead of reading them.

Two were load-bearing.

Step 4 passed `requiredFieldsOnly: true` and then read the per-type description
template off the same response. The template lives on `description`, which is not a
required field, so the narrow call filters it out. On Task it returns 6 fields of 24
and no template at all, which means a Bug would still have been built on Task's
headings. That is the exact failure the run-time read exists to prevent, so the skill
was paying for a call and getting nothing back that it used.

The step-7 gate used `AskUserQuestion`, which `.claude/README.md` documents two files
away as never reaching the screen under `skipAutoPermissionPrompt`. It returns the
first option, and the first option was Create. A gate on a write that fails toward
writing is worse than no gate, because it still reads as one. It now renders the
options and ends the turn, which no setting can answer on someone's behalf.

Four were smaller.

Both `/pick-up` references are gone. That skill does not exist yet, and this branch
removed the same forward reference from /grill one commit earlier.

Nothing decided the epic. The gate rendered one and step 8 sent it, but no step ever
chose it. Step 5 now takes it from the brief or leaves it unset.

The epic's payload shape is pinned to `{"parent": {"key": "DST-1520"}}`, since the
tool's own top-level `parent` argument is for sub-tasks.

CLAUDE.md's "Required Custom Fields (Task)" heading sat directly above the paragraph
explaining that the fields are not required everywhere and that two of the three carry
defaults. It is "Custom Fields by Issue Type" now.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Accessibility tests executed. Download the report here.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Coverage Report for Marigold Code Coverage

Status Category Percentage Covered / Total
🔵 Lines 98.81% 2995 / 3031
🔵 Statements 97.81% 3140 / 3210
🔵 Functions 97.97% 824 / 841
🔵 Branches 91.24% 1938 / 2124
File CoverageNo changed files found.
Generated in workflow #23786 for commit 63aeecb by the Vitest Coverage Report Action

The skill learned last commit that `getJiraIssueTypeMetaWithFields` defaults to
`requiredFieldsOnly: true` and that the filter drops `description`, which is where the
per-type template lives. CLAUDE.md's Description Template section still told the reader
to fetch the template without saying how, so following it gets an empty read and a
fallback to the Task template printed directly below.

That is the more reachable copy of the two. The skill is loaded when someone invokes it,
CLAUDE.md is loaded every session.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Accessibility tests executed. Download the report here.

@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Accessibility tests executed. Download the report here.

The side-effects rule said confirmation must be step 1 of the workflow.
That was written from `vrt`, which has nothing to render before it acts.
Any skill whose gate exists to *show* what it is about to do cannot meet
it, so both `create-pr` (step 6 of 8) and `create-ticket` (step 7 of 9)
broke it, and `create-ticket` argued the exception in its own Notes.

Restate the rule as what the three skills actually practise: a hard stop
immediately before the first outward call. The note in the skill that
argued around the old wording is now redundant and goes.

Also two precision fixes found in the same pass:

- `3 Blocks` is directional and the gate rendered it as a bare arrow,
  which does not say which side blocks. Spell out the inward/outward
  mapping and render the relationship in words.
- CLAUDE.md slash-separated a list of headings whose middle item is
  itself `Context / Trigger`, which is where "Context or Trigger" came
  from. Quote the headings literally and note that Unplanned carries
  them as bold paragraphs rather than headings.
@github-actions

github-actions Bot commented Sep 2, 2026

Copy link
Copy Markdown
Contributor

Accessibility tests executed. Download the report here.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant