@kensio/skills 1.13.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +48 -0
- package/bin/kensio-skills.mjs +213 -0
- package/package.json +39 -0
- package/skills/dynamodb-single-table/SKILL.md +329 -0
- package/skills/dynamodb-single-table/references/aws-guidance.md +183 -0
- package/skills/github-issue-drafting/SKILL.md +260 -0
- package/skills/isolated-testing-style/SKILL.md +293 -0
- package/skills/pangram-check/SKILL.md +125 -0
- package/skills/pangram-check/references/configuration.md +120 -0
- package/skills/pangram-check/references/reading-results.md +69 -0
- package/skills/pangram-check/scripts/pangram-check.mjs +807 -0
- package/skills/part-factory-test-data/SKILL.md +238 -0
- package/skills/skill-template/SKILL.md +126 -0
- package/skills/technical-prose-style/SKILL.md +356 -0
- package/skills/technical-prose-style/references/measurements.md +356 -0
- package/skills/technical-prose-style/scripts/prose-check.mjs +381 -0
- package/skills/yulin-aws-simulation/SKILL.md +324 -0
|
@@ -0,0 +1,183 @@
|
|
|
1
|
+
# Mechanics behind the modelling rules
|
|
2
|
+
|
|
3
|
+
The numbers and patterns the [SKILL.md](../SKILL.md) rules rest on, collected from the DynamoDB
|
|
4
|
+
developer guide. Read this when the question turns on cost, throughput or size. Fetch the linked
|
|
5
|
+
pages when a decision rests on an exact figure, because quotas move.
|
|
6
|
+
|
|
7
|
+
## Partitions and throughput
|
|
8
|
+
|
|
9
|
+
A table is stored in partitions, each backed by SSD and replicated across Availability Zones. AWS
|
|
10
|
+
manages them and never exposes them directly. More partitions appear when provisioned throughput
|
|
11
|
+
rises past what the current ones serve, and when an existing partition fills.
|
|
12
|
+
|
|
13
|
+
DynamoDB hashes the partition key to choose the partition. Items sharing a partition key value form
|
|
14
|
+
an **item collection**, held together and sorted by sort key, which is what makes a range query over
|
|
15
|
+
one collection cheap. Where the table carries no local secondary index, DynamoDB splits an item
|
|
16
|
+
collection across as many partitions as it needs, and there is no ceiling on the number of distinct
|
|
17
|
+
sort key values under one partition key.
|
|
18
|
+
|
|
19
|
+
**Every partition serves 3,000 read units and 1,000 write units per second.** One read unit is one
|
|
20
|
+
strongly consistent read of an item up to 4 KB, or two eventually consistent reads. One write unit
|
|
21
|
+
is one write of an item up to 1 KB. Item size multiplies this. A 20 KB item costs 5 read units per
|
|
22
|
+
consistent read, which puts the ceiling at 600 reads per second against that one item.
|
|
23
|
+
|
|
24
|
+
Source
|
|
25
|
+
[Partitions and data distribution](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/HowItWorks.Partitions.html)
|
|
26
|
+
and
|
|
27
|
+
[Best practices for partition keys](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-partition-key-design.html).
|
|
28
|
+
|
|
29
|
+
## Choosing a partition key that spreads
|
|
30
|
+
|
|
31
|
+
Throughput efficiency rises with the ratio of partition key values accessed to partition key values
|
|
32
|
+
that exist. The AWS comparison:
|
|
33
|
+
|
|
34
|
+
| Partition key value | Uniformity |
|
|
35
|
+
| --------------------------------------------------------------- | ---------- |
|
|
36
|
+
| User id, in an application with many users | Good |
|
|
37
|
+
| Status code, where few codes exist | Bad |
|
|
38
|
+
| Creation date rounded to a day, hour or minute | Bad |
|
|
39
|
+
| Device id, where devices are accessed at similar intervals | Good |
|
|
40
|
+
| Device id, where one device is far more popular than the others | Bad |
|
|
41
|
+
|
|
42
|
+
The date case is the one that catches people. Every item created today lands on one partition key
|
|
43
|
+
value and therefore one physical partition.
|
|
44
|
+
|
|
45
|
+
A table small enough to fit in a single partition, allowing for growth, and whose throughput stays
|
|
46
|
+
inside one partition's limits, will not throttle whatever the key looks like.
|
|
47
|
+
|
|
48
|
+
Source
|
|
49
|
+
[Designing partition keys to distribute your workload](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-partition-key-uniform-load.html).
|
|
50
|
+
|
|
51
|
+
## Write sharding
|
|
52
|
+
|
|
53
|
+
Where the natural key concentrates writes, widen the key space by appending a suffix.
|
|
54
|
+
|
|
55
|
+
**Random suffix.** Append a random number in a fixed range, giving `2026-07-09.1` through
|
|
56
|
+
`2026-07-09.200`. Writes spread evenly. Reading one item back becomes impossible without knowing
|
|
57
|
+
which suffix it took, and reading the whole day means one `Query` per suffix followed by a merge.
|
|
58
|
+
|
|
59
|
+
**Calculated suffix.** Derive the suffix from an attribute the reader already holds, such as the sum
|
|
60
|
+
of the UTF-8 code points of an order id modulo 200 plus 1. Writes spread the same way, and a
|
|
61
|
+
`GetItem` for a known order still works because the suffix is recomputable. Reading the whole day
|
|
62
|
+
still costs one `Query` per suffix.
|
|
63
|
+
|
|
64
|
+
A GSI can be sharded the same way to make selective queries parallel.
|
|
65
|
+
|
|
66
|
+
Source
|
|
67
|
+
[Using write sharding](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-partition-key-sharding.html).
|
|
68
|
+
|
|
69
|
+
## Sort key patterns
|
|
70
|
+
|
|
71
|
+
**Hierarchy.** A composite sort key defines one-to-many relationships queryable at any level, using
|
|
72
|
+
`begins_with`, `between`, `>` and `<`. The AWS example is
|
|
73
|
+
`[country]#[region]#[state]#[county]#[city]#[neighborhood]`.
|
|
74
|
+
|
|
75
|
+
**Version history.** Keep two copies of every item. One carries a `v0_` sort key prefix and holds
|
|
76
|
+
the current version, and each revision is written under the next number up (`v1_`, `v2_` and so on)
|
|
77
|
+
with its contents also copied over `v0_`. The current version is then a query on the `v0_` prefix,
|
|
78
|
+
and the history is the rest of the partition.
|
|
79
|
+
|
|
80
|
+
Source
|
|
81
|
+
[Best practices for sort keys](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-sort-keys.html).
|
|
82
|
+
|
|
83
|
+
## Secondary indexes
|
|
84
|
+
|
|
85
|
+
A table gets 20 global secondary indexes (default quota) and 5 local secondary indexes. AWS says
|
|
86
|
+
global indexes are usually the more useful of the two.
|
|
87
|
+
|
|
88
|
+
**Keep the number to a minimum.** An index that is seldom queried adds storage and I/O cost and buys
|
|
89
|
+
no performance.
|
|
90
|
+
|
|
91
|
+
**Choose projections deliberately.** A smaller index costs less and outperforms the base table by
|
|
92
|
+
more. Project the attributes the queries actually return. `ALL` removes every fetch and in most
|
|
93
|
+
cases doubles storage and write cost. Where an index entry is under 1 KB the projection is free up
|
|
94
|
+
to that point, because writes round up.
|
|
95
|
+
|
|
96
|
+
**Avoid fetches on the read path.** Querying an LSI for an attribute it does not project makes
|
|
97
|
+
DynamoDB read the whole item from the table, adding latency and I/O. Attributes queried occasionally
|
|
98
|
+
have a habit of becoming attributes queried always.
|
|
99
|
+
|
|
100
|
+
**Watch LSI item collections.** An item collection covers the table items and every LSI item sharing
|
|
101
|
+
a partition key, and it cannot exceed 10 GB. Writes fail once it does. Pass
|
|
102
|
+
`ReturnItemCollectionMetrics` on writes and alarm before the limit. An LSI cannot be deleted after
|
|
103
|
+
creation, which makes the decision to add one permanent.
|
|
104
|
+
|
|
105
|
+
Source
|
|
106
|
+
[General guidelines for secondary indexes](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-indexes-general.html).
|
|
107
|
+
|
|
108
|
+
## Large items
|
|
109
|
+
|
|
110
|
+
The item size limit is 400 KB, and exceeding it fails the write with a `ValidationException`. Pass
|
|
111
|
+
`ReturnConsumedCapacity` on writes and alarm on items approaching the limit.
|
|
112
|
+
|
|
113
|
+
Three ways out, in the order worth trying:
|
|
114
|
+
|
|
115
|
+
- **Vertical partitioning.** Break the item into several items under one partition key, ordered by
|
|
116
|
+
sort key. This is the single-table answer and keeps everything queryable.
|
|
117
|
+
- **Compression.** GZIP or LZO into a `Binary` attribute. A compressed attribute cannot be filtered
|
|
118
|
+
or queried on.
|
|
119
|
+
- **S3.** Store the payload as an object and the object key in the item, with the item's primary key
|
|
120
|
+
in the S3 object metadata pointing back. No transaction spans the two, so the application owns the
|
|
121
|
+
cleanup of orphaned objects.
|
|
122
|
+
|
|
123
|
+
Source
|
|
124
|
+
[Best practices for storing large items](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-use-s3-too.html).
|
|
125
|
+
|
|
126
|
+
## What a Scan actually costs
|
|
127
|
+
|
|
128
|
+
A `Scan` reads the whole table or index and then discards what the filter rejects. It slows as the
|
|
129
|
+
table grows.
|
|
130
|
+
|
|
131
|
+
A single 1 MB page of 4 KB items costs 128 eventually consistent read units, or 256 strongly
|
|
132
|
+
consistent. That arrives as one spike, and it lands on one partition, because the items a scan reads
|
|
133
|
+
sit next to each other. Requests sharing that partition throttle.
|
|
134
|
+
|
|
135
|
+
Where a scan is needed:
|
|
136
|
+
|
|
137
|
+
- Set `Limit` to shrink the page, which spreads the cost and leaves gaps for other traffic.
|
|
138
|
+
- Use parallel segments once the table passes about 20 GB, starting at roughly one segment per 2 GB,
|
|
139
|
+
and only where the provisioned read capacity is not already busy.
|
|
140
|
+
- Retry throttled requests with exponential backoff.
|
|
141
|
+
|
|
142
|
+
Source
|
|
143
|
+
[Best practices for querying and scanning](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-query-scan.html).
|
|
144
|
+
|
|
145
|
+
## Adjacency lists and the materialized graph
|
|
146
|
+
|
|
147
|
+
Top-level entities become partition keys, and each relationship becomes an item in the partition
|
|
148
|
+
whose sort key is the id at the other end. Data duplication stays minimal and the forward query is a
|
|
149
|
+
plain `Query`.
|
|
150
|
+
|
|
151
|
+
The reverse direction comes from an **inverted index**, a global secondary index whose partition key
|
|
152
|
+
is the table's sort key.
|
|
153
|
+
|
|
154
|
+
The materialized graph pattern extends this. Edge items carry `Type` and `Target` attributes
|
|
155
|
+
composed into a `TypeTarget` key, one overloaded GSI indexes a `Data` attribute holding dates,
|
|
156
|
+
names, places and skills, and a second GSI on `TypeTarget` answers reverse lookups. Aggregations
|
|
157
|
+
large enough to run hot (everyone born on one date, everyone with one skill) want sharding across
|
|
158
|
+
logical partitions.
|
|
159
|
+
|
|
160
|
+
Multi-hop traversal at millisecond latency is Amazon Neptune's job. AWS says so on the many-to-many
|
|
161
|
+
page itself.
|
|
162
|
+
|
|
163
|
+
Source
|
|
164
|
+
[Best practices for many-to-many relationships](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/bp-adjacency-graphs.html).
|
|
165
|
+
|
|
166
|
+
## AWS's own trade-off lists
|
|
167
|
+
|
|
168
|
+
Worth reading in full at
|
|
169
|
+
[Data modeling foundations](https://docs.aws.amazon.com/amazondynamodb/latest/developerguide/data-modeling-foundations.html),
|
|
170
|
+
because they are the most honest account of the cost of single-table design.
|
|
171
|
+
|
|
172
|
+
What AWS counts against single-table design. The learning curve is steep because the design runs
|
|
173
|
+
opposite to relational instinct, whole-table settings (backup, encryption, table class) apply to
|
|
174
|
+
every entity at once, streams carry every change whether or not a consumer wants it, GraphQL is
|
|
175
|
+
harder to implement, and higher-level SDK mappers struggle with one response holding several
|
|
176
|
+
classes.
|
|
177
|
+
|
|
178
|
+
What AWS counts for it. Data locality, fewer read units and fewer round trips, one set of
|
|
179
|
+
permissions and alarms, one key to rotate, capacity averaged across entities, and traffic that
|
|
180
|
+
smooths as patterns aggregate.
|
|
181
|
+
|
|
182
|
+
AWS's summary of when multiple tables are the right answer is short. Where the access patterns never
|
|
183
|
+
query several entities together, multiple tables are good and sufficient.
|
|
@@ -0,0 +1,260 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: github-issue-drafting
|
|
3
|
+
description: Draft a GitHub issue from a short note or a rough idea, grounding every claim about the code in the repository the issue will be filed against, splitting work that is really two issues, and filing it with `gh` once the user has approved the draft. Use when turning a to-do item, a Slack message, a code TODO, a failing test or a bug report into an issue, when asked to "write up an issue for" something, to "raise", "file" or "open an issue", when asked whether something should be one issue or several, and when tidying the titles, types or labels of issues that already exist.
|
|
4
|
+
license: Apache-2.0
|
|
5
|
+
metadata:
|
|
6
|
+
version: "1.13.1"
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# GitHub issue drafting
|
|
10
|
+
|
|
11
|
+
A short note ("fix the retry backoff", "SSM params", "the CLI hangs on empty input") carries enough
|
|
12
|
+
for whoever wrote it and too little for anyone else, including the same person in six months. The
|
|
13
|
+
job is an issue a reader can act on without asking what was meant, with every claim about the code
|
|
14
|
+
checked against the code.
|
|
15
|
+
|
|
16
|
+
## Process
|
|
17
|
+
|
|
18
|
+
1. **Get the note.** Use the text passed in as the skill argument. Ask the user for it if none
|
|
19
|
+
arrived. A note can come from anywhere (a to-do app, a chat message, a `TODO` comment, a stack
|
|
20
|
+
trace, a support thread) and the source changes nothing about the drafting.
|
|
21
|
+
|
|
22
|
+
2. **Work out which repository this belongs to, and how it files issues.** `gh repo view` names the
|
|
23
|
+
repository behind the working directory. Confirm with the user where the working directory is
|
|
24
|
+
ambiguous or where the issue belongs somewhere else. Then read how this project already works:
|
|
25
|
+
|
|
26
|
+
- `.github/ISSUE_TEMPLATE/` and `CONTRIBUTING.md`, if present. **A repository's own template wins
|
|
27
|
+
over the structure in this skill.** Fill in that template and follow its wording.
|
|
28
|
+
- `gh issue list --limit 10` and one or two full issues (`gh issue view <n>`) for the house
|
|
29
|
+
register, the section headings in use, and how long a typical issue runs.
|
|
30
|
+
|
|
31
|
+
3. **Investigate before drafting.** See
|
|
32
|
+
[Ground the draft in the repository](#ground-the-draft-in-the-repository).
|
|
33
|
+
|
|
34
|
+
4. **Decide whether it is one issue or several.** See
|
|
35
|
+
[One note is often more than one issue](#one-note-is-often-more-than-one-issue).
|
|
36
|
+
|
|
37
|
+
5. **Draft it**, separating what step 3 confirmed from what remains an assumption.
|
|
38
|
+
|
|
39
|
+
6. **Present the draft in chat as markdown, and stop.** Filing is the user's call. List any open
|
|
40
|
+
questions under the draft so they can be answered before anything is posted.
|
|
41
|
+
|
|
42
|
+
7. **File it with `gh` only after the user has explicitly asked.** See
|
|
43
|
+
[Filing the issue](#filing-the-issue).
|
|
44
|
+
|
|
45
|
+
## Ground the draft in the repository
|
|
46
|
+
|
|
47
|
+
The failure mode that matters is invention. A feature called missing when it half exists,
|
|
48
|
+
architecture nobody built, acceptance criteria assuming decisions nobody has made. A note is too
|
|
49
|
+
short to carry that context. The repository has to supply it.
|
|
50
|
+
|
|
51
|
+
Budget a handful of tool calls for this. A full audit is more than the draft needs.
|
|
52
|
+
|
|
53
|
+
- **Locate the area.** Grep for the nouns and identifiers in the note. A `README`, an architecture
|
|
54
|
+
doc or the directory layout usually points at the right subtree in one step.
|
|
55
|
+
- **Read the code that would change, and its tests.** Half-built is the common case, and it is the
|
|
56
|
+
case that embarrasses the issue.
|
|
57
|
+
- **Read what the docs already promise.** A behaviour documented as supported and a behaviour
|
|
58
|
+
actually supported are different facts, and the gap between them is sometimes the issue.
|
|
59
|
+
- **Check the history.** `git log --oneline -20` for work in flight, and
|
|
60
|
+
`git log --oneline --all --grep="<keyword>"` for work already done under another name.
|
|
61
|
+
- **Search the tracker, including closed issues.** `gh issue list --state all --search "<keyword>"`.
|
|
62
|
+
A near duplicate is usually worth a comment on the existing issue. Say so and let the user pick.
|
|
63
|
+
Where `gh` is missing or unauthenticated, skip this quietly and never claim to have checked.
|
|
64
|
+
|
|
65
|
+
Carry the unresolved parts forward. Anything step 3 failed to settle belongs in the draft as an
|
|
66
|
+
explicit question or a stated assumption, and in chat as something for the user to answer.
|
|
67
|
+
|
|
68
|
+
## One note is often more than one issue
|
|
69
|
+
|
|
70
|
+
A note is written in one breath. The work it names frequently spans several pull requests, and filed
|
|
71
|
+
whole it becomes one enormous branch that is hard to review and hard to stop halfway.
|
|
72
|
+
|
|
73
|
+
So before drafting, look for a seam that yields **independently shippable** issues. Seams that
|
|
74
|
+
usually work, roughly in build order:
|
|
75
|
+
|
|
76
|
+
- **A usable surface first, whatever sits on top of it second.** The library function is useful on
|
|
77
|
+
its own. The CLI flag, the config key or the framework integration exposing it reads better as a
|
|
78
|
+
follow-up that links back.
|
|
79
|
+
- **A piece blocked on something unbuilt.** Work waiting on another feature is its own issue, with
|
|
80
|
+
the dependency named, and not a caveat buried in this one.
|
|
81
|
+
- **A distinct usage mode.** The same capability reached at runtime and at build time is two
|
|
82
|
+
features with two sets of tests.
|
|
83
|
+
- **A bug fix and the hardening around it.** Ship the fix. File the class of problem separately.
|
|
84
|
+
|
|
85
|
+
Three seams produce issues nobody can ship alone. One issue per function or endpoint, implementation
|
|
86
|
+
split from its tests, and docs as their own issue. Docs belong with the behaviour they describe.
|
|
87
|
+
|
|
88
|
+
Two or three issues is the usual answer where a split is warranted. Five is over-slicing, and a
|
|
89
|
+
small self-contained note stays one issue. Where a split happens, say so in chat and present the set
|
|
90
|
+
together, each one naming its dependency and using **Out of scope** to hand work to the others.
|
|
91
|
+
|
|
92
|
+
## Drafting rules
|
|
93
|
+
|
|
94
|
+
- **Keep it short, and shorter than feels right.** Roughly 200 to 350 words of prose plus at most
|
|
95
|
+
one example. Three or four sections at most, and no more than six acceptance criteria. Cut any
|
|
96
|
+
section that fails to help a reader understand, implement, test or evaluate the change.
|
|
97
|
+
- **The design discussion in chat is not the issue.** Working a note through produces rejected
|
|
98
|
+
alternatives, trade-offs and cost estimates, and almost none of it belongs in the body. Record the
|
|
99
|
+
decision and one sentence of reason. Where the discussion settled something genuinely surprising,
|
|
100
|
+
one short paragraph earns its place.
|
|
101
|
+
- **Never invent behaviour, architecture, supported APIs or acceptance criteria.** Where step 3 left
|
|
102
|
+
something unconfirmed, write it as a question or a stated assumption.
|
|
103
|
+
- **Scrub anything private before it goes anywhere public.** Notes and stack traces carry customer
|
|
104
|
+
names, internal hostnames, internal ticket ids, paths with a username in them, tokens and API
|
|
105
|
+
keys. A public issue is publication. Redact by default and ask about anything borderline.
|
|
106
|
+
- **Write to the problem, and to the observable behaviour that would fix it.** An issue is not a
|
|
107
|
+
pull request description, and a detailed implementation plan belongs in it only where step 3
|
|
108
|
+
turned up a constraint the implementer would otherwise miss.
|
|
109
|
+
- **Prefer one concrete example** (a command, a config snippet, a failing assertion, the exact error
|
|
110
|
+
text) over a paragraph of description.
|
|
111
|
+
- **A bug needs the version, the environment, the steps, the expected result and the actual
|
|
112
|
+
result.** Anything absent is a question for the user, and a bug report missing them wastes the
|
|
113
|
+
first reply.
|
|
114
|
+
- **Leave the process fields alone.** No assignees, milestones, estimates, or wording implying that
|
|
115
|
+
the issue is approved or scheduled. Type and labels are set at filing time.
|
|
116
|
+
- **When in doubt, cut.** Erring long is the more common failure. Somebody re-reading this in six
|
|
117
|
+
months needs the problem, the intended behaviour, and enough grounding to trust both.
|
|
118
|
+
|
|
119
|
+
### Prose
|
|
120
|
+
|
|
121
|
+
Load the `technical-prose-style` and `avoid-ai-writing` skills before drafting where they are
|
|
122
|
+
installed, and run whatever check they ship over the body. Where neither is available, aim for one
|
|
123
|
+
claim per sentence, present tense, no em dashes, no marketing adjectives, and one name kept for one
|
|
124
|
+
thing.
|
|
125
|
+
|
|
126
|
+
## Structure
|
|
127
|
+
|
|
128
|
+
Use the repository's own issue template where it has one. Otherwise draw from the sections below,
|
|
129
|
+
taking only those this particular note needs. Most issues use three or four. **Problem** and
|
|
130
|
+
**Desired behaviour** are the two that nearly always earn their place. Reach for **Current
|
|
131
|
+
behaviour** where what exists today would surprise a reader, and for **Implementation notes** only
|
|
132
|
+
where step 3 turned up a real constraint.
|
|
133
|
+
|
|
134
|
+
```markdown
|
|
135
|
+
# Title
|
|
136
|
+
|
|
137
|
+
## Problem
|
|
138
|
+
|
|
139
|
+
The concrete limitation, missing capability or user need.
|
|
140
|
+
|
|
141
|
+
## Current behaviour
|
|
142
|
+
|
|
143
|
+
What happens today, grounded in what the repository actually shows. Omit where unknown.
|
|
144
|
+
|
|
145
|
+
## Desired behaviour
|
|
146
|
+
|
|
147
|
+
The observable behaviour that should exist once this is implemented.
|
|
148
|
+
|
|
149
|
+
## Example
|
|
150
|
+
|
|
151
|
+
A command, config, request or expected result. Include it only where it clarifies something.
|
|
152
|
+
|
|
153
|
+
## Acceptance criteria
|
|
154
|
+
|
|
155
|
+
Testable checklist items.
|
|
156
|
+
|
|
157
|
+
## Out of scope
|
|
158
|
+
|
|
159
|
+
Related work that should not be assumed to be included.
|
|
160
|
+
|
|
161
|
+
## Implementation notes
|
|
162
|
+
|
|
163
|
+
Grounded constraints from the investigation. Omit the section entirely where there are none.
|
|
164
|
+
```
|
|
165
|
+
|
|
166
|
+
For a bug, replace the middle three with **Steps to reproduce**, **Expected result**, **Actual
|
|
167
|
+
result** and **Environment** (version, runtime, operating system, anything version-pinned that
|
|
168
|
+
matters).
|
|
169
|
+
|
|
170
|
+
## Titles people can find
|
|
171
|
+
|
|
172
|
+
Issues get indexed, by GitHub's search and by search engines, and they surface far more readily than
|
|
173
|
+
pull requests do. For a public repository the title and the opening paragraph are the whole search
|
|
174
|
+
snippet, and they do nearly all the work of getting the issue in front of the person who has the
|
|
175
|
+
problem.
|
|
176
|
+
|
|
177
|
+
- **Write the words a user would type.** Internal shorthand and internal abbreviations describe the
|
|
178
|
+
problem to people who already know it. Spell them out.
|
|
179
|
+
- **Keep the term that makes the project distinctive**, even while cutting shorthand around it. The
|
|
180
|
+
two look like the same edit and are opposites. An abbreviation nobody searches for should go. The
|
|
181
|
+
one accurate word separating this project from every other page about the same topic should stay,
|
|
182
|
+
because a title without it competes with the upstream documentation and loses.
|
|
183
|
+
- **Front-load.** Search snippets truncate around 60 characters, so anything load-bearing goes
|
|
184
|
+
early.
|
|
185
|
+
- **Generic verbs are weak alone.** "add", "support", "fix" are fine where the sentence wants them
|
|
186
|
+
anyway. They differentiate nothing on their own, and forcing one in is where a title starts
|
|
187
|
+
sounding written for a crawler.
|
|
188
|
+
- **Keep it subtle.** An issue that reads as search filler makes a project look automated, and costs
|
|
189
|
+
more credibility than the traffic is worth. Aim for a title a developer would have written anyway,
|
|
190
|
+
which happens to use the words someone with this problem would search for.
|
|
191
|
+
- **Be honest about the size of the prize.** On a small repository these titles win long-tail
|
|
192
|
+
queries. That makes the work worth doing and never worth distorting a title for.
|
|
193
|
+
|
|
194
|
+
`SSR hydration bug in the DS button` becomes
|
|
195
|
+
`Design system button loses its click handler after server-side rendering`. The second expands the
|
|
196
|
+
shorthand nobody searches for, keeps the words that place the problem, and adds nothing that is
|
|
197
|
+
untrue of the bug.
|
|
198
|
+
|
|
199
|
+
On a private repository the audience is the team, and all of this reduces to one rule. Say what the
|
|
200
|
+
problem is in plain words.
|
|
201
|
+
|
|
202
|
+
## Filing the issue
|
|
203
|
+
|
|
204
|
+
Only once the user has explicitly asked.
|
|
205
|
+
|
|
206
|
+
Write the body to a file and pass it with `--body-file`, so quoting and backticks survive intact:
|
|
207
|
+
|
|
208
|
+
```bash
|
|
209
|
+
gh issue create --repo <owner>/<repo> --title "<title>" --body-file <path> --label <label>
|
|
210
|
+
```
|
|
211
|
+
|
|
212
|
+
**Do not hard-wrap the body.** This is the one formatting trap. GitHub renders issue and comment
|
|
213
|
+
Markdown with the GFM hard-line-break extension, so every newline inside a paragraph becomes a
|
|
214
|
+
`<br>`. A repository whose prose style wraps at 80 or 100 columns will produce visibly ragged output
|
|
215
|
+
when that habit reaches an issue body. Write each paragraph and each list item as one long line.
|
|
216
|
+
Blank lines between blocks still separate paragraphs, and code fences and list structure are
|
|
217
|
+
unaffected.
|
|
218
|
+
|
|
219
|
+
**Labels.** Read `gh label list` and pick from what exists. A label passed to `gh` that the
|
|
220
|
+
repository lacks fails the whole command.
|
|
221
|
+
|
|
222
|
+
**Type,** where the organisation has issue types configured. `--type` takes one of them (`Bug`,
|
|
223
|
+
`Feature` and `Task` are the GitHub defaults). Its value is in-repo filtering and a readable issue
|
|
224
|
+
list. Where the flag or the type is rejected, drop it and carry on.
|
|
225
|
+
|
|
226
|
+
**Think before applying `good first issue` or `help wanted`.** GitHub surfaces both in its
|
|
227
|
+
contributor-discovery UI, and third-party sites scrape them to list approachable open-source work.
|
|
228
|
+
For a project actively recruiting contributors that is the point. For a solo maintainer it invites
|
|
229
|
+
drive-by pull requests that cost more to review than they return. `CONTRIBUTING.md` and the existing
|
|
230
|
+
issues usually say which kind of project this is. Where it stays unclear, leave both off and mention
|
|
231
|
+
it.
|
|
232
|
+
|
|
233
|
+
**Skip Projects and date fields** unless the user asks for them. They are planning tools with
|
|
234
|
+
recurring upkeep and no search benefit. Milestones are the lighter option for grouping.
|
|
235
|
+
|
|
236
|
+
Where several issues came out of one note, file them in dependency order and put each preceding URL
|
|
237
|
+
into the issue that depends on it.
|
|
238
|
+
|
|
239
|
+
After filing, report the URL and state plainly which type and labels were applied.
|
|
240
|
+
|
|
241
|
+
## Revisiting existing issues
|
|
242
|
+
|
|
243
|
+
The same thinking applies to issues that already exist, whether retitling or backfilling a missing
|
|
244
|
+
type or label. Closed issues are worth including. They stay indexed, and a closed issue describing a
|
|
245
|
+
capability that now exists is often exactly what a searcher wants.
|
|
246
|
+
|
|
247
|
+
Retitling is cheap and low-risk (the URL survives, GitHub keeps the edit history, nobody gets
|
|
248
|
+
notified) and it is still a public edit. Propose the full set in chat and get an explicit go-ahead
|
|
249
|
+
before running any `gh issue edit`. A table of current against proposed makes the set easy to scan
|
|
250
|
+
and easy to reject one row at a time.
|
|
251
|
+
|
|
252
|
+
Two things to watch in bulk:
|
|
253
|
+
|
|
254
|
+
- **A run of near-identical titles is a real cost.** One formula applied across five issues makes
|
|
255
|
+
the list scannable and reads as a deliberate series. A shared long prefix is also the first thing
|
|
256
|
+
a sceptical reader notices. Accept it where the issues genuinely are one series, and vary the
|
|
257
|
+
phrasing on a couple where the run gets long.
|
|
258
|
+
- **The bodies are usually the bigger win.** The opening paragraph becomes the search snippet, so a
|
|
259
|
+
retitled issue still opening with internal shorthand has had half the job done. Rewriting the
|
|
260
|
+
first sentence of **Problem** often beats the title edit.
|