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..f06dc240fc --- /dev/null +++ b/.claude/skills/jira-issue/SKILL.md @@ -0,0 +1,95 @@ +--- +name: jira-issue +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 + +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. + +Style contract: `.claude/references/compact-style.md`. Read it first. + +## Phases + +| # | Phase | Rule | +| --- | --- | --- | +| 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 | + +## The two gates + +Nothing is filed until the developer has answered both: + +1. the duplicate table (Phase 2) +2. the three lede lines (Phase 3) + +If either can be skipped quietly, the skill is decoration. + +## Non-negotiable + +- **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. + +## Lede labels + +| Type | Labels | +| --- | --- | +| 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. + +## Preferences + +`.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. + +## References + +| 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 + +- [ ] 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 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/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/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/examples.md b/.claude/skills/jira-issue/references/examples.md new file mode 100644 index 0000000000..863d7cf1b3 --- /dev/null +++ b/.claude/skills/jira-issue/references/examples.md @@ -0,0 +1,97 @@ +# 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-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 + +# 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. +``` + +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 + +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..0d970bee07 --- /dev/null +++ b/.claude/skills/jira-issue/references/format.md @@ -0,0 +1,128 @@ +# 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 | + +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. + +## 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. + +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 + +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/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. 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