From fb622fe0d2928967b621c33eae7beb210cbaec37 Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 21 Aug 2026 08:58:39 -0400 Subject: [PATCH 1/6] Add jira-issue skill and compact style for issues and PR bodies Agent-authored LT tickets put good analysis in the wrong shape. The reference failure opens with a heading, runs past a thousand words, and states that it contains four separate user-visible problems, so a triager scanning a queue cannot act on it. Jira Data Center has no expand macro, so nothing in a description can be folded away. The skill keeps the description short and moves the depth into the first comment. The skill interviews for who, when, where and how under a cap of six questions, hunts duplicates before drafting rather than before posting, and gets a three-line lede approved before anything else is written. Unknowns are recorded rather than guessed, and nothing enters a description that the reporter did not say or that we did not verify. compact-style.md is a shared reference adapting the MIT-licensed i-have-adhd skill from chat turns to written artifacts. pr-pitch now points at it and gains a Start here line, numbered verification steps, a closing Next line, an optional status line for long-lived PRs, and the pre-send check. jira-bugfix gains the LT-XXXXX-short-slug branch convention, a relaxed worktree rule that asks rather than refuses, and a note that it is entered at Step 3 when jira-issue hands off. Evidence and screenshot handling is deliberately absent; it follows in a stacked branch. Co-Authored-By: Claude Opus 5 --- .claude/references/compact-style.md | 104 ++++++++ .claude/skills/jira-bugfix/SKILL.md | 32 ++- .claude/skills/jira-issue/SKILL.md | 223 ++++++++++++++++++ .../skills/jira-issue/references/bug-track.md | 91 +++++++ .../skills/jira-issue/references/examples.md | 96 ++++++++ .../jira-issue/references/feature-track.md | 79 +++++++ .../skills/jira-issue/references/format.md | 102 ++++++++ .claude/skills/pr-pitch/SKILL.md | 46 +++- .gitignore | 3 + .../2026-08-21-jira-issue-skill-design.md | 83 +++++++ 10 files changed, 852 insertions(+), 7 deletions(-) create mode 100644 .claude/references/compact-style.md create mode 100644 .claude/skills/jira-issue/SKILL.md create mode 100644 .claude/skills/jira-issue/references/bug-track.md create mode 100644 .claude/skills/jira-issue/references/examples.md create mode 100644 .claude/skills/jira-issue/references/feature-track.md create mode 100644 .claude/skills/jira-issue/references/format.md create mode 100644 Docs/workflows/2026-08-21-jira-issue-skill-design.md diff --git a/.claude/references/compact-style.md b/.claude/references/compact-style.md new file mode 100644 index 0000000000..902cb70855 --- /dev/null +++ b/.claude/references/compact-style.md @@ -0,0 +1,104 @@ +# Compact style for issues and PR bodies + +Shared reference. Pointed at by `.claude/skills/jira-issue/SKILL.md` and +`.claude/skills/pr-pitch/SKILL.md`. Adapted from the MIT-licensed +`i-have-adhd` skill (https://github.com/ayghri/i-have-adhd), which shapes chat +turns; these rules shape written artifacts instead. + +## What changes when the reader is not in the conversation + +A chat reader has just asked you something. A Jira reader is scanning a queue +of forty tickets, and a PR reader has eleven other reviews open. Both arrive +cold, months after the work, with none of your context and no way to ask a +follow-up. Four consequences: + +1. The first line is the only line guaranteed to be read. It carries the + decision, not the setup. +2. Nothing can be held in the reader's head from a previous paragraph. +3. There is no "let me know if you need anything else". Whatever is unanswered + stays unanswered. +4. Length is not a cost the reader chooses to pay. Jira Data Center has no + collapsible sections, so every word in a description is on the screen. + +## The rules + +### 1. Lead with what the reader must decide + +Not context, not history, not the investigation. For a triager: what breaks, +who it hits, how bad. For a reviewer: what the change does and what it risks. + +Bad: `h3. The underlying problem` / "While investigating LT-22710 we noticed" +Good: "Deleting a feature-based natural class empties every rule that used it." + +### 2. Number every multi-step sequence + +Repro steps, verification steps, migration steps. One bounded action per step. +**No step contains "and then" twice.** If a step needs a sub-list, it is two +steps. + +### 3. End with one named next action + +Every description ends with a single `*Next:*` line naming who does what. +Tickets and PRs that end without one stall, because nobody is named. + +Good: `*Next:* reporter to confirm the FLEx version (see comment).` +Good: `Next: approve, or tell me to split the installer change out.` + +### 4. One artifact, one problem + +A ticket describing four problems is four tickets, linked. A PR doing three +unrelated things is three PRs, stacked. Splitting is cheap at filing time and +expensive at review time. + +### 5. State cause, not concern + +No "seems to", no "there may be an issue with", no apology. Name the symptom +and, if known, the mechanism. If the mechanism is inferred rather than +observed, say "inferred" -- that is information, whereas hedging is noise. + +### 6. Cost shape, not hours + +Hours on a ticket read as a commitment nobody made. Size the work by what it +touches. + +Bad: "This will take some work." / "About two days." +Good: "One branch in one file." / "Needs a liblcm release and a package bump." + +### 7. Cap any list at five + +Past five, split into "must" and "nice to have", or accept that the artifact +is really an epic. Five ranked beats ten unranked. + +### 8. No preamble, no recap, no closer + +Banned openers: "This issue describes", "This PR refactors", "While +investigating", "As a note", "I have been looking into". Banned closers: +"Please let me know", "Hope this helps", "Happy to provide more detail". + +Start with the answer. Stop when the answer is done. + +### 9. Say what is not known + +An explicit `*Not known:*` line is worth more than a confident guess. It tells +the next reader where to dig and stops a fabrication becoming folklore. + +### 10. Never assert what was not verified + +Everything above the fold is either something a reporter said or something we +observed. Analysis, inference and reconstruction go in a comment or an +accordion, labelled as such. A screenshot from a headless test is not a +screenshot of the product; say which it is. + +## Pre-send check + +Delete before publishing: + +1. The first sentence, if it announces what the artifact is about to do. +2. The last sentence, if it recaps or asks for further questions. +3. Any "by the way" sidebar. It is a separate ticket. +4. Hedging adverbs carrying no information. Keep a hedge that carries real + uncertainty; deleting that one manufactures confidence. +5. Any idiom. Replace with the literal action. + +Then verify: **reading only the title and the last line, does the reader know +what is wrong and what happens next?** If yes, publish. diff --git a/.claude/skills/jira-bugfix/SKILL.md b/.claude/skills/jira-bugfix/SKILL.md index daa0de36d3..d43378101d 100644 --- a/.claude/skills/jira-bugfix/SKILL.md +++ b/.claude/skills/jira-bugfix/SKILL.md @@ -128,20 +128,37 @@ git branch --show-current > The current branch is ``. This doesn't match > LT-XXXXX. Options: - > - Create a new branch `LT-XXXXX` from `main` + > - Create a new branch `LT-XXXXX-` from `main` > - Continue on the current branch > - Switch to an existing branch (specify name) If creating a new branch: ```powershell git fetch origin - git checkout -b LT-XXXXX origin/main + git checkout -b LT-XXXXX- origin/main ``` -> **Important**: Do NOT create worktrees automatically. This -> repo uses worktrees but creating them involves workspace -> setup scripts. If a worktree is needed, tell the user to -> run the "Worktree: Create/Open from branch" VS Code task. +**Branch naming**: `LT-XXXXX-short-slug`, for example +`LT-22715-nc-delete-warning`. The number keeps this step's +"contains the LT number" check working and makes the branch +greppable; the slug is what makes a list of a dozen worktrees +readable. A bare `LT-XXXXX` tells you nothing at a glance. + +> **Worktrees**: do not create one without asking, and do not +> refuse to create one either. The repo has +> `scripts/Worktree-CreateFromBranch.ps1` and a set of +> `Worktree:` VS Code tasks. Ask which the user wants; if the +> `jira-issue` skill already recorded a `workspace` preference +> in `.claude/.jira-issue-prefs.json`, follow it and say so. +> Note that the script places worktrees under +> `../.worktrees/` while existing worktrees on disk are +> under `.tmp/worktrees/` -- match what is already there. + +> **Arriving from `jira-issue`**: if that skill just filed the +> ticket and the user chose "start now", Steps 0-2 are already +> done -- the issue is fetched, assigned, In Progress, and the +> branch and worktree exist and are named in a ticket comment. +> Begin at Step 3. ## Step 3: Reproduce the Bug (TDD) @@ -410,6 +427,9 @@ All other steps proceed automatically. ## Integration with Other Skills This skill composes with: +- `jira-issue` — files the ticket in the first place, and + hands off to this skill at Step 3 when the user chooses + "start now" - `atlassian-readonly-skills` — reading JIRA issues - `atlassian-skills` — writing to JIRA (assign, comment) - `session-workflow` — session management and handoff diff --git a/.claude/skills/jira-issue/SKILL.md b/.claude/skills/jira-issue/SKILL.md new file mode 100644 index 0000000000..f112907b61 --- /dev/null +++ b/.claude/skills/jira-issue/SKILL.md @@ -0,0 +1,223 @@ +--- +name: jira-issue +description: "Write and file a well-shaped LT Jira issue -- bug or feature. Use whenever asked to file, raise, report, or create a Jira issue or LT ticket, to turn a user report or a finding into a ticket, or to restructure a ticket that buries its point. Interviews for who/when/where/how, hunts duplicates before drafting, gets the three-line lede approved, keeps the description short because Jira Data Center cannot collapse anything, and moves the analysis to a comment." +argument-hint: "Optional: the raw report, a finding, or an existing LT-XXXXX to restructure" +user-invocable: true +--- + +# Jira Issue + +Read `.claude/references/compact-style.md` first. Every rule below assumes it. + +Related skills: `atlassian-readonly-skills` (search, read), `atlassian-skills` +(create, comment, link, transition), `jira-bugfix` (takes over at Phase 8). + +## The constraint that shapes everything + +Jira Data Center has no `{expand}` macro. Nothing in a description can be +folded away, so length in the description is length on the screen. The +description stays short; the depth goes in the first comment. + +## Preferences + +Read `.claude/.jira-issue-prefs.json` (gitignored). On first run, ask the two +questions once and write it: + +```json +{ + "jiraUsername": "", + "workspace": "worktree | branch", + "branchStyle": "LT-XXXXX-slug" +} +``` + +Thereafter do not ask. State which preference was used when acting on it, so a +wrong one is visible and correctable. + +## Phase 0 -- Type, and the one-problem test + +Bug or feature. Then count the problems in the raw material. + +**More than one problem means more than one ticket.** LT-22715 announces that +it contains "four separate user-visible problems"; that is four tickets and a +set of links, and splitting is far cheaper now than after triage. Propose the +split, name each ticket in one line, and get agreement before drafting. + +## Phase 1 -- Interview + +One question at a time. **Hard cap of six.** Quote the answers; do not +paraphrase them into confidence. + +| | Bug | Feature | +| --- | --- | --- | +| Who | Which user, what role, how many affected | Who wants it, and what they are actually trying to accomplish | +| When | FLEx version and build, date, first time or recurring, did it work before | How often it comes up | +| Where | Tool, window, menu path, which project | Which area of FLEx | +| How | Exact actions -- typed, pasted, dragged, IME, Send/Receive | What they do today instead | +| How bad | Data loss, workaround, blocking | What "done" looks like | + +Two rules outrank the questions: + +- **"I don't know" is an answer and it goes in the ticket** as a `*Not known:*` + line. A recorded gap tells the next reader where to dig; a guess becomes + folklore. +- **Nothing enters the description that the reporter did not say or that we + did not verify.** Inferred mechanism goes to the analysis comment, labelled + inferred. + +If the developer is relaying a second-hand report, ask which parts they +witnessed. Second-hand detail is recorded as second-hand. + +## Phase 2 -- Duplicates and related issues, before drafting + +Search first. If the ticket exists, the work is a comment on it, and finding +that out after drafting wastes the drafting. + +Four passes, ten results each: + +```powershell +python -c @' +import sys; sys.path.insert(0, ".claude/skills/atlassian-readonly-skills/scripts") +from jira_search import jira_search +jql = "project = LT AND text ~ \"natural class\" AND status != Closed ORDER BY updated DESC" +print(jira_search(jql, fields="key,summary,status,updated", limit=10)) +'@ +``` + +1. **Symptom words** -- the user's vocabulary, not ours. +2. **Area** -- `project = LT AND component = "..." AND status != Closed`. +3. **Link walk** -- for every ticket already cited, read its links and follow + one hop. +4. **Mechanism** -- the type or method name, when the code location is known. + +Present at most five candidates: + +| Key | Summary | Why it might be the same | Verdict | +| --- | --- | --- | --- | + +Verdicts are duplicate, related, or unrelated. **Never file without showing +this table and getting a yes.** If a duplicate exists, stop and offer to +comment on it instead; if the developer still wants a new ticket, file it and +link it as a duplicate. + +## Phase 3 -- The lede, approved before anything else is written + +Draft exactly three lines and stop: + +``` +*What happens:* Deleting a feature-based natural class empties every rule +formula that used it. No warning, no prompt. +*Who hits it:* Anyone whose phonological rules reference Natural Classes. +*How bad:* Silent data loss. Rules must be rebuilt by hand. +``` + +For a feature track the labels are `*What they want:*`, `*Who wants it:*`, +`*Why it matters:*`. + +Show it rendered. The developer approves, edits, or adds detail. Loop at most +three times; if it is still wrong, ask which line is wrong rather than guessing +a fourth. **Nothing else is drafted until these three lines are theirs.** + +## Phase 4 -- The rest of the ticket + +Follow `references/bug-track.md` or `references/feature-track.md`. Field +budgets and Jira markup are in `references/format.md`. Worked before-and-after +examples are in `references/examples.md`. + +Budgets, since nothing can be folded: summary 80 characters, lede 60 words, +whole description 250 words ending in one `*Next:*` line. Overflow goes to the +analysis comment. + +## Phase 5 -- Permission + +**Hard stop.** Before any attachment, sample project, log, or screenshot of +real data leaves the machine: + +> Do you have permission to post this? + +Language data is frequently unpublished and community-owned -- vernacular +text, speaker names, unreleased lexicons. Never attach a project or capture +the agent found on disk without being told to. If the answer is no or unclear, +describe the data instead of attaching it and record that in the ticket. + +## Phase 6 -- Publish + +Order matters: issue, then comment, then links. + +Write the description and the comment to files first; never inline multi-line +Jira markup into a command. + +```powershell +python -c @' +import sys; sys.path.insert(0, ".claude/skills/atlassian-skills/scripts") +from jira_issues import jira_create_issue +desc = open("desc.txt", encoding="utf-8").read() +print(jira_create_issue("LT", "", "Bug", description=desc)) +'@ +``` + +Then the analysis comment via `jira_add_comment(issue_key, comment)`, then one +link per related ticket. Read the real link type names rather than guessing: + +```powershell +python -c @' +import sys; sys.path.insert(0, ".claude/skills/atlassian-readonly-skills/scripts") +from jira_links import jira_get_link_types +print(jira_get_link_types()) +'@ +``` + +Then `jira_create_issue_link(link_type, inward_issue_key, outward_issue_key)`. + +SIL Jira is Data Center: `assignee` takes a username, not an account ID, +despite what the docstring says. If assignment fails, fall back to +`custom_fields={"assignee": {"name": ""}}`. + +## Phase 7 -- Report + +Three lines: the key, the URL, and one `Next:` line. Nothing else. + +## Phase 8 -- "Do you want to start this now?" + +Ask. Four exits: + +| Answer | Actions | +| --- | --- | +| Start now | Assign, transition to In Progress, comment the branch and worktree, create the workspace, hand to `jira-bugfix` at its Step 3 | +| Mine, later | Assign only. Status untouched, no branch | +| Leave for triage | Nothing. This is the default | +| Someone else's | Assign to the named person, comment, stop | + +On "start now": + +1. Branch `LT-XXXXX-short-slug` off fresh `origin/main`. +2. Workspace per the saved preference -- worktree or branch in place. Say + which preference was used. +3. Comment on the ticket, naming both: + + > Taken. Working on branch `LT-22715-nc-delete-warning`, worktree + > `.tmp/worktrees/nc-delete-warning`. + + This is what makes the ticket the index into a worktree list of sixteen. +4. Hand off to `jira-bugfix` at Step 3. Do not repeat its Steps 0-2; the issue + is already in hand, assigned and in progress. + +Resolve transitions by ID from `jira_get_transitions`, never by guessing a +name. If the transition is refused for permissions, say "assigned, please move +it to In Progress yourself" and continue rather than aborting. **Never +transition to Done or Resolved** -- that follows a merged PR. + +If Phase 2 found the work already exists, Phase 8 targets that existing key. + +## Before finishing + +- [ ] Summary is 80 characters or fewer and names the symptom, not the cause. +- [ ] The three lede lines are the developer's, not the first draft. +- [ ] Description is 250 words or fewer and ends in one `*Next:*` line. +- [ ] Every claim above the fold was said by the reporter or verified by us. +- [ ] Every unknown is a `*Not known:*` line rather than a guess. +- [ ] The duplicate table was shown and answered. +- [ ] Links created for every candidate marked duplicate or related. +- [ ] Nothing was attached without the permission question being answered. +- [ ] The reader knows what is wrong and what happens next from the title and + the last line alone. diff --git a/.claude/skills/jira-issue/references/bug-track.md b/.claude/skills/jira-issue/references/bug-track.md new file mode 100644 index 0000000000..bb66fe55d1 --- /dev/null +++ b/.claude/skills/jira-issue/references/bug-track.md @@ -0,0 +1,91 @@ +# Bug track + +What a FLEx bug report needs, from a real reporter's list: a brief description +of the problem, a sample project, and the steps to reproduce. Everything below +serves those three. + +## The interview + +Six questions maximum, one at a time. Stop early if the answers are already in +the raw report. + +**Who** -- which user, what role (linguist, consultant, translation advisor), +and how many people are affected. "One user" and "every project on the team" +are different tickets. + +**When** -- FLEx version *and* build number, the date it happened, whether it +is the first time or recurring, and whether it worked before. Version is +usually the decisive fact: a guard that shipped in 9.2.5 changes the whole +diagnosis depending on which side of it the reporter is on. + +**Where** -- the tool and window, the exact menu path, and which project. Ask +whether the project can be shared before assuming it can. + +**How** -- the exact actions. For text-entry bugs this is where the answer +hides: typed directly, pasted, dragged, typed with a vernacular keyboard or +IME, or arrived through Send/Receive. "Just typed it" and "pasted it" are +different bugs with different fixes. + +**How bad** -- data loss, a workaround, or blocked work. Silent data loss +outranks a visible error. Say whether Undo recovers it. + +## Steps to reproduce + +Numbered, one bounded action per step, no step containing "and then" twice. +Each step is something the reader can do without knowing the codebase. + +``` +# Open Grammar > Natural Classes. +# Create a natural class from phonological features and name it. +# Insert that class into a phonological rule formula. +# Return to Natural Classes and delete the class. +# Open the rule again. +``` + +Rules: + +- Start from a state the reader can reach: a new project, or a named sample. +- Never start at "with the corrupted project open". +- If a step needs specific data, say exactly what data. +- If reproduction is unreliable, say how many attempts out of how many. An + intermittent bug reported as reliable wastes the first hour of the fix. +- If nobody has reproduced it, say so in one line and put the inferred path in + the comment. A ticket that claims a reproduction it does not have is worse + than one that admits the gap. + +## Expected and Actual + +One line each, both observable. "It should work" is neither. + +## Environment + +FLEx version and build, Windows version, and anything unusual: Send/Receive in +use, a non-default keyboard or IME, a project migrated from an older version. + +## Sample project + +**Ask before attaching. Always.** + +> Do you have permission to post this? + +FLEx projects contain unpublished lexical data, vernacular text, and often +speaker names. The reporter may not own the data, and a Jira attachment is a +publication to everyone with project access. + +If the answer is no, or unclear: + +- Describe the shape of the data instead: how many entries, which writing + systems, which fields populated. +- Ask whether a minimal synthetic project reproduces it. +- Record in the ticket that a sample exists but was not attached, so nobody + re-asks. + +Never attach a project the agent found on disk. Never attach a screenshot of a +live project without the same question. + +## Priority + +Do not set a priority number. Give the triager the facts that determine one: +whether data is lost, whether Undo recovers it, whether a workaround exists, +and how many users are affected. Those four lines are worth more than a guess +at a field value, and the lede already carries them. diff --git a/.claude/skills/jira-issue/references/examples.md b/.claude/skills/jira-issue/references/examples.md new file mode 100644 index 0000000000..655a7fad7e --- /dev/null +++ b/.claude/skills/jira-issue/references/examples.md @@ -0,0 +1,96 @@ +# Worked example: LT-22715 + +A real ticket, filed before this skill existed. The analysis in it is good. +The shape is the failure. + +## Before + +Summary: `No distinction between user-created and auto-generated natural classes` + +Description opened with: + +``` +h3. The underlying problem + +FLEx has two kinds of feature-based natural class and no way to tell them apart. +``` + +and ran past a thousand words through `h3. Symptoms`, `h3. Why the LT-22576 +approach cannot be extended`, `h3. Proposed signal: presence of an +Abbreviation`, a five-column table, `h3. Open challenges`, and `h3. Three ways +to resolve them`. + +Four things went wrong: + +1. **The first rendered line is a heading**, so the first thing a triager reads + is the word "problem" and nothing else. +2. **It contains four problems.** The description says so outright: "that + single missing distinction produces four separate user-visible problems." + None of the four can be triaged, prioritised or closed on its own. +3. **Analysis sits above the fold.** The comparison with LT-22576, the + proposed signal, and the three resolution options are all real and useful, + and none of them is what a triager needs in ten seconds. +4. **The blocking question is buried.** Four open challenges needing a team + decision appear after roughly eight hundred words. + +## After + +Summary: `Natural Classes: generated classes are indistinguishable from real ones` + +``` +*What they want:* A rule should show the natural class the user picked, not a +stack of features, and the Natural Classes list should not fill with entries +nobody created. +*Who wants it:* Anyone building phonological rules from features. Surfaced by +LT-22576. +*Why it matters:* Editing a shared class from inside one rule silently changes +every other rule using it. + +h3. The cause + +FLEx has two kinds of feature-based natural class and nothing in the model +separates them: ones created deliberately in Grammar > Natural Classes, and +ones fabricated silently when features are inserted into a rule. Both are +{{PhNCFeatures}}. + +h3. Symptoms, filed separately + +# LT-AAAAA -- a shared class is rewritten from inside a rule, with nothing on +screen saying so +# LT-BBBBB -- a named class renders as a decomposed feature list +# LT-CCCCC -- generated classes accumulate in the user's list, never cleaned up +# LT-DDDDD -- no way to promote a feature bundle into a real class + +h3. Ideas to resolve + +# Treat a filled Abbreviation as "this is a real class" -- one branch in +{{RuleFormulaVcBase.Display}}, no model change +# Migrate, filling Abbreviation from Name -- needs a liblcm release and a +package bump, so no longer FieldWorks-only +# Stop naming generated classes at all -- no migration, but legacy data stays +ambiguous + +h3. Open question + +Existing real classes with no Abbreviation become indistinguishable from +generated ones. A team decision is needed before any of the three are built. + +_Trade-offs, the LT-22576 comparison and the migration detail are in the first +comment._ + +*Next:* team decision on the migration question. +``` + +228 words. The four symptom tickets are linked, each triageable on its own. + +## What moved to the comment + +Everything cut is still on the ticket, one scroll down: + +- Why the LT-22576 display-name heuristic cannot be extended +- The five-column rendering table +- Full detail on all three options +- All four open challenges, not just the blocking one +- The implementation note about the existing label-rendering path + +Nothing was lost. It stopped being the first thing a triager reads. diff --git a/.claude/skills/jira-issue/references/feature-track.md b/.claude/skills/jira-issue/references/feature-track.md new file mode 100644 index 0000000000..b865506947 --- /dev/null +++ b/.claude/skills/jira-issue/references/feature-track.md @@ -0,0 +1,79 @@ +# Feature track + +What a FLEx feature request needs: the user story -- what does the user want to +do -- what has been tried including workarounds, and ideas to resolve it. + +## The interview + +**Who wants it, and what are they actually trying to accomplish.** Users +request a widget when they have a goal. "Add a button that clears generated +natural classes" is a widget; "stop my rule list filling with entries I never +created" is the goal. File the goal, mention the widget as an idea. A ticket +filed as a widget forecloses every better solution. + +**How often it comes up.** Once, in one project, is a different ticket from +every workshop for three years. + +**What they do today.** The workaround is the single most useful thing in a +feature request. It shows the shape of the gap, proves the need is real, and +sometimes turns out to be good enough with one small change. + +**What "done" looks like.** In the user's terms, not ours. This is what the +reporter will check when a build ships. + +## Description skeleton + +``` +*What they want:* +*Who wants it:* +*Why it matters:* + +h3. What they do today + + +h3. Ideas to resolve +# -- +# -- + +h3. Open questions + + +*Not known:* + +_Detail in the first comment._ + +*Next:* +``` + +## Ideas to resolve + +Cap at five, ranked, one line each. Each idea carries a **cost shape**, not +hours: + +- "One branch in one file." +- "Needs a liblcm release and a package bump, so no longer a FieldWorks-only + change." +- "New model field, so a data migration." + +Cost shape is what lets a triager sequence the work. Hours are a commitment +nobody in the conversation is authorised to make. + +Detail belongs in the comment. The description gets the one-liners. + +## Open questions + +If the feature needs a decision that is not the implementer's to make -- what +happens to existing data, whether a field becomes mandatory, whether a +migration runs once or repeatedly -- say so, in one line each, under a heading +that says a team decision is needed. Burying a blocking question inside a +paragraph of analysis is how a ticket sits untouched for a year. + +## Scope + +**One ticket, one problem**, and features break this more often than bugs. A +request that reads "and while we are there we should also" is two tickets. + +When one underlying cause produces several user-visible problems, file the +problems separately and link them to one ticket describing the cause. Each +problem can then be triaged, prioritised and fixed on its own, which is the +whole point of separating them. diff --git a/.claude/skills/jira-issue/references/format.md b/.claude/skills/jira-issue/references/format.md new file mode 100644 index 0000000000..7bd3580e9c --- /dev/null +++ b/.claude/skills/jira-issue/references/format.md @@ -0,0 +1,102 @@ +# Field contract, budgets, and Jira markup + +## Budgets + +| Field | Budget | Why | +| --- | --- | --- | +| Summary | 80 characters | Truncates in queue views past roughly that | +| Lede | 60 words, three labelled lines | One glance, no scrolling | +| Description | 250 words total, ending in one `*Next:*` line | Nothing can be collapsed | +| Analysis comment | As long as the reasoning deserves | Nobody is forced to scroll past it | + +Count words before publishing. Over budget means the overflow was comment +material, not that the budget was wrong. + +## Summary + +Shape: `Area: what goes wrong`. + +- Names the **symptom**, not the cause. The cause is often wrong at filing + time; the symptom is what a duplicate search will match. +- No ticket references, no "Bug:", no trailing punctuation. +- Uses the reporter's vocabulary so the next person searching finds it. + +Good: `Natural Classes: deleting a feature class empties rules that use it` +Bad: `PhNCFeatures.DeletionTextTSS override suppresses the delete warning` +Bad: `Issue with natural classes` + +## Description skeleton + +``` +*What happens:* +*Who hits it:* +*How bad:* + +h3. Steps to reproduce +# +# + +h3. Expected + + +h3. Actual + + +h3. Environment +FLEx , . + +*Not known:* + +_Analysis in the first comment._ + +*Next:* +``` + +Feature track replaces Steps/Expected/Actual with the sections in +`feature-track.md`. Everything else is identical. + +## Jira Data Center wiki markup + +There is **no `{expand}`**. It is a Confluence macro. Do not write one, and do +not plan a description around content being hidden. + +| Need | Markup | +| --- | --- | +| Heading | `h3. Text` | +| Bold | `*text*` | +| Italic | `_text_` | +| Monospace | `{{text}}` | +| Numbered list | `# item` | +| Bullet list | `* item` | +| Code block | `{code:java}...{code}` or `{noformat}...{noformat}` | +| Quote block | `{quote}...{quote}` | +| Table | `||head||head||` then `|cell|cell|` | +| Link | `[text|https://example.com]` | +| Ticket reference | `LT-12345` -- linkifies automatically | +| Attached image | `!name.png!` or `!name.png\|thumbnail!` | + +Two traps: + +- `*` at the start of a line is a bullet, not bold. The lede labels work + because `*What happens:*` is followed by text on the same line. +- Underscores inside identifiers turn on italics. Wrap any identifier in + `{{...}}`. + +## What goes in the analysis comment + +Everything true and useful that a triager does not need in the first ten +seconds: + +- Root-cause analysis and the code path +- Evidence, probes, test output +- Inferred mechanism, labelled inferred +- Options considered and their trade-offs +- Cost shape for each option +- Anything second-hand, labelled second-hand + +Open the comment with one line saying what it is, so a reader scrolling past +knows whether to stop: + +``` +h3. Analysis (inferred unless marked verified) +``` diff --git a/.claude/skills/pr-pitch/SKILL.md b/.claude/skills/pr-pitch/SKILL.md index 961e2e66f2..579af0abac 100644 --- a/.claude/skills/pr-pitch/SKILL.md +++ b/.claude/skills/pr-pitch/SKILL.md @@ -14,6 +14,10 @@ Related skills this one calls: `fieldworks-code-commenting` (when doc text is inlined into source), `fieldworks-migration-scope-review` (when the PR is a large migration branch and the pitch must justify its scope). +Read `.claude/references/compact-style.md` before writing the pitch. It is the +shared style contract for issues and PR bodies, and it is where the banned +openers, the five-item list cap and the pre-send check live. + ## What this produces Two artifacts, always together, never one without the other: @@ -137,6 +141,16 @@ deleting the qualifiers that make a claim honest. The top zone, in this order. +### 0. Status, for a PR that has been open a while (one line, optional) + +A reviewer returning to a long-lived PR cannot hold its state between visits. +Give them one line at the top: what it is waiting on, and anything red. + +> Ready for review. CI green except the known-flaky interlinear test. + +Skip it on a PR opened today. It exists so nobody has to reconstruct the state +from a comment thread. + ### 1. Lead with what it does (2-3 sentences) Open with the concrete thing, not the framing. A screenshot or GIF if the @@ -152,7 +166,15 @@ not make them read to find it. ### 3. Where to look (at most five bullets, one line each) -The load-bearing section. The failure points a domain expert would +Open with one `**Start here:**` line naming the first file to read and why. +That is the *entry point*, not the riskiest thing -- a reviewer who is not told +where to begin reads the diff in alphabetical order, which is nobody's reading +order. + +> **Start here:** `ViewDefinitionOverrideApplier.cs` -- everything else is +> plumbing around what it does. + +Then the load-bearing part. The failure points a domain expert would anticipate, ordered by what would sink the PR -- not by what was hardest to build. One line each: the risk, and the thing that pins it -- the gate, the test, the invariant. @@ -176,6 +198,23 @@ tests, manual checks -- and what was *not* run. Anything currently red or known-broken goes here in plain words; a reviewer must never learn of a red job from the checks tab after reading a body that implied green. +When the reviewer needs to run something themselves, give it as numbered +steps, one bounded action each, copy-pasteable: + +> 1. `.\build.ps1 -CommentHygiene` +> 2. `.\test.ps1 -TestProject '' -TestFilter ''` +> 3. Open Grammar > Natural Classes and delete a feature-based class. + +Prose verification instructions get skipped. Numbered ones get run. + +### 6. What you want from the reviewer (one line) + +Close the top zone with a single line naming the next action and who takes it. +A pitch that ends on a verification paragraph leaves the reviewer guessing +whether they are approving, splitting, or blocking. + +> Next: approve, or tell me to split the installer change out. + Pitch rules: - No process narration. "We then discovered...", "after several @@ -314,6 +353,11 @@ Before finishing, confirm: - [ ] Every count was recounted. - [ ] The pitch does not repeat what an accordion already says. - [ ] Working notes are gitignored (`Docs/migration/working/`), not merged. +- [ ] The top zone opens with a `**Start here:**` line and closes with a + `Next:` line. +- [ ] The pre-send check in `.claude/references/compact-style.md` passes: + reading only the title and the last line, does the reviewer know what + this does and what is wanted from them? Do not mark this complete on unverified claims. If a claim could not be checked, say so in the report rather than asserting it. diff --git a/.gitignore b/.gitignore index a4c7c89ff4..6db7bd9207 100644 --- a/.gitignore +++ b/.gitignore @@ -223,3 +223,6 @@ DataTreeTimingBaselines.json Docs/migration/working/ Build/Agent/comment-hygiene-report.json .review/ + +# Per-developer preferences for the jira-issue skill +.claude/.jira-issue-prefs.json diff --git a/Docs/workflows/2026-08-21-jira-issue-skill-design.md b/Docs/workflows/2026-08-21-jira-issue-skill-design.md new file mode 100644 index 0000000000..881c8b417f --- /dev/null +++ b/Docs/workflows/2026-08-21-jira-issue-skill-design.md @@ -0,0 +1,83 @@ +# Design: the `jira-issue` skill + +Working document. When this becomes a PR, `pr-pitch` triages it as RESEARCH and +evicts it into the PR body. It is not meant to merge. + +## Problem + +Agent-authored LT tickets carry good analysis in the wrong shape. LT-22715 is +the reference failure: its first rendered line is `h3. The underlying problem`, +its description runs past a thousand words, and it announces that it contains +"four separate user-visible problems". A triager scanning a queue cannot use it. + +Jira Data Center has no `{expand}` macro, so nothing can be folded away. Length +in the description is length on the screen. That single constraint drives the +whole design: the description stays short and the depth moves to a comment. + +## Shape + +One skill, `.claude/skills/jira-issue/`, two tracks (bug and feature) sharing +one format contract, one style reference, one publish path. Nine phases. + +| Phase | Name | Gate | +| --- | --- | --- | +| 0 | Type and one-problem test | Split before drafting | +| 1 | Interview: who/when/where/how/how-bad | Cap of six questions | +| 2 | Duplicate and related hunt | Candidate table, developer says yes | +| 3 | Lede | Developer approves three lines | +| 4 | Track sections | - | +| 5 | Permission | Hard stop on user data | +| 6 | Publish | Issue, analysis comment, links | +| 7 | Report | Key, URL, one `Next:` line | +| 8 | Start now? | Four exits, hands off to `jira-bugfix` | + +## Decisions + +**Search before drafting, not before posting.** If the ticket already exists, +the work is a comment on it. Discovering that after twenty minutes of drafting +wastes the drafting. + +**The lede is approved before anything else is written.** Three labelled lines, +rendered and shown. Up to three revisions, then the skill asks which line is +wrong rather than guessing a fourth time. + +**"I don't know" is recorded, not resolved.** Missing facts go into the ticket +as `*Not known:*`. Invented ones are damage. Nothing enters the description +that the reporter did not say or that we did not verify; inferred mechanism +goes to the analysis comment, labelled as inferred. + +**Preferences are a file, not memory.** `.claude/.jira-issue-prefs.json`, +gitignored, written on first run. Portable to any agent this repo supports, +per-clone, and one developer's `worktree` never becomes another's default. + +**Branches are `LT-XXXXX-short-slug`.** Greppable by ticket and readable in a +worktree list of sixteen. `jira-bugfix`'s existing "branch name contains the LT +number" check keeps working. + +## Rejected + +**A second skill pair mirroring `pr-preflight` / `pr-pitch`.** Two entrypoints +to keep straight for a workflow that is mostly linear. The PR pair earns its +split because the write-up is re-run on existing PRs; a Jira description is +rewritten far less often. + +**Collapsible sections in the description.** `{expand}` is a Confluence macro. +Verified absent before relying on the comment split. + +**A shared `compact-writing` skill.** A fourth skill in the chain, loadable +when nobody asked for it. A reference file both skills point at costs less. + +## Sources + +`compact-style.md` adapts the MIT-licensed `i-have-adhd` skill +(https://github.com/ayghri/i-have-adhd) from chat turns to written artifacts. + +## Split + +PR 1, this branch: skill, style reference, prefs, `jira-bugfix` and `pr-pitch` +edits. Touches no image behaviour. + +PR 2, stacked: evidence framework, `jira_add_attachment`, `pr-pitch` evidence +section, `fieldworks-avalonia-ui` capture step. Its publish step probes for +`gh --attach`, falls back to `gh image`, then to an orphan evidence branch, so +it does not depend on the gh release landing. From 9a7ca75aeccc5bba7266187ca1811aed8d39f8fd Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 21 Aug 2026 09:12:37 -0400 Subject: [PATCH 2/6] Skip ticket sections that do not apply to the subject Not every LT ticket is about FLEx the product. One about developer tooling, an agent skill, the build or documentation has no FLEx version, no project file, no menu path and nothing to reproduce inside the application. Phase 0b now decides what kind of thing the ticket is about before the interview starts, and maps that to the environment questions worth asking. Asking which FLEx build was running, for a ticket about a Markdown reference file, spends one of six questions and signals that the ticket was generated rather than written. The output rule is the same: never emit a section that does not apply. A template dutifully filled with N/A costs the reader the same scan and returns nothing, so the heading goes instead. Co-Authored-By: Claude Opus 5 --- .claude/skills/jira-issue/SKILL.md | 26 ++++++++++++++++++- .../skills/jira-issue/references/format.md | 5 ++++ 2 files changed, 30 insertions(+), 1 deletion(-) diff --git a/.claude/skills/jira-issue/SKILL.md b/.claude/skills/jira-issue/SKILL.md index f112907b61..f6b819f258 100644 --- a/.claude/skills/jira-issue/SKILL.md +++ b/.claude/skills/jira-issue/SKILL.md @@ -43,10 +43,32 @@ it contains "four separate user-visible problems"; that is four tickets and a set of links, and splitting is far cheaper now than after triage. Propose the split, name each ticket in one line, and get agreement before drafting. +## Phase 0b -- Relevance + +**Ask what kind of thing this ticket is about before asking anything else.** +Not every LT ticket is about FLEx the product. A ticket about developer +tooling, an agent skill, the build, CI, or documentation has no FLEx version, +no project file, no menu path, and no reproduction inside the application. + +| Subject | Environment questions that apply | +| --- | --- | +| FLEx product | Version and build, OS, project, menu path, keyboard/IME | +| Developer tooling, agent skills, docs | Repo, branch, which skill or script. No FLEx version | +| Build, CI, installer | Branch, runner, toolchain, which script. No project | + +**Never emit a section that does not apply.** A template dutifully filled with +"N/A" is worse than a short ticket: it costs the reader the same scan and +returns nothing. Drop the heading instead. + +The same test governs the interview. Asking a developer which FLEx build they +were running, for a ticket about a Markdown reference file, wastes one of the +six questions and signals that the ticket was generated rather than written. + ## Phase 1 -- Interview One question at a time. **Hard cap of six.** Quote the answers; do not -paraphrase them into confidence. +paraphrase them into confidence. Skip any row Phase 0b ruled irrelevant -- +the cap is a budget, and a wasted question is one you do not get back. | | Bug | Feature | | --- | --- | --- | @@ -216,6 +238,8 @@ If Phase 2 found the work already exists, Phase 8 targets that existing key. - [ ] Description is 250 words or fewer and ends in one `*Next:*` line. - [ ] Every claim above the fold was said by the reporter or verified by us. - [ ] Every unknown is a `*Not known:*` line rather than a guess. +- [ ] No section was emitted that does not apply -- no "N/A" environment, no + empty repro steps on a ticket with nothing to reproduce. - [ ] The duplicate table was shown and answered. - [ ] Links created for every candidate marked duplicate or related. - [ ] Nothing was attached without the permission question being answered. diff --git a/.claude/skills/jira-issue/references/format.md b/.claude/skills/jira-issue/references/format.md index 7bd3580e9c..0b2caea1a1 100644 --- a/.claude/skills/jira-issue/references/format.md +++ b/.claude/skills/jira-issue/references/format.md @@ -55,6 +55,11 @@ _Analysis in the first comment._ Feature track replaces Steps/Expected/Actual with the sections in `feature-track.md`. Everything else is identical. +This is a skeleton, not a form. Drop any heading that does not apply to the +subject -- a ticket about a build script has no `h3. Environment` worth +writing, and a ticket about a documentation file has nothing to reproduce. +See Phase 0b in `../SKILL.md`. + ## Jira Data Center wiki markup There is **no `{expand}`**. It is a Confluence macro. Do not write one, and do From 15eb19cc627a06d1d6c4891a16734f6eb25203d8 Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 21 Aug 2026 10:35:49 -0400 Subject: [PATCH 3/6] Move the design record from the tree into the PR body The design document was a working artifact, not something a future maintainer needs in order to change this code correctly. Under the pr-pitch triage it is RESEARCH: a one-time investigation whose conclusions are now carried by the skill itself. Its content survives in the pull request body, where the decisions, the rejected alternatives and the reasoning stay recoverable without shipping scaffolding into the repository. Refs LT-22723 Co-Authored-By: Claude Opus 5 --- .../2026-08-21-jira-issue-skill-design.md | 83 ------------------- 1 file changed, 83 deletions(-) delete mode 100644 Docs/workflows/2026-08-21-jira-issue-skill-design.md diff --git a/Docs/workflows/2026-08-21-jira-issue-skill-design.md b/Docs/workflows/2026-08-21-jira-issue-skill-design.md deleted file mode 100644 index 881c8b417f..0000000000 --- a/Docs/workflows/2026-08-21-jira-issue-skill-design.md +++ /dev/null @@ -1,83 +0,0 @@ -# Design: the `jira-issue` skill - -Working document. When this becomes a PR, `pr-pitch` triages it as RESEARCH and -evicts it into the PR body. It is not meant to merge. - -## Problem - -Agent-authored LT tickets carry good analysis in the wrong shape. LT-22715 is -the reference failure: its first rendered line is `h3. The underlying problem`, -its description runs past a thousand words, and it announces that it contains -"four separate user-visible problems". A triager scanning a queue cannot use it. - -Jira Data Center has no `{expand}` macro, so nothing can be folded away. Length -in the description is length on the screen. That single constraint drives the -whole design: the description stays short and the depth moves to a comment. - -## Shape - -One skill, `.claude/skills/jira-issue/`, two tracks (bug and feature) sharing -one format contract, one style reference, one publish path. Nine phases. - -| Phase | Name | Gate | -| --- | --- | --- | -| 0 | Type and one-problem test | Split before drafting | -| 1 | Interview: who/when/where/how/how-bad | Cap of six questions | -| 2 | Duplicate and related hunt | Candidate table, developer says yes | -| 3 | Lede | Developer approves three lines | -| 4 | Track sections | - | -| 5 | Permission | Hard stop on user data | -| 6 | Publish | Issue, analysis comment, links | -| 7 | Report | Key, URL, one `Next:` line | -| 8 | Start now? | Four exits, hands off to `jira-bugfix` | - -## Decisions - -**Search before drafting, not before posting.** If the ticket already exists, -the work is a comment on it. Discovering that after twenty minutes of drafting -wastes the drafting. - -**The lede is approved before anything else is written.** Three labelled lines, -rendered and shown. Up to three revisions, then the skill asks which line is -wrong rather than guessing a fourth time. - -**"I don't know" is recorded, not resolved.** Missing facts go into the ticket -as `*Not known:*`. Invented ones are damage. Nothing enters the description -that the reporter did not say or that we did not verify; inferred mechanism -goes to the analysis comment, labelled as inferred. - -**Preferences are a file, not memory.** `.claude/.jira-issue-prefs.json`, -gitignored, written on first run. Portable to any agent this repo supports, -per-clone, and one developer's `worktree` never becomes another's default. - -**Branches are `LT-XXXXX-short-slug`.** Greppable by ticket and readable in a -worktree list of sixteen. `jira-bugfix`'s existing "branch name contains the LT -number" check keeps working. - -## Rejected - -**A second skill pair mirroring `pr-preflight` / `pr-pitch`.** Two entrypoints -to keep straight for a workflow that is mostly linear. The PR pair earns its -split because the write-up is re-run on existing PRs; a Jira description is -rewritten far less often. - -**Collapsible sections in the description.** `{expand}` is a Confluence macro. -Verified absent before relying on the comment split. - -**A shared `compact-writing` skill.** A fourth skill in the chain, loadable -when nobody asked for it. A reference file both skills point at costs less. - -## Sources - -`compact-style.md` adapts the MIT-licensed `i-have-adhd` skill -(https://github.com/ayghri/i-have-adhd) from chat turns to written artifacts. - -## Split - -PR 1, this branch: skill, style reference, prefs, `jira-bugfix` and `pr-pitch` -edits. Touches no image behaviour. - -PR 2, stacked: evidence framework, `jira_add_attachment`, `pr-pitch` evidence -section, `fieldworks-avalonia-ui` capture step. Its publish step probes for -`gh --attach`, falls back to `gh image`, then to an orphan evidence branch, so -it does not depend on the gh release landing. From 0f1f0d85aad9461c9ec5f1e1a05d3ffa5ec7f6f3 Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 21 Aug 2026 10:39:28 -0400 Subject: [PATCH 4/6] Require Affects Version FW 9.3 on every new ticket Every new LT ticket carries Affects Version FW 9.3, with no exception. It is a filing convention that keeps the queue filterable rather than a claim about which build the reporter was running, so the Phase 0b relevance rule does not exempt a tooling, build or documentation ticket from it. A specific point release the reporter names is added alongside FW 9.3, never in place of it. Neither jira_create_issue nor jira_update_issue exposes the versions field, so it goes through custom_fields. The same applies to assignee, which those helpers send as a Cloud-style accountId that SIL's Data Center rejects; both fallbacks are now written down where the publish step needs them. Refs LT-22723 Co-Authored-By: Claude Opus 5 --- .claude/skills/jira-issue/SKILL.md | 30 +++++++++++++++++++ .../skills/jira-issue/references/format.md | 7 +++++ 2 files changed, 37 insertions(+) diff --git a/.claude/skills/jira-issue/SKILL.md b/.claude/skills/jira-issue/SKILL.md index f6b819f258..80d09c3202 100644 --- a/.claude/skills/jira-issue/SKILL.md +++ b/.claude/skills/jira-issue/SKILL.md @@ -60,6 +60,9 @@ no project file, no menu path, and no reproduction inside the application. "N/A" is worse than a short ticket: it costs the reader the same scan and returns nothing. Drop the heading instead. +One exception, and it is not a section: **Affects Version is always set**, even +on a ticket with no FLEx version in its body. See Phase 6. + The same test governs the interview. Asking a developer which FLEx build they were running, for a ticket about a Markdown reference file, wastes one of the six questions and signals that the ticket was generated rather than written. @@ -164,6 +167,32 @@ describe the data instead of attaching it and record that in the ticket. ## Phase 6 -- Publish +### Required on every new issue + +**Affects Version is `FW 9.3` on every new ticket, without exception.** It is +a filing convention that keeps the queue filterable, not a claim about which +build the reporter was running -- so Phase 0b does **not** exempt a tooling, +build or documentation ticket from it. If the reporter names a specific point +release, add that version as well; never in place of `FW 9.3`. + +The field is `versions` (Affects Version), not `fixVersions`. Neither +`jira_create_issue` nor `jira_update_issue` exposes it directly, so it goes +through `custom_fields`: + +```powershell +python -c @' +import sys; sys.path.insert(0, ".claude/skills/atlassian-skills/scripts") +from jira_issues import jira_update_issue +print(jira_update_issue("LT-XXXXX", custom_fields={"versions": [{"name": "FW 9.3"}]})) +'@ +``` + +`assignee` is the same story: `jira_create_issue` sends a Cloud-style +`accountId`, which SIL's Data Center rejects. Use +`custom_fields={"assignee": {"name": ""}}`. + +### Order + Order matters: issue, then comment, then links. Write the description and the comment to files first; never inline multi-line @@ -240,6 +269,7 @@ If Phase 2 found the work already exists, Phase 8 targets that existing key. - [ ] Every unknown is a `*Not known:*` line rather than a guess. - [ ] No section was emitted that does not apply -- no "N/A" environment, no empty repro steps on a ticket with nothing to reproduce. +- [ ] Affects Version is set to `FW 9.3`. Every new ticket, no exceptions. - [ ] The duplicate table was shown and answered. - [ ] Links created for every candidate marked duplicate or related. - [ ] Nothing was attached without the permission question being answered. diff --git a/.claude/skills/jira-issue/references/format.md b/.claude/skills/jira-issue/references/format.md index 0b2caea1a1..94ea1929a2 100644 --- a/.claude/skills/jira-issue/references/format.md +++ b/.claude/skills/jira-issue/references/format.md @@ -9,6 +9,13 @@ | Description | 250 words total, ending in one `*Next:*` line | Nothing can be collapsed | | Analysis comment | As long as the reasoning deserves | Nobody is forced to scroll past it | +Required fields, regardless of subject: + +| Field | Value | +| --- | --- | +| Affects Version (`versions`) | `FW 9.3` on every new ticket, plus any specific point release the reporter names | +| Assignee | Only when someone is taking it now. Data Center wants `{"name": ""}` | + Count words before publishing. Over budget means the overflow was comment material, not that the budget was wrong. From 34b639ed733c06db9ef7e01fa2567a71b2b3eab9 Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 21 Aug 2026 10:57:55 -0400 Subject: [PATCH 5/6] Point the worked example at the tickets it actually produced The example carried placeholder keys for the four symptom tickets that splitting LT-22715 would create. Those tickets now exist, filed on 2026-08-21 as LT-22724 through LT-22727 and linked back with Issue split, so the example names them. A worked example that cites real tickets can be checked by a reader. One that cites LT-AAAAA cannot, and quietly invites the reader to treat the whole example as hypothetical. Refs LT-22715, LT-22723 Co-Authored-By: Claude Opus 5 --- .claude/skills/jira-issue/references/examples.md | 13 +++++++------ 1 file changed, 7 insertions(+), 6 deletions(-) diff --git a/.claude/skills/jira-issue/references/examples.md b/.claude/skills/jira-issue/references/examples.md index 655a7fad7e..863d7cf1b3 100644 --- a/.claude/skills/jira-issue/references/examples.md +++ b/.claude/skills/jira-issue/references/examples.md @@ -55,11 +55,10 @@ ones fabricated silently when features are inserted into a rule. Both are h3. Symptoms, filed separately -# LT-AAAAA -- a shared class is rewritten from inside a rule, with nothing on -screen saying so -# LT-BBBBB -- a named class renders as a decomposed feature list -# LT-CCCCC -- generated classes accumulate in the user's list, never cleaned up -# LT-DDDDD -- no way to promote a feature bundle into a real class +# LT-22724 -- editing a class from one rule changes every rule using it +# LT-22725 -- a named feature class renders as a feature list inside rules +# LT-22726 -- generated classes accumulate and are never cleaned up +# LT-22727 -- no way to promote a generated feature bundle into a real class h3. Ideas to resolve @@ -81,7 +80,9 @@ comment._ *Next:* team decision on the migration question. ``` -228 words. The four symptom tickets are linked, each triageable on its own. +271 words with the Environment line. The four symptom tickets were filed on +2026-08-21 as LT-22724 through LT-22727, linked to this one with "Issue split", +and each is triageable on its own. ## What moved to the comment From 03f686e4021fa7ae3bb8a9b635e1e61cd718dc25 Mon Sep 17 00:00:00 2001 From: John Lambert Date: Fri, 21 Aug 2026 11:12:31 -0400 Subject: [PATCH 6/6] Halve the always-loaded skill and record the traps that bit us SKILL.md goes from 224 lines to 95. It is the only file loaded every time the skill triggers, so every line it holds is context spent whether or not the reader needs it. Mechanics move to references, which load when the phase that needs them is reached. Moved out: the four duplicate-search passes to a new duplicates.md, the publish calls to publish.md, the relevance table to format.md. What stays is the phase table, the two gates, the budgets and the traps. Recorded from the retroactive rewrite of 28 tickets: - Link types must be read, never guessed. There is no Relates in this Jira, and falling back to the first name in the list produced four bogus Cloners links between a cause ticket and its children. - resolution cannot be set by an update, only by a transition. - The read-only skill's jira_workflow.py and jira_projects.py raise NameError on import; use the atlassian-skills copies. - A private Gmail or Drive URL in a description is broken evidence. - Rewriting a ticket posts the original as a comment first. Task-type tickets gain their own lede labels. Two of the rewritten tickets were Tasks and had to improvise them. Refs LT-22723 Co-Authored-By: Claude Opus 5 --- .claude/skills/jira-issue/SKILL.md | 314 ++++-------------- .../jira-issue/references/duplicates.md | 36 ++ .../skills/jira-issue/references/format.md | 22 +- .../skills/jira-issue/references/publish.md | 98 ++++++ 4 files changed, 218 insertions(+), 252 deletions(-) create mode 100644 .claude/skills/jira-issue/references/duplicates.md create mode 100644 .claude/skills/jira-issue/references/publish.md diff --git a/.claude/skills/jira-issue/SKILL.md b/.claude/skills/jira-issue/SKILL.md index 80d09c3202..f06dc240fc 100644 --- a/.claude/skills/jira-issue/SKILL.md +++ b/.claude/skills/jira-issue/SKILL.md @@ -1,277 +1,95 @@ --- name: jira-issue -description: "Write and file a well-shaped LT Jira issue -- bug or feature. Use whenever asked to file, raise, report, or create a Jira issue or LT ticket, to turn a user report or a finding into a ticket, or to restructure a ticket that buries its point. Interviews for who/when/where/how, hunts duplicates before drafting, gets the three-line lede approved, keeps the description short because Jira Data Center cannot collapse anything, and moves the analysis to a comment." +description: "Write and file an LT Jira issue -- bug, feature or task -- that a triager can act on from the first line. Use whenever asked to file, raise, report or create a Jira issue or LT ticket, to turn a user report or a finding into a ticket, or to restructure a ticket that buries its point. Interviews, hunts duplicates before drafting, gets a three-line lede approved, and moves the analysis to a comment." argument-hint: "Optional: the raw report, a finding, or an existing LT-XXXXX to restructure" user-invocable: true --- # Jira Issue -Read `.claude/references/compact-style.md` first. Every rule below assumes it. +Jira Data Center has no `{expand}`. Nothing folds away, so length in the +description is length on the screen for every reader, permanently. Keep the +description short and put the depth in the first comment. -Related skills: `atlassian-readonly-skills` (search, read), `atlassian-skills` -(create, comment, link, transition), `jira-bugfix` (takes over at Phase 8). +Style contract: `.claude/references/compact-style.md`. Read it first. -## The constraint that shapes everything +## Phases -Jira Data Center has no `{expand}` macro. Nothing in a description can be -folded away, so length in the description is length on the screen. The -description stays short; the depth goes in the first comment. - -## Preferences - -Read `.claude/.jira-issue-prefs.json` (gitignored). On first run, ask the two -questions once and write it: - -```json -{ - "jiraUsername": "", - "workspace": "worktree | branch", - "branchStyle": "LT-XXXXX-slug" -} -``` - -Thereafter do not ask. State which preference was used when acting on it, so a -wrong one is visible and correctable. - -## Phase 0 -- Type, and the one-problem test - -Bug or feature. Then count the problems in the raw material. - -**More than one problem means more than one ticket.** LT-22715 announces that -it contains "four separate user-visible problems"; that is four tickets and a -set of links, and splitting is far cheaper now than after triage. Propose the -split, name each ticket in one line, and get agreement before drafting. - -## Phase 0b -- Relevance - -**Ask what kind of thing this ticket is about before asking anything else.** -Not every LT ticket is about FLEx the product. A ticket about developer -tooling, an agent skill, the build, CI, or documentation has no FLEx version, -no project file, no menu path, and no reproduction inside the application. - -| Subject | Environment questions that apply | -| --- | --- | -| FLEx product | Version and build, OS, project, menu path, keyboard/IME | -| Developer tooling, agent skills, docs | Repo, branch, which skill or script. No FLEx version | -| Build, CI, installer | Branch, runner, toolchain, which script. No project | - -**Never emit a section that does not apply.** A template dutifully filled with -"N/A" is worse than a short ticket: it costs the reader the same scan and -returns nothing. Drop the heading instead. - -One exception, and it is not a section: **Affects Version is always set**, even -on a ticket with no FLEx version in its body. See Phase 6. - -The same test governs the interview. Asking a developer which FLEx build they -were running, for a ticket about a Markdown reference file, wastes one of the -six questions and signals that the ticket was generated rather than written. - -## Phase 1 -- Interview - -One question at a time. **Hard cap of six.** Quote the answers; do not -paraphrase them into confidence. Skip any row Phase 0b ruled irrelevant -- -the cap is a budget, and a wasted question is one you do not get back. - -| | Bug | Feature | +| # | Phase | Rule | | --- | --- | --- | -| Who | Which user, what role, how many affected | Who wants it, and what they are actually trying to accomplish | -| When | FLEx version and build, date, first time or recurring, did it work before | How often it comes up | -| Where | Tool, window, menu path, which project | Which area of FLEx | -| How | Exact actions -- typed, pasted, dragged, IME, Send/Receive | What they do today instead | -| How bad | Data loss, workaround, blocking | What "done" looks like | - -Two rules outrank the questions: - -- **"I don't know" is an answer and it goes in the ticket** as a `*Not known:*` - line. A recorded gap tells the next reader where to dig; a guess becomes - folklore. -- **Nothing enters the description that the reporter did not say or that we - did not verify.** Inferred mechanism goes to the analysis comment, labelled - inferred. - -If the developer is relaying a second-hand report, ask which parts they -witnessed. Second-hand detail is recorded as second-hand. - -## Phase 2 -- Duplicates and related issues, before drafting - -Search first. If the ticket exists, the work is a comment on it, and finding -that out after drafting wastes the drafting. - -Four passes, ten results each: - -```powershell -python -c @' -import sys; sys.path.insert(0, ".claude/skills/atlassian-readonly-skills/scripts") -from jira_search import jira_search -jql = "project = LT AND text ~ \"natural class\" AND status != Closed ORDER BY updated DESC" -print(jira_search(jql, fields="key,summary,status,updated", limit=10)) -'@ -``` - -1. **Symptom words** -- the user's vocabulary, not ours. -2. **Area** -- `project = LT AND component = "..." AND status != Closed`. -3. **Link walk** -- for every ticket already cited, read its links and follow - one hop. -4. **Mechanism** -- the type or method name, when the code location is known. - -Present at most five candidates: - -| Key | Summary | Why it might be the same | Verdict | -| --- | --- | --- | --- | +| 0 | Type | Bug, feature or task. **More than one problem means more than one ticket** | +| 0b | Relevance | Not every ticket is about FLEx. Drop sections that do not apply; never fill them with "N/A" | +| 1 | Interview | Who / when / where / how / how bad. One at a time, **max 6**. "I don't know" is recorded, not guessed | +| 2 | Duplicates | Search **before** drafting. Show at most 5 candidates as a table with verdicts | +| 3 | Lede | Three labelled lines, **approved before anything else is written**. Max 3 revisions, then ask which line is wrong | +| 4 | Body | Track file, plus the budgets in `references/format.md` | +| 5 | Permission | "Do you have permission to post this?" Hard stop before anything leaves the machine | +| 6 | Publish | `references/publish.md` | +| 7 | Report | Key, URL, one `Next:` line. Nothing else | +| 8 | Start now? | Assign, transition, comment the branch and worktree, hand to `jira-bugfix` at its Step 3 | -Verdicts are duplicate, related, or unrelated. **Never file without showing -this table and getting a yes.** If a duplicate exists, stop and offer to -comment on it instead; if the developer still wants a new ticket, file it and -link it as a duplicate. +## The two gates -## Phase 3 -- The lede, approved before anything else is written +Nothing is filed until the developer has answered both: -Draft exactly three lines and stop: +1. the duplicate table (Phase 2) +2. the three lede lines (Phase 3) -``` -*What happens:* Deleting a feature-based natural class empties every rule -formula that used it. No warning, no prompt. -*Who hits it:* Anyone whose phonological rules reference Natural Classes. -*How bad:* Silent data loss. Rules must be rebuilt by hand. -``` +If either can be skipped quietly, the skill is decoration. -For a feature track the labels are `*What they want:*`, `*Who wants it:*`, -`*Why it matters:*`. +## Non-negotiable -Show it rendered. The developer approves, edits, or adds detail. Loop at most -three times; if it is still wrong, ask which line is wrong rather than guessing -a fourth. **Nothing else is drafted until these three lines are theirs.** +- **Affects Version `FW 9.3` on every new ticket.** A filing convention, not a + claim about a build, so Phase 0b does not exempt tooling or docs tickets. +- **Nothing above the fold that the reporter did not say or you did not + verify.** Inference goes in the comment, labelled inferred. +- Budgets: summary 80 characters, lede 60 words, description 250 words ending + in one `*Next:*` line. Overflow goes to the comment. -## Phase 4 -- The rest of the ticket +## Lede labels -Follow `references/bug-track.md` or `references/feature-track.md`. Field -budgets and Jira markup are in `references/format.md`. Worked before-and-after -examples are in `references/examples.md`. - -Budgets, since nothing can be folded: summary 80 characters, lede 60 words, -whole description 250 words ending in one `*Next:*` line. Overflow goes to the -analysis comment. - -## Phase 5 -- Permission - -**Hard stop.** Before any attachment, sample project, log, or screenshot of -real data leaves the machine: - -> Do you have permission to post this? - -Language data is frequently unpublished and community-owned -- vernacular -text, speaker names, unreleased lexicons. Never attach a project or capture -the agent found on disk without being told to. If the answer is no or unclear, -describe the data instead of attaching it and record that in the ticket. - -## Phase 6 -- Publish - -### Required on every new issue - -**Affects Version is `FW 9.3` on every new ticket, without exception.** It is -a filing convention that keeps the queue filterable, not a claim about which -build the reporter was running -- so Phase 0b does **not** exempt a tooling, -build or documentation ticket from it. If the reporter names a specific point -release, add that version as well; never in place of `FW 9.3`. - -The field is `versions` (Affects Version), not `fixVersions`. Neither -`jira_create_issue` nor `jira_update_issue` exposes it directly, so it goes -through `custom_fields`: - -```powershell -python -c @' -import sys; sys.path.insert(0, ".claude/skills/atlassian-skills/scripts") -from jira_issues import jira_update_issue -print(jira_update_issue("LT-XXXXX", custom_fields={"versions": [{"name": "FW 9.3"}]})) -'@ -``` - -`assignee` is the same story: `jira_create_issue` sends a Cloud-style -`accountId`, which SIL's Data Center rejects. Use -`custom_fields={"assignee": {"name": ""}}`. - -### Order - -Order matters: issue, then comment, then links. - -Write the description and the comment to files first; never inline multi-line -Jira markup into a command. - -```powershell -python -c @' -import sys; sys.path.insert(0, ".claude/skills/atlassian-skills/scripts") -from jira_issues import jira_create_issue -desc = open("desc.txt", encoding="utf-8").read() -print(jira_create_issue("LT", "", "Bug", description=desc)) -'@ -``` - -Then the analysis comment via `jira_add_comment(issue_key, comment)`, then one -link per related ticket. Read the real link type names rather than guessing: - -```powershell -python -c @' -import sys; sys.path.insert(0, ".claude/skills/atlassian-readonly-skills/scripts") -from jira_links import jira_get_link_types -print(jira_get_link_types()) -'@ -``` - -Then `jira_create_issue_link(link_type, inward_issue_key, outward_issue_key)`. - -SIL Jira is Data Center: `assignee` takes a username, not an account ID, -despite what the docstring says. If assignment fails, fall back to -`custom_fields={"assignee": {"name": ""}}`. - -## Phase 7 -- Report - -Three lines: the key, the URL, and one `Next:` line. Nothing else. - -## Phase 8 -- "Do you want to start this now?" - -Ask. Four exits: - -| Answer | Actions | +| Type | Labels | | --- | --- | -| Start now | Assign, transition to In Progress, comment the branch and worktree, create the workspace, hand to `jira-bugfix` at its Step 3 | -| Mine, later | Assign only. Status untouched, no branch | -| Leave for triage | Nothing. This is the default | -| Someone else's | Assign to the named person, comment, stop | - -On "start now": +| Bug | `*What happens:*` `*Who hits it:*` `*How bad:*` | +| Feature | `*What they want:*` `*Who wants it:*` `*Why it matters:*` | +| Task | `*What this is:*` `*Who it affects:*` `*Why it matters:*` | + +## Traps that have already bitten + +- **Read link types; never guess.** There is no `Relates` here. Falling back to + the first name in the list once produced four bogus `Cloners` links. +- **`resolution` cannot be set by an update** -- only by a transition. +- **The read-only skill's `jira_workflow.py` and `jira_projects.py` raise + `NameError: Optional` on import.** Use the `atlassian-skills` copies. +- **A private URL is broken evidence.** A Gmail or Drive link renders for + nobody. Attach the file. +- **Rewriting a ticket: post the original as a comment first**, then replace + the description. -1. Branch `LT-XXXXX-short-slug` off fresh `origin/main`. -2. Workspace per the saved preference -- worktree or branch in place. Say - which preference was used. -3. Comment on the ticket, naming both: - - > Taken. Working on branch `LT-22715-nc-delete-warning`, worktree - > `.tmp/worktrees/nc-delete-warning`. +## Preferences - This is what makes the ticket the index into a worktree list of sixteen. -4. Hand off to `jira-bugfix` at Step 3. Do not repeat its Steps 0-2; the issue - is already in hand, assigned and in progress. +`.claude/.jira-issue-prefs.json` (gitignored): `jiraUsername`, `workspace` +(`worktree` or `branch`), `branchStyle`. Ask the two questions once on first +run, then act on them silently -- but say which preference was used, so a +wrong one is visible. -Resolve transitions by ID from `jira_get_transitions`, never by guessing a -name. If the transition is refused for permissions, say "assigned, please move -it to In Progress yourself" and continue rather than aborting. **Never -transition to Done or Resolved** -- that follows a merged PR. +## References -If Phase 2 found the work already exists, Phase 8 targets that existing key. +| File | For | +| --- | --- | +| `references/bug-track.md` | Interview, repro rules, sample-project permission | +| `references/duplicates.md` | The four search passes and the candidate table | +| `references/feature-track.md` | User story, workarounds, cost shape | +| `references/format.md` | Budgets, required fields, Jira markup | +| `references/publish.md` | The API calls and their gotchas | +| `references/examples.md` | LT-22715 before and after | ## Before finishing -- [ ] Summary is 80 characters or fewer and names the symptom, not the cause. -- [ ] The three lede lines are the developer's, not the first draft. -- [ ] Description is 250 words or fewer and ends in one `*Next:*` line. -- [ ] Every claim above the fold was said by the reporter or verified by us. +- [ ] Both gates answered by the developer. +- [ ] Affects Version set. +- [ ] Description 250 words or fewer, ending in one `*Next:*` line. - [ ] Every unknown is a `*Not known:*` line rather than a guess. -- [ ] No section was emitted that does not apply -- no "N/A" environment, no - empty repro steps on a ticket with nothing to reproduce. -- [ ] Affects Version is set to `FW 9.3`. Every new ticket, no exceptions. -- [ ] The duplicate table was shown and answered. -- [ ] Links created for every candidate marked duplicate or related. -- [ ] Nothing was attached without the permission question being answered. -- [ ] The reader knows what is wrong and what happens next from the title and - the last line alone. +- [ ] No section emitted that does not apply. +- [ ] The title and the last line alone tell the reader what is wrong and what + happens next. diff --git a/.claude/skills/jira-issue/references/duplicates.md b/.claude/skills/jira-issue/references/duplicates.md new file mode 100644 index 0000000000..73ca30af0d --- /dev/null +++ b/.claude/skills/jira-issue/references/duplicates.md @@ -0,0 +1,36 @@ +# Duplicate and related search (Phase 2) + +**Search before drafting, not before posting.** If the ticket exists, the work +is a comment on it, and finding that out after twenty minutes wastes the +twenty minutes. + +```powershell +python -c @' +import sys; sys.path.insert(0, ".claude/skills/atlassian-skills/scripts") +from jira_search import jira_search +jql = "project = LT AND text ~ \"natural class\" AND status != Closed ORDER BY updated DESC" +print(jira_search(jql, fields="key,summary,status,updated", limit=10)) +'@ +``` + +Four passes, ten results each: + +1. **Symptom words** -- the reporter's vocabulary, not ours. +2. **Area** -- `project = LT AND component = "..." AND status != Closed`. +3. **Link walk** -- for every ticket already cited, read its links, follow one hop. +4. **Mechanism** -- the type or method name, when the code location is known. + +`text ~` searches summary, description, comments and environment, so a common +word like "triage" returns hundreds of irrelevant hits. Say so when reporting a +noisy pass rather than listing its results. + +Present at most five candidates: + +| Key | Summary | Why it might be the same | Verdict | +| --- | --- | --- | --- | + +Verdicts are duplicate, related or unrelated. **Never file without showing this +table and getting a yes.** If a duplicate exists, offer to comment on it +instead; if the developer still wants a new ticket, file it and link it. + +Link types are in `publish.md` -- read them, never guess. diff --git a/.claude/skills/jira-issue/references/format.md b/.claude/skills/jira-issue/references/format.md index 94ea1929a2..0d970bee07 100644 --- a/.claude/skills/jira-issue/references/format.md +++ b/.claude/skills/jira-issue/references/format.md @@ -62,10 +62,24 @@ _Analysis in the first comment._ Feature track replaces Steps/Expected/Actual with the sections in `feature-track.md`. Everything else is identical. -This is a skeleton, not a form. Drop any heading that does not apply to the -subject -- a ticket about a build script has no `h3. Environment` worth -writing, and a ticket about a documentation file has nothing to reproduce. -See Phase 0b in `../SKILL.md`. +This is a skeleton, not a form. Drop any heading that does not apply. + +## Relevance (Phase 0b) + +Not every LT ticket is about FLEx the product. Decide the subject before the +interview, because it decides which questions are worth one of your six. + +| Subject | Environment questions that apply | +| --- | --- | +| FLEx product | Version and build, OS, project, menu path, keyboard/IME | +| Developer tooling, agent skills, docs | Repo, branch, which skill or script. No FLEx version | +| Build, CI, installer | Branch, runner, toolchain, which script. No project | + +A template dutifully filled with "N/A" is worse than a short ticket: same scan, +no information. Drop the heading instead. + +One exception, and it is a field rather than a section: **Affects Version is +always set**, whatever the subject. ## Jira Data Center wiki markup diff --git a/.claude/skills/jira-issue/references/publish.md b/.claude/skills/jira-issue/references/publish.md new file mode 100644 index 0000000000..0200812320 --- /dev/null +++ b/.claude/skills/jira-issue/references/publish.md @@ -0,0 +1,98 @@ +# Publishing: the calls, and what bites + +Order: issue, comment, attachments, links. Write description and comment to +files first; never inline multi-line Jira markup into a command. + +## Create + +```powershell +python -c @' +import sys; sys.path.insert(0, ".claude/skills/atlassian-skills/scripts") +from jira_issues import jira_create_issue +desc = open("desc.txt", encoding="utf-8").read() +print(jira_create_issue("LT", "", "Bug", description=desc, + custom_fields={"versions": [{"name": "FW 9.3"}]})) +'@ +``` + +Then `jira_add_comment(key, comment)`, then `jira_add_attachment(key, paths)` +once Phase 5 is answered, then one link per related ticket. + +## Fields that need the custom_fields back door + +`jira_create_issue` and `jira_update_issue` do not expose these, and their +docstrings describe Jira Cloud rather than SIL's Data Center. + +| Field | Pass | +| --- | --- | +| Affects Version | `custom_fields={"versions": [{"name": "FW 9.3"}]}` | +| Assignee | `custom_fields={"assignee": {"name": "John_Lambert"}}` -- a username, **not** an accountId | +| Resolution | **Not settable by update at all.** It is not on the edit screen; only a transition sets it | + +Usernames are not email addresses. Read the caller's own with +`client.get(client.api_path("myself"))["name"]`. + +## Link types -- read them, never guess + +```powershell +python -c @' +import sys; sys.path.insert(0, ".claude/skills/atlassian-skills/scripts") +from jira_links import jira_get_link_types +print(jira_get_link_types()) +'@ +``` + +**There is no `Relates` in this Jira.** As of 2026-08-21 the types are +`Cloners, Depends on, Duplicate, Issue split, partially implements, Redesign, +Related, Requires, Solution, Story/Task, Test`. + +- Splitting one ticket into several -> **`Issue split`** +- Merely related -> **`Related`** +- Same defect -> **`Duplicate`** + +**Never fall back to the first name in the list.** Doing that once produced +four `Cloners` links between a cause ticket and its children, which reads as a +claim nobody made. If the intended type is absent, stop and ask. + +Then `jira_create_issue_link(link_type, inward_issue_key, outward_issue_key)`. + +## Broken scripts to route around + +`.claude/skills/atlassian-readonly-skills/scripts/jira_workflow.py` and +`jira_projects.py` both raise `NameError: name 'Optional' is not defined` on +import -- a missing typing import. The `atlassian-skills` copies work. Use +those, for reads as well as writes, until the read-only ones are fixed. + +## Rewriting an existing ticket + +**Post the original as a comment before replacing the description.** Someone +has already read that text and may have replied to it; replacing it outright +destroys the record. + +``` +h3. Full detail (original description, preserved ) + +The description above was shortened so a triager can act on the first line. +Nothing was deleted -- the original text follows verbatim. + +---- +``` + +Then update the description. Every edit notifies watchers, so a bulk pass is a +mail burst: do it in one sitting and tell the team it is coming. + +## Phase 8 -- starting work + +1. Branch `LT-XXXXX-short-slug` off fresh `origin/main`. +2. Workspace per the saved preference. Say which one was used. +3. Comment on the ticket naming both, so the ticket indexes the worktree list: + + > Taken. Working on branch `LT-22715-nc-delete-warning`, worktree + > `.tmp/worktrees/nc-delete-warning`. + +4. Hand to `jira-bugfix` at its Step 3. Its Steps 0-2 are already done. + +Resolve transitions by ID from `jira_get_transitions`, never by guessing a +name. A permissions failure degrades to "assigned, please move it to In +Progress yourself" rather than aborting. **Never transition to Done or +Resolved** -- that follows a merged PR.